在创建我的html5游戏引擎时,我已经能够做一些不错的事情并获得一些很酷的功能。在制作游戏的合同中,我被要求查看是否可以从精灵图像中删除背景颜色。我看到了这个因素,因为我们可以在pngs上使用jpgs并减小图像的大小。
我有什么方法可以使用纯JavaScript进行此操作吗?我希望能够在不使用canvas元素的情况下做到这一点,因此它可以更快,但如果我必须这样做。
如果我必须这样做,我还有另一个问题,我不希望canvas对象显示我使用的,我可以使用带有document.createElement的canvas对象而不将其应用于文档吗?这将是很好的,因为它不必呈现到网页。如果不是,我想我可以将画布对象移到视图左侧。
最后你认为预处理图像的好方法是将它们发送到服务器cgi脚本并让它返回一个json像素数组吗?
答案 0 :(得分: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;
}
答案 1 :(得分:2)
删除没有波动边框的背景并不是一项简单的任务,即使是在图像编辑程序中也是如此。至少,你必须实现某种抗锯齿效果。
此外,操纵压缩成有损格式的图像并不是一个好主意。
PNG压缩在JPG上对于更简单的图像具有优越性(连续填充相同颜色和某些类型的渐变)。 JPG仅适用于以不可预测的方式混合大量不同颜色的异构图像。喜欢照片。我想,哪一个人不会期待游戏精灵。而且 - JPG是一种lossy格式。
至于Canvas元素,它根本就是doesn't have to be added到DOM树。
使给定颜色透明的最天真的算法是这样的:绘制图像,获取其像素数据,迭代数据并将每个像素颜色与给定颜色进行比较。如果匹配,则将alpha设置为0.
您需要的Canvas 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];
}
答案 2 :(得分:1)
我个人不会走这条路。 JPEG图像被压缩,这意味着无论您定义为背景颜色在压缩文件中可能会稍微改变(即,您将获得经典的JPEG伪像)。此外,除非您为背景颜色定义范围,否则您将无法支持部分透明度,这反过来会使编辑更加复杂。在我看来,文件大小和性能/质量之间的权衡在这里是值得的。
话虽如此,您可以从图像访问像素数据的唯一方法是首先将其放在画布上。正如您所提到的,您可以在内存中使用画布屏幕,而无需将其附加到文档中。
如果我正确理解了您的上一个问题,则无法使用服务器端的canvas元素。要处理服务器上的像素数据,您必须使用类似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";
});
答案 3 :(得分:0)
你可以使用画布来做,不知道没有它是否可行。
实现您要执行的操作的简单方法是在画布的上下文中使用getImageData
:
imgData = myCanvasContext.getImageData(x1, y1, x2, y2);
x1,y1,x2,y2是您想要获取数据的区域的共存,用于整个使用0, 0, width, height
图像。 getImageData
会返回ImageData
,其中包含一个包含每个像素的rgba值的数组。这些值将按如下顺序排序:
http://i.stack.imgur.com/tdHNJ.png
您可以操纵数组imgData.data[index]
,编辑数值,然后编辑图像。
这是一篇关于使用canvas在html5上编辑图像的好文章: http://beej.us/blog/2010/02/html5s-canvas-part-ii-pixel-manipulation/
要显示您正在执行的操作,只需使用css命令创建画布display:none;
(...)如果我可以从精灵图像中删除背景颜色。我看到了这个因为我们可以在pngs(...)
上使用jpgs
我真的建议你不要这样做。图像的jpg压缩可以使图像编辑非常困难。删除jpg图像的背景并不容易,并且图像上的边框数量变得越来越难。我不认为你会节省的尺寸将弥补从jpg图像中删除背景的艰苦工作。
答案 4 :(得分:0)