2011-01-31 57 views
0

我目前正在编写一个小图形应用程序,需要为它的污迹和模糊工具访问像素数据,并在Firefox中使用HTML5 Canvas API遇到了一个令人讨厌的问题。显然它并没有按照规范中的定义实现getImageData。 spec specifically says“...画布外的像素必须以透明黑色的形式返回......”。如何在Firefox中处理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); 
      }, 

这不是很酷,因为我向调用方返回一堆空像素。返回结果就像在规范中那样更好。

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

这是XXX的麻烦部分。我只需要一些方法来返回符合规格的ImageData。任何想法如何做到这一点都是值得欢迎的。 :)

回答

1

也许你可以通过d创建一个尺寸为d的画布,并将原始画布的相应部分绘制到它上面?可悲的是,你不能直接绘制原始画布,因为你遇到了相同的边界检查代码,所以你必须找出重叠。

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

顺便提一下,这是Mozilla bug 392751

+0

是的。我想没有其他办法了......这是一个遗憾,他们还没有修复它(错误报告是从2007年起!)。我将继续讨论这个问题一段时间以获得一些其他想法。如果什么都没有出现,我会接受你的答案。 :)关于嗅探的好处! – 2011-02-01 05:53:19

0

我最终使用下面的代码片段来解决这个问题。希望有人认为它有用...

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); 
}