gpt4 book ai didi

javascript - 像素化图像数据算法非常慢

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:15:23 24 4
gpt4 key购买 nike

我的工具中有这种像素化算法,但是当我今天将它应用到我的绘图应用程序时,它的性能非常糟糕。我想知道你是否可以帮我解决这个问题。

这是我的算法:

//apply pixalate algorithm
for(var x = 1; x < w; x += aOptions.blockSize)
{
for(var y = 1; y < h; y += aOptions.blockSize)
{
var pixel = sctx.getImageData(x, y, 1, 1);
dctx.fillStyle = "rgb("+pixel.data[0]+","+pixel.data[1]+","+pixel.data[2]+")";
dctx.fillRect(x, y, x + aOptions.blockSize - 1, y + aOptions.blockSize - 1);
}
}

我想知道您是否可以帮助我加快速度,我不确定是什么导致了性能下降。

(是的,我知道 imageSmoothingEnabled 技术,但是它不能让我完美地控制 block 大小)

最佳答案

快速硬件像素化

GPU 可以做到..

为什么不直接使用 GPU 通过 drawImage 调用来完成它,它要快得多。在像素化中以您想要的像素数将源 Canvas 绘制得更小,然后将其绘制到按比例放大的源 Canvas 上。您只需确保事先关闭图像平滑,否则像素化效果将不起作用。

无透明像素

对于没有透明像素的 Canvas ,这是一个更简单的解决方案,以下函数将执行此操作。像素化 Canvas ,其中 sctx 是源,dctx 是目标,pixelCount 是目标 x 轴上的像素 block 数。而且你会得到 filter 选项作为奖励,因为 GPU 正在为你做这些艰苦的工作。

请注意,您应该检查 2D 上下文 imageSmoothingEnabled 的 vendor 前缀,并为您打算支持的浏览器添加它们。

// pixelate where
// sctx is source context
// dctx is destination context
// pixelCount is the number of pixel blocks across in the destination
// filter boolean if true then pixel blocks as bilinear interpolation of all pixels involved
// if false then pixel blocks are nearest neighbour of pixel in center of block


function pixelate(sctx, dctx, pixelCount, filter){
var sw = sctx.canvas.width;
var sh = sctx.canvas.height;
var dw = dctx.canvas.width;
var dh = dctx.canvas.height;
var downScale = pixelCount / sw; // get the scale reduction
var pixH = Math.floor(sh * downScale); // get pixel y axis count

// clear destination
dctx.clearRect(0, 0, dw, dh);
// set the filter mode
dctx.mozImageSmoothingEnabled = dctx.imageSmoothingEnabled = filter;
// scale image down;
dctx.drawImage(sctx.canvas, 0, 0, pixelCount, pixH);

// scale image back up
// IMPORTANT for this to work you must turn off smoothing
dctx.mozImageSmoothingEnabled = dctx.imageSmoothingEnabled = false;
dctx.drawImage(dctx.canvas, 0, 0, pixelCount, pixH, 0, 0, dw, dh);

// restore smoothing assuming it was on.
dctx.mozImageSmoothingEnabled = dctx.imageSmoothingEnabled = true;
//all done
}

透明像素

如果您有透明像素,您将需要一个工作 Canvas 来容纳按比例缩小的图像。为此,添加一个 workCanvas 参数并返回相同的参数。它会为你创建一个 Canvas ,并在需要时调整它的大小,但你也应该保留它的副本,这样你就不会在每次像素化时都不必要地创建一个新的

function pixelate(sctx, dctx, pixelCount, filter, workCanvas){
var sw = sctx.canvas.width;
var sh = sctx.canvas.height;
var dw = dctx.canvas.width;
var dh = dctx.canvas.height;
var downScale = pixelCount / sw; // get the scale reduction
var pixH = Math.floor(sh * downScale); // get pixel y axis count

// create a work canvas if needed
if(workCanvas === undefined){
workCanvas = document.createElement("canvas");
workCanvas.width = pixelCount ;
workCanvas.height = pixH;
workCanvas.ctx = workCanvas.getContext("2d");
}else // resize if needed
if(workCanvas.width !== pixelCount || workCanvas.height !== pixH){
workCanvas.width = pixelCount ;
workCanvas.height = pixH;
workCanvas.ctx = workCanvas.getContext("2d");
}
// clear the workcanvas
workCanvas.ctx.clearRect(0, 0, pixelCount, pixH);
// set the filter mode Note the prefix, and I have been told same goes for IE
workCanvas.ctx.mozImageSmoothingEnabled = workCanvas.ctx.imageSmoothingEnabled = filter;
// scale image down;
workCanvas.ctx.drawImage(sctx.canvas, 0, 0, pixelCount, pixH);

// clear the destination
dctx.clearRect(0,0,dw,dh);
// scale image back up
// IMPORTANT for this to work you must turn off smoothing
dctx.mozImageSmoothingEnabled = dctx.imageSmoothingEnabled = false;
dctx.drawImage(workCanvas, 0, 0, pixelCount, pixH, 0, 0, dw, dh);

// restore smoothing assuming it was on.
dctx.mozImageSmoothingEnabled = dctx.imageSmoothingEnabled = true;
//all done
return workCanvas; // Return the canvas reference so there is no need to recreate next time
}

要使用透明版本,您需要持有 workCanvas 引用,否则您需要每次都创建一个新引用。

var pixelateWC; // create and leave undefined in the app global scope 
// (does not have to be JS context global) just of your app
// so you dont loss it between renders.

然后在你的主循环中

pixelateWC = pixelate(sourceCtx,destContext,20,true, pixelateWC);

因此该函数将在第一次创建它,然后将反复使用它直到它需要更改其大小或您使用以下命令将其删除

pixelateWC = undefined;

演示

我已经包含了一个演示(因为我的原始版本有一个错误)以确保一切正常。显示函数的透明版本。全屏 60fp 效果很好我做整个 Canvas 而不仅仅是分割部分。演示中的说明。

// adapted from QuickRunJS environment. 

// simple mouse
var mouse = (function(){
function preventDefault(e) { e.preventDefault(); }
var mouse = {
x : 0, y : 0, buttonRaw : 0,
bm : [1, 2, 4, 6, 5, 3], // masks for setting and clearing button raw bits;
mouseEvents : "mousemove,mousedown,mouseup".split(",")
};
function mouseMove(e) {
var t = e.type, m = mouse;
m.x = e.offsetX; m.y = e.offsetY;
if (m.x === undefined) { m.x = e.clientX; m.y = e.clientY; }
if (t === "mousedown") { m.buttonRaw |= m.bm[e.which-1];
} else if (t === "mouseup") { m.buttonRaw &= m.bm[e.which + 2];}
e.preventDefault();
}
mouse.start = function(element, blockContextMenu){
if(mouse.element !== undefined){ mouse.removeMouse();}
mouse.element = element;
mouse.mouseEvents.forEach(n => { element.addEventListener(n, mouseMove); } );
if(blockContextMenu === true){
element.addEventListener("contextmenu", preventDefault, false);
mouse.contextMenuBlocked = true;
}
}
mouse.remove = function(){
if(mouse.element !== undefined){
mouse.mouseEvents.forEach(n => { mouse.element.removeEventListener(n, mouseMove); } );
if(mouse.contextMenuBlocked === true){ mouse.element.removeEventListener("contextmenu", preventDefault);}
mouse.contextMenuBlocked = undefined;
mouse.element = undefined;
}
}
return mouse;
})();

// delete needed for my QuickRunJS environment
function removeCanvas(){
if(canvas !== undefined){
document.body.removeChild(canvas);
}
canvas = undefined;
canvasB = undefined;
canvasP = undefined;
}
// create onscreen, background, and pixelate canvas
function createCanvas(){
canvas = document.createElement("canvas");
canvas.style.position = "absolute";
canvas.style.left = "0px";
canvas.style.top = "0px";
canvas.style.zIndex = 1000;
document.body.appendChild(canvas);
canvasP = document.createElement("canvas");
canvasB = document.createElement("canvas");
}
function resizeCanvas(){
if(canvas === undefined){ createCanvas(); }
canvasB.width = canvasP.width = canvas.width = window.innerWidth;
canvasB.height = canvasP.height = canvas.height = window.innerHeight;
canvasB.ctx = canvasB.getContext("2d");
canvasP.ctx = canvasP.getContext("2d");
canvas.ctx = canvas.getContext("2d");
// lazy coder bug fix
joinPos = Math.floor(window.innerWidth/2);
}

function pixelate(sctx, dctx, pixelCount, filter, workCanvas){
var sw = sctx.canvas.width;
var sh = sctx.canvas.height;
var dw = dctx.canvas.width;
var dh = dctx.canvas.height;
var downScale = pixelCount / sw; // get the scale reduction
var pixH = Math.floor(sh * downScale); // get pixel y axis count

// create a work canvas if needed
if(workCanvas === undefined){
workCanvas = document.createElement("canvas");
workCanvas.width = pixelCount;
workCanvas.height = pixH;
workCanvas.ctx = workCanvas.getContext("2d");
}else // resize if needed
if(workCanvas.width !== pixelCount || workCanvas.height !== pixH){
workCanvas.width = pixelCount;
workCanvas.height = pixH;
workCanvas.ctx = workCanvas.getContext("2d");
}
// clear the workcanvas
workCanvas.ctx.clearRect(0, 0, pixelCount, pixH);
// set the filter mode
workCanvas.ctx.mozImageSmoothingEnabled = workCanvas.ctx.imageSmoothingEnabled = filter;
// scale image down;
workCanvas.ctx.drawImage(sctx.canvas, 0, 0, pixelCount, pixH);

// clear the destination
dctx.clearRect(0,0,dw,dh);
// scale image back up
// IMPORTANT for this to work you must turn off smoothing
dctx.mozImageSmoothingEnabled = dctx.imageSmoothingEnabled = false;
dctx.drawImage(workCanvas, 0, 0, pixelCount, pixH, 0, 0, dw, dh);

// restore smoothing assuming it was on.
dctx.mozImageSmoothingEnabled = dctx.imageSmoothingEnabled = true;
//all done
return workCanvas; // Return the canvas reference so there is no need to recreate next time
}



var canvas,canvaP, canvasB;
// create and size canvas
resizeCanvas();
// start mouse listening to canvas
mouse.start(canvas,true); // flag that context needs to be blocked
// listen to resize
window.addEventListener("resize",resizeCanvas);

// get some colours
const NUM_COLOURS = 10;
var currentCol = 0;
var cols = (function(count){
for(var i = 0, a = []; i < count; i ++){
a.push("hsl("+Math.floor((i/count)*360)+",100%,50%)");
}
return a;
})(NUM_COLOURS);
var holdExit = 0; // To stop in QuickRunJS environment
var workCanvas;
var joinPos = Math.floor(canvas.width / 2);
var mouseOverJoin = false;
var dragJoin = false;
var drawing = false;
var filterChange = 0;
var filter = true;
ctx = canvas.ctx;
function update(time){
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.drawImage(canvasB, 0, 0, joinPos, canvas.height, 0 , 0, joinPos, canvas.height); // draw background
ctx.drawImage(canvasP,
joinPos, 0,
canvas.width - joinPos, canvas.height,
joinPos, 0,
canvas.width - joinPos, canvas.height
); // pixilation background

if(dragJoin){
if(!(mouse.buttonRaw & 1)){
dragJoin = false;
canvas.style.cursor = "default";
if(filterChange < 20){
filter = !filter;
}
}else{
joinPos = mouse.x;
}
filterChange += 1;
mouseOverJoin = true;
}else{
if(Math.abs(mouse.x - joinPos) < 4 && ! drawing){
if(mouse.buttonRaw & 1){
canvas.style.cursor = "none";
dragJoin = true;
filterChange = 0;
}else{
canvas.style.cursor = "e-resize";
}
mouseOverJoin = true;
}else{
canvas.style.cursor = "default";
mouseOverJoin = false;
if(mouse.buttonRaw & 1){
canvasB.ctx.fillStyle = cols[currentCol % NUM_COLOURS];
canvasB.ctx.beginPath();
canvasB.ctx.arc(mouse.x, mouse.y, 20, 0, Math.PI * 2);
canvasB.ctx.fill();
drawing = true
}else{
drawing = false;
}
}
ctx.fillStyle = cols[currentCol % NUM_COLOURS];
ctx.beginPath();
ctx.arc(mouse.x, mouse.y, 20, 0, Math.PI * 2);
ctx.fill();
}
if(mouse.buttonRaw & 4){ // setp cols on right button
currentCol += 1;
}
if(mouse.buttonRaw & 2){ // setp cols on right button
canvasB.ctx.clearRect(0, 0, canvas.width, canvas.height);
holdExit += 1;
}else{
holdExit = 0;
}
workCanvas = pixelate(canvasB.ctx, canvasP.ctx, 30, filter, workCanvas);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
if(mouseOverJoin){
ctx.fillStyle = "rgba(0,255,0,0.5)";
ctx.fillRect(joinPos - 3, 0, 6, canvas.height);
ctx.fillRect(joinPos - 2, 0, 4, canvas.height);
ctx.fillRect(joinPos - 1, 0, 2, canvas.height);
}
ctx.strokeRect(joinPos - 3, 0, 6, canvas.height);

ctx.font = "18px arial";
ctx.textAlign = "left";
ctx.fillStyle = "black"
ctx.fillText("Normal canvas.", 10, 20)
ctx.textAlign = "right";
ctx.fillText("Pixelated canvas.", canvas.width - 10, 20);

ctx.textAlign = "center";
ctx.fillText("Click drag to move join.", joinPos, canvas.height - 62);
ctx.fillText("Click to change Filter : " + (filter ? "Bilinear" : "Nearest"), joinPos, canvas.height - 40);

ctx.fillText("Click drag to draw.", canvas.width / 2, 20);
ctx.fillText("Right click change colour.", canvas.width / 2, 42);
ctx.fillText("Middle click to clear.", canvas.width / 2, 64);

if(holdExit < 60){
requestAnimationFrame(update);
}else{
removeCanvas();
}
}
requestAnimationFrame(update);

关于javascript - 像素化图像数据算法非常慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36678335/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com