2011-10-10 96 views
1

在创建我的html5游戏引擎时,我已经能够做一些很好的事情并获得一些很酷的功能。在制作游戏的合同中,我被问到是否可以从精灵图像中移除背景颜色。而且我看到了这些优点,因为我们可以使用jpg代替png,并减小图像的大小。使用javascript编辑图像

有没有什么办法可以用纯JavaScript来做到这一点?我希望能够在不使用画布元素的情况下做到这一点,所以它可以更快,但如果我必须这样做。

如果我必须这样做,我有另一个问题,我不希望画布对象显示我使用,我可以使用document.createElement画布对象,而不应用于文档?这将是很好的,因为它不会被渲染到网页。如果不是,我想我可以将画布对象向左移出视图。

最后,你认为预处理图像的好方法是将它们发送到服务器cgi脚本并让它返回一个json像素数组?

+0

您必须使用Canvas。此外,如果您不希望canvas元素进行渲染,请在其中添加“display:none”。 –

回答

2

删除背景没有波涛汹涌的边界并不是一项简单的任务,即使通过图像编辑程序。至少,你必须实施某种反锯齿。

此外,操纵压缩成有损格式的图像不是一个好主意。

PNG在更简单的图像上压缩优于(大小方面)到JPG,连续填充相同颜色和某些类型的渐变。 JPG仅适用于以不可预知的方式混合大量不同颜色的异构图像。像照片一样。我想,在游戏精灵中哪个人不会期待。再次 - JPG是一种lossy格式。

至于Canvas元素,它的所有DOM树都是doesn't have to be added

让一个给定的颜色透明的最天真的算法是:绘制图像,获取像素数据,遍历数据并将每个像素颜色与给定颜色进行比较。如果匹配,设置alpha为0

帆布API方法,你需要:

在它的有点棘手的简单部分是CanvasPixelArray。要检查这样的阵列中的每个像素,你需要这样做:

for (var i = 0; i < pixelAr.length; i += 4) { 
    var r = pixelAr[i]; 
    var g = pixelAr[i + 1]; 
    var b = pixelAr[i + 2]; 
    var alpha = pixelAr[i + 3]; 
} 
+0

真的吗?我总是认为jpgs会更好。我从来没有研究过它,只是从保存gimp图像时的大小来考虑它。保存为jpg的图像通常比保存到png的图片要小很多 – Isaiah

+1

我也使用了与katspaugh解释的类似的方法,可以使用floodfill算法和颜色范围来使过程更好。 –

+0

@Shamaila Tahir,谢谢!颜色范围将允许沿着边缘的渐变透明度,与参考颜色的差异成比例? – katspaugh

1

就我个人而言,我不会走这条路。 JPEG图像是压缩的,这意味着无论您定义为背景颜色,在压缩文件中可能会稍微变化(即,您将获得经典的JPEG制品)。此外,除非为背景颜色定义了一个范围,而这又会使编辑变得更加复杂,否则您将无法支持部分透明度。在我看来,文件大小和性能/质量之间的权衡远不是值得的。

话虽如此,唯一可以从图像访问像素数据的方法是先将它放置在画布上。正如您所提到的,您可以在屏幕外使用画布,而无需将其附加到文档中。

如果我正确理解了上一个问题,则无法在服务器端使用画布元素。要使用服务器上的像素数据,您必须使用诸如PHP的图像库之类的东西。

如果所有的不挥洒你赞成仅仅使用PNG图像,这里的一些示例代码,将从加载图像删除指定的背景色:

$(document).ready(function() { 
    var matte_color = [0, 255, 0, 255]; // rgba: [0, 255]; 

    // Handles the loaded image element by erasing the matte color 
    // and appending the transformed image to the document. 
    function handleLoadedImage() { 
     eraseMatte(); // Eliminate the matte. 

     // Append the canvas element to the document where it is needed. 
     document.getElementById("parent_container").appendChild(canvas); 
    } 

    // Given the matte color defined at the top, clear all pixels to 100% transparency 
    // within the loaded sprite. 
    function eraseMatte() { 
     canvas.width = sprite.width; 
     canvas.height = sprite.height; 
     context.drawImage(sprite, 0, 0); // Draw the image on the canvas so we can read the pixels. 

     // Get the pixel data from the canvas. 
     var image_data = context.getImageData(0, 0, sprite.width, sprite.height); 
     var data = image_data.data; // Obtaining a direct handle is a huge performance boost. 
     var end = sprite.width * sprite.height * 4; // W x H x RGBA 

     // Loop over each pixel from the image and clear matte pixels as needed. 
     for (var i = 0; i < end; i += 4) { 
      if (data[i] == matte_color[0] && data[i + 1] == matte_color[1] && 
       data[i + 2] == matte_color[2] && data[i + 3] == matte_color[3]) { // Color match. 
       data[i] = data[i + 1] = data[i + 2] = data[i + 3] = 0; // Set pixel to transparent. 
      } 
     } 

     // Put the transformed image data back on the canvas. 
     context.putImageData(image_data, 0, 0); 
    } 

    var canvas = document.createElement("canvas"); 
    var context = canvas.getContext("2d"); 
    var sprite = new Image(); 
    sprite.onload = handleLoadedImage; 
    sprite.src = "sprite.jpg"; 
}); 
0

你可以做到这一点使用画布,不知道没有它是否可能。 一个简单的方法来实现你试图使用你的画布的背景下getImageData做:

imgData = myCanvasContext.getImageData(x1, y1, x2, y2); 

X1,Y1,X2,Y2是要获取数据的区域的coordenates,为全使用0, 0, width, height图像。 getImageData将返回一个ImageData,其中包含一个数组,每个像素都包含一个rgba值。这些值将按如下顺序排列: http://i.stack.imgur.com/tdHNJ.png

您可以操作数组imgData.data[index],编辑它的值并因此编辑图像。

下面是关于与画布上的HTML5编辑图像的好文章: http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/


为了不显示你在做什么,只是用CSS命令display:none;


创建画布

(...)如果我可以从精灵图像中删除背景颜色。我看到了这个优势,因为我们可以使用jpg代替PNG(...)

我真的建议你不要那么做。图像的jpg压缩可能会使图像编辑变得非常困难。去除jpg图像的背景并不容易,图像边框的数量越来越大。我不认为你将节省的大小将弥补从jpg图像中移除背景的辛勤工作。

3

下面是floodfill算法的函数,它从已经绘制在画布上的图像中移除背景。

在下面的代码中,canvas是HTML5 canvas元素,它的上下文是canvas.getContext(“2d”)。您可以更改colorRange的值并尝试使用不同颜色的功能。功能的最后一行

imageElement.src=canvas.toDataURL("image/png"); 

是在img标签内显示图像。所以你需要一个img和一个画布在你的页面上。如果你不想在img元素中显示图像,只需删除最后一行。

// Remove backgroud without ajax call, can be used in non IE browsers. 
function RemoveBackground(){ 

    var startR,startG,startB; 
    var canvasData; 

    var canvasWidth=canvas.width; 
    var canvasHeight=canvas.height; 
    canvasData=mainContext.getImageData(0,0,canvasWidth,canvasHeight); 
    startR = canvasData.data[0]; 
    startG = canvasData.data[1]; 
    startB = canvasData.data[2]; 
    if(startR==0&& startG==0 && startR==0) return; 
    var pixelStack = [[0, 0]]; 
    while(pixelStack.length) 
    { 
     var newPos, x, y, pixelPos, reachLeft, reachRight; 
     newPos = pixelStack.pop(); 
     x = newPos[0]; 
     y = newPos[1]; 

     pixelPos = (y*canvasWidth + x) * 4; 
     while(y-- >= 0 && matchStartColor(pixelPos,canvasData,startR,startG,startB)){ 
     pixelPos -= canvasWidth * 4; 
    } 
     pixelPos += canvasWidth * 4; 
     ++y; 
     reachLeft = false; 
     reachRight = false; 
     while(y++ < canvasHeight-1 && matchStartColor(pixelPos,canvasData,startR,startG,startB)) 
     { 
     colorPixel(pixelPos,canvasData); 
     if(x > 0) 
     { 
      if(matchStartColor(pixelPos-4,canvasData,startR,startG,startB)) 
      { 
      if(!reachLeft){ 
       pixelStack.push([x - 1, y]); 
       reachLeft = true; 
      } 
      } 
      else if(reachLeft) 
      { 
      reachLeft = false; 
      } 
     } 

     if(x < canvasWidth-1) 
     { 
      if(matchStartColor(pixelPos+4,canvasData,startR,startG,startB)) 
      { 
      if(!reachRight) 
      { 
       pixelStack.push([x + 1, y]); 
       reachRight = true; 
      } 
      } 
      else if(reachRight) 
      { 
      reachRight = false; 
      } 
     } 
     pixelPos += canvasWidth * 4; 
     } 
    } 
    context.putImageData(canvasData, 0, 0); 
    imageElement.src=canvas.toDataURL("image/png"); 

} 

// Helper function for remove background color. 
function matchStartColor(pixelPos,canvasData,startR,startG,startB) 
{ 
    var r = canvasData.data[pixelPos];  
    var g = canvasData.data[pixelPos+1]; 
    var b = canvasData.data[pixelPos+2]; 
    var colorRange=8; 

    return ((r >= startR-colorRange && r<=startR+colorRange) 
     &&(g >= startG-colorRange && g<=startG+colorRange) 
     &&(b >= startB-colorRange && b<= startB+colorRange)); 
} 
// Helper function for remove background color. 
function colorPixel(pixelPos,canvasData) 
{ 
    canvasData.data[pixelPos] = 255; 
    canvasData.data[pixelPos+1] = 255; 
    canvasData.data[pixelPos+2] = 255; 
} 
+0

我已经测试了一个图像的梯度背景和前景绘图中的相同颜色的功能,它的作品非常好,无论是洪水填充和颜色范围功能! – katspaugh

0

不完全一样,但你可以做到这一点。我可以给你一个开头 - 结帐这jsFiddle。我使用FabricJS构建了这个编辑器。

var canvas = new fabric.Canvas('c'); 
var imgInstance = new fabric.Image(imgElement); 
canvas.add(imgInstance);//initialize the Canvas with the image