如何在 Firefox 中处理边界处的 getImageData?

发布于 2024-10-15 02:06:27 字数 1945 浏览 5 评论 0原文

我目前正在编写一个小型绘图应用程序,需要为其涂抹和模糊工具访问像素数据,并且在 Firefox 中遇到了 HTML5 Canvas API 的严重问题。显然它没有按照规范中的定义完全实现 getImageData 。 规范明确指出“...画布外的像素必须以透明黑色返回......”。

这在 FF 中不会发生(在 FF 3.6 和 4 beta 9 中测试)。相反,它会给出如下错误:指定了无效或非法字符串“代码:”12

请注意,这在 Chrome 中似乎工作得很好。

我想这意味着我将不得不实现一些额外的代码来解决这个限制。我设法使用以下代码绕过了这个问题:

            getImageDataAround: function(p, r) {
                p = this._toAbsolute(p);
                r = this._toAbsolute(r);

                p = p.sub(r);

                var d = r * 2;
                var width = d;
                var height = d;

                // XXX: FF hack
                if(navigator.userAgent.indexOf('Firefox') != -1) {
                    if(p.x < 0) {
                        width += p.x;
                        p.x = 0;
                    }

                    if(p.y < 0) {
                        height += p.y;
                        p.y = 0;
                    }

                    var x2 = p.x + width;
                    if(x2 >= this.width) {
                        width = d - (x2 - this.width);
                    }

                    var y2 = p.y + height;
                    if(y2 >= this.height) {
                        height = d - (y2 - this.height);
                    }

                    if((width != d) || (height != d)) {
                        // XXX: not ideal but at least this won't give any
                        // errors
                        return this.ctx.createImageData(d, d);
                    }
                }

                return this.ctx.getImageData(p.x, p.y, width, height);
            },

这并不酷,因为我向调用者返回了一堆空像素。如果像规范中那样返回结果会更好。

只是为了澄清一下,代码是 Context API 的一部分,它包装了真实的上下文并提供了一些额外的功能(相对坐标等)。这可能解释了诸如 this.width 等内容的来源。

麻烦的是XXX部分。我只需要某种方法来返回符合规范的 ImageData 。欢迎任何关于如何做到这一点的想法。 :)

I'm currently writing a little drawing application that needs to access pixel data for its smudge and blur tools and bumped into a nasty issue with HTML5 Canvas API in Firefox. Apparently it does not implement getImageData quite as defined in the spec. The spec specifically says "... Pixels outside the canvas must be returned as transparent black. ...".

This doesn't happen in FF (tested in FF 3.6 and 4 beta 9). Instead it will give an error such as this: An invalid or illegal string was specified" code: "12

Note that this appears to work in Chrome just fine.

I guess this means I will have to implement some extra code to work around this limitation. I managed to bypass the issue using the following code:

            getImageDataAround: function(p, r) {
                p = this._toAbsolute(p);
                r = this._toAbsolute(r);

                p = p.sub(r);

                var d = r * 2;
                var width = d;
                var height = d;

                // XXX: FF hack
                if(navigator.userAgent.indexOf('Firefox') != -1) {
                    if(p.x < 0) {
                        width += p.x;
                        p.x = 0;
                    }

                    if(p.y < 0) {
                        height += p.y;
                        p.y = 0;
                    }

                    var x2 = p.x + width;
                    if(x2 >= this.width) {
                        width = d - (x2 - this.width);
                    }

                    var y2 = p.y + height;
                    if(y2 >= this.height) {
                        height = d - (y2 - this.height);
                    }

                    if((width != d) || (height != d)) {
                        // XXX: not ideal but at least this won't give any
                        // errors
                        return this.ctx.createImageData(d, d);
                    }
                }

                return this.ctx.getImageData(p.x, p.y, width, height);
            },

This isn't cool since I return bunch of empty pixels to the caller. It would be way nicer to return results just like in the spec.

Just to clarify the code is a part of a Context API that wraps real context and provides some extra functionality (relative coords etc.). That probably explains where things like this.width etc. come from.

It's the XXX part that's troublesome. I simply need some way to return ImageData that's up to spec. Any ideas on how to do this are welcome. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甜宝宝 2024-10-22 02:06:27

也许您可以创建一个大小为 d × d 的画布,并在其上绘制原始画布的适当部分?遗憾的是,您无法直接绘制原始画布,因为您遇到了相同类型的边界检查代码,因此您必须找出重叠部分。

您应该考虑嗅探 Gecko 而不是 Firefox。

顺便说一句,这是 Mozilla bug 392751

Perhaps you could create a canvas of size d by d and draw the appropriate portion of the original canvas on to it? Sadly you can't draw the original canvas directly because you run into the same sort of bounds-checking code, so you have to figure out the overlap.

You should consider sniffing for Gecko rather than Firefox.

By the way, this is Mozilla bug 392751.

枕梦 2024-10-22 02:06:27

我最终使用以下代码片段来解决该问题。希望有人觉得它有用......

var getImageDataAround = function(ctx, p, r) {
    // ctx: HTML5 Canvas 2D context
    // p: {x: 23, y: 37}
    // r: radius in px

    // FF fails with fractional values
    p.x = Math.round(p.x);
    p.y = Math.round(p.y);
    r = parseInt(r);

    p.x -= r;
    p.y -= r;

    var d = r * 2;
    var width = d;
    var height = d;

    // FF fails at bounds
    if(navigator.userAgent.indexOf('Gecko') != -1) {
        var xOffset = 0;
        var yOffset = 0;

        if(p.x < 0) {
            xOffset = -p.x;
            width += p.x;
            p.x = 0;
        }

        if(p.y < 0) {
            yOffset = -p.y;
            height += p.y;
            p.y = 0;
        }

        var x2 = p.x + width;
        if(x2 >= ctx.canvas.width) {
            width = d - (x2 - ctx.canvas.width);
        }

        var y2 = p.y + height;
        if(y2 >= ctx.canvas.height) {
            height = d - (y2 - ctx.canvas.height);
        }

        if((width != d) || (height != d)) {
            var data = ctx.createImageData(d, d);

            if(xOffset >= d || yOffset >= d ||
                    width < 1 || height < 1) {
                // totally outside of bounds
                return data;
            }

            var originalData = ctx.getImageData(p.x, p.y,
                width, height);
            var pos = 4 * (xOffset + d * yOffset);
            var dataLen = 4 * d * (yOffset + height);

            for(var originalPos = 0, x = xOffset;
                    pos < dataLen;
                    pos += 4, originalPos += 4, x++) {
                if(x == d) {
                    x = xOffset;
                    pos += xOffset * 4;
                }

                if(xOffset <= x && x < width + xOffset) {
                    data.data[pos] = originalData.data[originalPos];
                    data.data[pos + 1] = originalData.data[originalPos + 1];
                    data.data[pos + 2] = originalData.data[originalPos + 2];
                    data.data[pos + 3] = originalData.data[originalPos + 3];
                }
                else {
                    originalPos -= 4;
                }
            }

            return data;
        }
    }

    return ctx.getImageData(p.x, p.y, width, height);
}

I ended up using following snippet to work around the issue. Hopefully someone finds it useful...

var getImageDataAround = function(ctx, p, r) {
    // ctx: HTML5 Canvas 2D context
    // p: {x: 23, y: 37}
    // r: radius in px

    // FF fails with fractional values
    p.x = Math.round(p.x);
    p.y = Math.round(p.y);
    r = parseInt(r);

    p.x -= r;
    p.y -= r;

    var d = r * 2;
    var width = d;
    var height = d;

    // FF fails at bounds
    if(navigator.userAgent.indexOf('Gecko') != -1) {
        var xOffset = 0;
        var yOffset = 0;

        if(p.x < 0) {
            xOffset = -p.x;
            width += p.x;
            p.x = 0;
        }

        if(p.y < 0) {
            yOffset = -p.y;
            height += p.y;
            p.y = 0;
        }

        var x2 = p.x + width;
        if(x2 >= ctx.canvas.width) {
            width = d - (x2 - ctx.canvas.width);
        }

        var y2 = p.y + height;
        if(y2 >= ctx.canvas.height) {
            height = d - (y2 - ctx.canvas.height);
        }

        if((width != d) || (height != d)) {
            var data = ctx.createImageData(d, d);

            if(xOffset >= d || yOffset >= d ||
                    width < 1 || height < 1) {
                // totally outside of bounds
                return data;
            }

            var originalData = ctx.getImageData(p.x, p.y,
                width, height);
            var pos = 4 * (xOffset + d * yOffset);
            var dataLen = 4 * d * (yOffset + height);

            for(var originalPos = 0, x = xOffset;
                    pos < dataLen;
                    pos += 4, originalPos += 4, x++) {
                if(x == d) {
                    x = xOffset;
                    pos += xOffset * 4;
                }

                if(xOffset <= x && x < width + xOffset) {
                    data.data[pos] = originalData.data[originalPos];
                    data.data[pos + 1] = originalData.data[originalPos + 1];
                    data.data[pos + 2] = originalData.data[originalPos + 2];
                    data.data[pos + 3] = originalData.data[originalPos + 3];
                }
                else {
                    originalPos -= 4;
                }
            }

            return data;
        }
    }

    return ctx.getImageData(p.x, p.y, width, height);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文