jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
<img src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png" id="sourceImage" crossOrigin="Anonymous"></img>
let sourceImage = document.getElementById("sourceImage");
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
let width = sourceImage.width;
let height = sourceImage.height;
canvas.width = width;
canvas.height = height;
context.drawImage(
sourceImage, 0, 0, width, height, 0, 0, width, height);
let imageData = context.getImageData(0, 0, width, height);
function hashImageBeforeOptimization(imageData, blockBits)
{
function median(data)
{
let mdarr = data.slice(0);
mdarr.sort((a, b) => a - b);
if (mdarr.length % 2 === 0)
{
return (mdarr[mdarr.length / 2 - 1] + mdarr[mdarr.length / 2]) / 2.0;
}
return mdarr[Math.floor(mdarr.length / 2)];
}
function translateBlocksToBits(blocks, pixelsPerBlock)
{
let halfBlockValue = pixelsPerBlock * 256 * 3 / 2;
let bandsize = blocks.length / 4;
// Compare medians across four horizontal bands
for (let i = 0; i < 4; i++)
{
let m = median(blocks.slice(i * bandsize, (i + 1) * bandsize));
for (let j = i * bandsize; j < (i + 1) * bandsize; j++)
{
let v = blocks[j];
// Output a 1 if the block is brighter than the median.
// With images dominated by black or white, the median may
// end up being 0 or the max value, and thus having a lot
// of blocks of value equal to the median. To avoid
// generating hashes of all zeros or ones, in that case output
// 0 if the median is in the lower value space, 1 otherwise
blocks[j] = Number(v > m ||
(Math.abs(v - m) < 1 && m > halfBlockValue));
}
}
}
function bitsToHexhash(bitsArray)
{
let hex = [];
for (let i = 0; i < bitsArray.length; i += 4)
{
let nibble = bitsArray.slice(i, i + 4);
hex.push(parseInt(nibble.join(""), 2).toString(16));
}
return hex.join("");
}
function bmvbhashEven(data, bits)
{
let blocksizeX = Math.floor(data.width / bits);
let blocksizeY = Math.floor(data.height / bits);
let result = [];
for (let y = 0; y < bits; y++)
{
for (let x = 0; x < bits; x++)
{
let total = 0;
for (let iy = 0; iy < blocksizeY; iy++)
{
for (let ix = 0; ix < blocksizeX; ix++)
{
let cx = x * blocksizeX + ix;
let cy = y * blocksizeY + iy;
let ii = (cy * data.width + cx) * 4;
let alpha = data.data[ii + 3];
if (alpha === 0)
{
total += 765;
}
else
{
total += data.data[ii] + data.data[ii + 1] + data.data[ii + 2];
}
}
}
result.push(total);
}
}
translateBlocksToBits(result, blocksizeX * blocksizeY);
return bitsToHexhash(result);
}
function bmvbhash(data, bits)
{
let result = [];
let i; let j; let x; let y;
let blockWidth; let blockHeight;
let weightTop; let weightBottom; let weightLeft; let weightRight;
let blockTop; let blockBottom; let blockLeft; let blockRight;
let yMod; let yFrac; let yInt;
let xMod; let xFrac; let xInt;
let blocks = [];
let evenX = data.width % bits === 0;
let evenY = data.height % bits === 0;
if (evenX && evenY)
{
return bmvbhashEven(data, bits);
}
// initialize blocks array with 0s
for (i = 0; i < bits; i++)
{
blocks.push([]);
for (j = 0; j < bits; j++)
{
blocks[i].push(0);
}
}
blockWidth = data.width / bits;
blockHeight = data.height / bits;
for (y = 0; y < data.height; y++)
{
if (evenY)
{
// don't bother dividing y, if the size evenly divides by bits
blockTop = blockBottom = Math.floor(y / blockHeight);
weightTop = 1;
weightBottom = 0;
}
else
{
yMod = (y + 1) % blockHeight;
yFrac = yMod - Math.floor(yMod);
yInt = yMod - yFrac;
weightTop = (1 - yFrac);
weightBottom = (yFrac);
// yInt will be 0 on bottom/right borders and on block boundaries
if (yInt > 0 || (y + 1) === data.height)
{
blockTop = blockBottom = Math.floor(y / blockHeight);
}
else
{
blockTop = Math.floor(y / blockHeight);
blockBottom = Math.ceil(y / blockHeight);
}
}
for (x = 0; x < data.width; x++)
{
let ii = (y * data.width + x) * 4;
let avgvalue; let alpha = data.data[ii + 3];
if (alpha === 0)
{
avgvalue = 765;
}
else
{
avgvalue = data.data[ii] + data.data[ii + 1] + data.data[ii + 2];
}
if (evenX)
{
blockLeft = blockRight = Math.floor(x / blockWidth);
weightLeft = 1;
weightRight = 0;
}
else
{
xMod = (x + 1) % blockWidth;
xFrac = xMod - Math.floor(xMod);
xInt = xMod - xFrac;
weightLeft = (1 - xFrac);
weightRight = xFrac;
// xInt will be 0 on bottom/right borders and on block boundaries
if (xInt > 0 || (x + 1) === data.width)
{
blockLeft = blockRight = Math.floor(x / blockWidth);
}
else
{
blockLeft = Math.floor(x / blockWidth);
blockRight = Math.ceil(x / blockWidth);
}
}
// add weighted pixel value to relevant blocks
blocks[blockTop][blockLeft] += avgvalue * weightTop * weightLeft;
blocks[blockTop][blockRight] += avgvalue * weightTop * weightRight;
blocks[blockBottom][blockLeft] += avgvalue * weightBottom * weightLeft;
blocks[blockBottom][blockRight] +=
avgvalue * weightBottom * weightRight;
}
}
for (i = 0; i < bits; i++)
{
for (j = 0; j < bits; j++)
{
result.push(blocks[i][j]);
}
}
translateBlocksToBits(result, blockWidth * blockHeight);
return bitsToHexhash(result);
}
return bmvbhash(imageData, blockBits);
}
function hashImageIssue55(imageData, blockBits)
{
function median(data)
{
let mdarr = data.slice(0);
mdarr.sort((a, b) => a - b);
if (mdarr.length % 2 === 0)
{
return (mdarr[mdarr.length / 2 - 1] + mdarr[mdarr.length / 2]) / 2.0;
}
return mdarr[Math.floor(mdarr.length / 2)];
}
function translateBlocksToBits(blocks, pixelsPerBlock)
{
let halfBlockValue = pixelsPerBlock * 256 * 3 / 2;
let bandsize = blocks.length / 4;
// Compare medians across four horizontal bands
for (let i = 0; i < 4; i++)
{
let m = median(blocks.slice(i * bandsize, (i + 1) * bandsize));
for (let j = i * bandsize; j < (i + 1) * bandsize; j++)
{
let v = blocks[j];
// Output a 1 if the block is brighter than the median.
// With images dominated by black or white, the median may
// end up being 0 or the max value, and thus having a lot
// of blocks of value equal to the median. To avoid
// generating hashes of all zeros or ones, in that case output
// 0 if the median is in the lower value space, 1 otherwise
blocks[j] = Number(v > m ||
(Math.abs(v - m) < 1 && m > halfBlockValue));
}
}
}
function bitsToHexhash(bitsArray)
{
let hex = [];
for (let i = 0; i < bitsArray.length; i += 4)
{
let nibble = bitsArray.slice(i, i + 4);
hex.push(parseInt(nibble.join(""), 2).toString(16));
}
return hex.join("");
}
function bmvbhashEven(data, bits)
{
let blocksizeX = Math.floor(data.width / bits);
let blocksizeY = Math.floor(data.height / bits);
let result = [];
let imgData = data.data;
let dataWidth = data.width;
for (let y = 0; y < bits; y++)
{
for (let x = 0; x < bits; x++)
{
let total = 0;
for (let iy = 0; iy < blocksizeY; iy++)
{
for (let ix = 0; ix < blocksizeX; ix++)
{
let cx = x * blocksizeX + ix;
let cy = y * blocksizeY + iy;
let ii = (cy * dataWidth + cx) * 4;
let alpha = imgData[ii + 3];
if (alpha === 0)
{
total += 765;
}
else
{
total += imgData[ii] + imgData[ii + 1] + imgData[ii + 2];
}
}
}
result.push(total);
}
}
translateBlocksToBits(result, blocksizeX * blocksizeY);
return bitsToHexhash(result);
}
function bmvbhash(data, bits)
{
let result = [];
let i; let j; let x; let y;
let blockWidth; let blockHeight;
let weightTop; let weightBottom; let weightLeft; let weightRight;
let blockTop; let blockBottom; let blockLeft; let blockRight;
let yMod; let yFrac; let yInt;
let xMod; let xFrac; let xInt;
let blocks = [];
let evenX = data.width % bits === 0;
let evenY = data.height % bits === 0;
if (evenX && evenY)
{
return bmvbhashEven(data, bits);
}
// initialize blocks array with 0s
for (i = 0; i < bits; i++)
{
blocks.push([]);
for (j = 0; j < bits; j++)
{
blocks[i].push(0);
}
}
let imgData = data.data;
let dataWidth = data.width;
let dataHeight = data.height;
blockWidth = dataWidth / bits;
blockHeight = dataHeight / bits;
for (y = 0; y < dataHeight; y++)
{
if (evenY)
{
// don't bother dividing y, if the size evenly divides by bits
blockTop = blockBottom = Math.floor(y / blockHeight);
weightTop = 1;
weightBottom = 0;
}
else
{
yMod = (y + 1) % blockHeight;
yFrac = yMod - Math.floor(yMod);
yInt = yMod - yFrac;
weightTop = (1 - yFrac);
weightBottom = (yFrac);
// yInt will be 0 on bottom/right borders and on block boundaries
if (yInt > 0 || (y + 1) === dataHeight)
{
blockTop = blockBottom = Math.floor(y / blockHeight);
}
else
{
blockTop = Math.floor(y / blockHeight);
blockBottom = Math.ceil(y / blockHeight);
}
}
for (x = 0; x < dataWidth; x++)
{
let ii = (y * dataWidth + x) * 4;
let avgvalue; let alpha = imgData[ii + 3];
if (alpha === 0)
{
avgvalue = 765;
}
else
{
avgvalue = imgData[ii] + imgData[ii + 1] + imgData[ii + 2];
}
if (evenX)
{
blockLeft = blockRight = Math.floor(x / blockWidth);
weightLeft = 1;
weightRight = 0;
}
else
{
xMod = (x + 1) % blockWidth;
xFrac = xMod - Math.floor(xMod);
xInt = xMod - xFrac;
weightLeft = (1 - xFrac);
weightRight = xFrac;
// xInt will be 0 on bottom/right borders and on block boundaries
if (xInt > 0 || (x + 1) === dataWidth)
{
blockLeft = blockRight = Math.floor(x / blockWidth);
}
else
{
blockLeft = Math.floor(x / blockWidth);
blockRight = Math.ceil(x / blockWidth);
}
}
// add weighted pixel value to relevant blocks
blocks[blockTop][blockLeft] += avgvalue * weightTop * weightLeft;
blocks[blockTop][blockRight] += avgvalue * weightTop * weightRight;
blocks[blockBottom][blockLeft] += avgvalue * weightBottom * weightLeft;
blocks[blockBottom][blockRight] +=
avgvalue * weightBottom * weightRight;
}
}
for (i = 0; i < bits; i++)
{
for (j = 0; j < bits; j++)
{
result.push(blocks[i][j]);
}
}
translateBlocksToBits(result, blockWidth * blockHeight);
return bitsToHexhash(result);
}
return bmvbhash(imageData, blockBits);
}
function hashImageIssue62(imageData, blockBits)
{
function median(mdarr)
{
mdarr.sort((a, b) => a - b);
let {length} = mdarr;
if (length % 2 === 0)
{
return (mdarr[length / 2 - 1] + mdarr[length / 2]) / 2.0;
}
return mdarr[Math.floor(length / 2)];
}
function translateBlocksToBits(blocks, pixelsPerBlock)
{
let halfBlockValue = pixelsPerBlock * 256 * 3 / 2;
let bandsize = blocks.length / 4;
// Compare medians across four horizontal bands
for (let i = 0; i < 4; i++)
{
let index = i * bandsize;
let length = (i + 1) * bandsize;
let m = median(blocks.slice(index, length));
for (let j = index; j < length; j++)
{
let v = blocks[j];
// Output a 1 if the block is brighter than the median.
// With images dominated by black or white, the median may
// end up being 0 or the max value, and thus having a lot
// of blocks of value equal to the median. To avoid
// generating hashes of all zeros or ones, in that case output
// 0 if the median is in the lower value space, 1 otherwise
blocks[j] = (v > m ||
(Math.abs(v - m) < 1 && m > halfBlockValue)) ? 1 : 0;
}
}
}
function bitsToHexhash(bitsArray)
{
let hex = [];
let {length} = bitsArray;
for (let i = 0; i < length; i += 4)
{
let nibble = bitsArray.slice(i, i + 4);
hex.push(parseInt(nibble.join(""), 2).toString(16));
}
return hex.join("");
}
function bmvbhashEven(data, bits)
{
let {width, height, data: imageData} = data;
let blocksizeX = Math.floor(width / bits);
let blocksizeY = Math.floor(height / bits);
let result = [];
for (let y = 0; y < bits; y++)
{
for (let x = 0; x < bits; x++)
{
let total = 0;
for (let iy = 0; iy < blocksizeY; iy++)
{
for (let ix = 0; ix < blocksizeX; ix++)
{
let cx = x * blocksizeX + ix;
let cy = y * blocksizeY + iy;
let ii = (cy * width + cx) * 4;
let alpha = imageData[ii + 3];
if (alpha === 0)
{
total += 765;
}
else
{
total += imageData[ii] + imageData[ii + 1] + imageData[ii + 2];
}
}
}
result.push(total);
}
}
translateBlocksToBits(result, blocksizeX * blocksizeY);
return bitsToHexhash(result);
}
function bmvbhash(data, bits)
{
let result = [];
let i; let j; let x; let y;
let blockWidth; let blockHeight;
let weightTop; let weightBottom; let weightLeft; let weightRight;
let blockTop; let blockBottom; let blockLeft; let blockRight;
let yMod; let yFrac; let yInt;
let xMod; let xFrac; let xInt;
let blocks = [];
let {width, height, data: imageData} = data;
let evenX = width % bits === 0;
let evenY = height % bits === 0;
if (evenX && evenY)
{
return bmvbhashEven(data, bits);
}
// initialize blocks array with 0s
for (i = 0; i < bits; i++)
{
let block = [];
blocks.push(block);
for (j = 0; j < bits; j++)
{
block.push(0);
}
}
blockWidth = width / bits;
blockHeight = height / bits;
for (y = 0; y < height; y++)
{
if (evenY)
{
// don't bother dividing y, if the size evenly divides by bits
blockTop = blockBottom = Math.floor(y / blockHeight);
weightTop = 1;
weightBottom = 0;
}
else
{
yMod = (y + 1) % blockHeight;
yFrac = yMod - Math.floor(yMod);
yInt = yMod - yFrac;
weightTop = (1 - yFrac);
weightBottom = (yFrac);
// yInt will be 0 on bottom/right borders and on block boundaries
if (yInt > 0 || (y + 1) === height)
{
blockTop = blockBottom = Math.floor(y / blockHeight);
}
else
{
blockTop = Math.floor(y / blockHeight);
blockBottom = Math.ceil(y / blockHeight);
}
}
for (x = 0; x < width; x++)
{
let ii = (y * width + x) * 4;
let avgvalue = 765;
let alpha = imageData[ii + 3];
if (alpha !== 0)
{
avgvalue = imageData[ii] + imageData[ii + 1] + imageData[ii + 2];
}
if (evenX)
{
blockLeft = blockRight = Math.floor(x / blockWidth);
weightLeft = 1;
weightRight = 0;
}
else
{
xMod = (x + 1) % blockWidth;
xFrac = xMod - Math.floor(xMod);
xInt = xMod - xFrac;
weightLeft = (1 - xFrac);
weightRight = xFrac;
// xInt will be 0 on bottom/right borders and on block boundaries
if (xInt > 0 || (x + 1) === width)
{
blockLeft = blockRight = Math.floor(x / blockWidth);
}
else
{
blockLeft = Math.floor(x / blockWidth);
blockRight = Math.ceil(x / blockWidth);
}
}
// add weighted pixel value to relevant blocks
blocks[blockTop][blockLeft] += avgvalue * weightTop * weightLeft;
blocks[blockTop][blockRight] += avgvalue * weightTop * weightRight;
blocks[blockBottom][blockLeft] += avgvalue * weightBottom * weightLeft;
blocks[blockBottom][blockRight] +=
avgvalue * weightBottom * weightRight;
}
}
for (i = 0; i < bits; i++)
{
let block = blocks[i];
for (j = 0; j < bits; j++)
{
result.push(block[j]);
}
}
translateBlocksToBits(result, blockWidth * blockHeight);
return bitsToHexhash(result);
}
return bmvbhash(imageData, blockBits);
}
Ready to run.
Test | Ops/sec | |
---|---|---|
Before optimization |
| ready |
Issue 55 |
| ready |
Issue 62 |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.