Skip to content

Commit bf1a4ad

Browse files
committed
Merge pull request mozilla#2499 from yurydelendik/rescale
Refactors rescaleImage: improving quality of scanned images
2 parents 956d5b0 + 1826daa commit bf1a4ad

File tree

1 file changed

+104
-71
lines changed

1 file changed

+104
-71
lines changed

src/canvas.js

Lines changed: 104 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -245,57 +245,109 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
245245
}
246246
}
247247

248-
function rescaleImage(pixels, width, height, widthScale, heightScale) {
249-
var scaledWidth = Math.ceil(width / widthScale);
250-
var scaledHeight = Math.ceil(height / heightScale);
251-
252-
var itemsSum = new Float32Array(scaledWidth * scaledHeight * 3);
253-
var itemsCount = new Float32Array(scaledWidth * scaledHeight);
254-
var maxAlphas = new Uint8Array(scaledWidth * scaledHeight);
255-
for (var i = 0, position = 0; i < height; i++) {
256-
var lineOffset = (0 | (i / heightScale)) * scaledWidth;
257-
for (var j = 0; j < width; j++) {
258-
var countOffset = lineOffset + (0 | (j / widthScale));
259-
var sumOffset = countOffset * 3;
260-
var maxAlpha = maxAlphas[countOffset];
261-
var currentAlpha = pixels[position + 3];
262-
if (maxAlpha < currentAlpha) {
263-
// lowering total alpha
264-
var scale = 1 - (currentAlpha - maxAlpha) / 255;
265-
itemsSum[sumOffset] *= scale;
266-
itemsSum[sumOffset + 1] *= scale;
267-
itemsSum[sumOffset + 2] *= scale;
268-
maxAlphas[countOffset] = maxAlpha = currentAlpha;
248+
function putBinaryImageData(ctx, data, w, h) {
249+
var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
250+
ctx.getImageData(0, 0, w, h);
251+
252+
var tmpImgDataPixels = tmpImgData.data;
253+
if ('set' in tmpImgDataPixels)
254+
tmpImgDataPixels.set(data);
255+
else {
256+
// Copy over the imageData pixel by pixel.
257+
for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
258+
tmpImgDataPixels[i] = data[i];
259+
}
260+
261+
ctx.putImageData(tmpImgData, 0, 0);
262+
}
263+
264+
function prescaleImage(pixels, width, height, widthScale, heightScale) {
265+
pixels = new Uint8Array(pixels); // creating a copy
266+
while (widthScale > 2 || heightScale > 2) {
267+
if (heightScale > 2) {
268+
// scaling image twice vertically
269+
var rowSize = width * 4;
270+
var k = 0, l = 0;
271+
for (var i = 0; i < height - 1; i += 2) {
272+
for (var j = 0; j < width; j++) {
273+
var alpha1 = pixels[k + 3], alpha2 = pixels[k + 3 + rowSize];
274+
if (alpha1 === alpha2) {
275+
pixels[l] = (pixels[k] + pixels[k + rowSize]) >> 1;
276+
pixels[l + 1] = (pixels[k + 1] + pixels[k + 1 + rowSize]) >> 1;
277+
pixels[l + 2] = (pixels[k + 2] + pixels[k + 2 + rowSize]) >> 1;
278+
pixels[l + 3] = alpha1;
279+
} else if (alpha1 < alpha2) {
280+
var d = 256 - alpha2 + alpha1;
281+
pixels[l] = (pixels[k] * d + (pixels[k + rowSize] << 8)) >> 9;
282+
pixels[l + 1] = (pixels[k + 1] * d +
283+
(pixels[k + 1 + rowSize] << 8)) >> 9;
284+
pixels[l + 2] = (pixels[k + 2] * d +
285+
(pixels[k + 2 + rowSize] << 8)) >> 9;
286+
pixels[l + 3] = alpha2;
287+
} else {
288+
var d = 256 - alpha1 + alpha2;
289+
pixels[l] = ((pixels[k] << 8) + pixels[k + rowSize] * d) >> 9;
290+
pixels[l + 1] = ((pixels[k + 1] << 8) +
291+
pixels[k + 1 + rowSize] * d) >> 9;
292+
pixels[l + 2] = ((pixels[k + 2] << 8) +
293+
pixels[k + 2 + rowSize] * d) >> 9;
294+
pixels[l + 3] = alpha1;
295+
}
296+
k += 4; l += 4;
297+
}
298+
k += rowSize;
269299
}
270-
if (maxAlpha > currentAlpha) {
271-
var scale = 1 - (maxAlpha - currentAlpha) / 255;
272-
itemsSum[sumOffset] += pixels[position] * scale;
273-
itemsSum[sumOffset + 1] += pixels[position + 1] * scale;
274-
itemsSum[sumOffset + 2] += pixels[position + 2] * scale;
275-
itemsCount[countOffset] += scale;
276-
} else {
277-
itemsSum[sumOffset] += pixels[position];
278-
itemsSum[sumOffset + 1] += pixels[position + 1];
279-
itemsSum[sumOffset + 2] += pixels[position + 2];
280-
itemsCount[countOffset]++;
300+
if (height & 1) {
301+
for (var i = 0; i < rowSize; i++) {
302+
pixels[l++] = pixels[k++];
303+
}
304+
}
305+
height = (height + 1) >> 1;
306+
heightScale /= 2;
307+
}
308+
if (widthScale > 2) {
309+
// scaling image twice horizontally
310+
var k = 0, l = 0;
311+
for (var i = 0; i < height; i++) {
312+
for (var j = 0; j < width - 1; j += 2) {
313+
var alpha1 = pixels[k + 3], alpha2 = pixels[k + 7];
314+
if (alpha1 === alpha2) {
315+
pixels[l] = (pixels[k] + pixels[k + 4]) >> 1;
316+
pixels[l + 1] = (pixels[k + 1] + pixels[k + 5]) >> 1;
317+
pixels[l + 2] = (pixels[k + 2] + pixels[k + 6]) >> 1;
318+
pixels[l + 3] = alpha1;
319+
} else if (alpha1 < alpha2) {
320+
var d = 256 - alpha2 + alpha1;
321+
pixels[l] = (pixels[k] * d + (pixels[k + 4] << 8)) >> 9;
322+
pixels[l + 1] = (pixels[k + 1] * d + (pixels[k + 5] << 8)) >> 9;
323+
pixels[l + 2] = (pixels[k + 2] * d + (pixels[k + 6] << 8)) >> 9;
324+
pixels[l + 3] = alpha2;
325+
} else {
326+
var d = 256 - alpha1 + alpha2;
327+
pixels[l] = ((pixels[k] << 8) + pixels[k + 4] * d) >> 9;
328+
pixels[l + 1] = ((pixels[k + 1] << 8) + pixels[k + 5] * d) >> 9;
329+
pixels[l + 2] = ((pixels[k + 2] << 8) + pixels[k + 6] * d) >> 9;
330+
pixels[l + 3] = alpha1;
331+
}
332+
k += 8; l += 4;
333+
}
334+
if (width & 1) {
335+
pixels[l++] = pixels[k++];
336+
pixels[l++] = pixels[k++];
337+
pixels[l++] = pixels[k++];
338+
pixels[l++] = pixels[k++];
339+
}
281340
}
282-
position += 4;
341+
width = (width + 1) >> 1;
342+
widthScale /= 2;
283343
}
284344
}
285-
var tmpCanvas = createScratchCanvas(scaledWidth, scaledHeight);
345+
346+
var tmpCanvas = createScratchCanvas(width, height);
286347
var tmpCtx = tmpCanvas.getContext('2d');
287-
var imgData = tmpCtx.getImageData(0, 0, scaledWidth, scaledHeight);
288-
pixels = imgData.data;
289-
var j = 0, q = 0;
290-
for (var i = 0, ii = scaledWidth * scaledHeight; i < ii; i++) {
291-
var count = itemsCount[i];
292-
pixels[j] = itemsSum[q++] / count;
293-
pixels[j + 1] = itemsSum[q++] / count;
294-
pixels[j + 2] = itemsSum[q++] / count;
295-
pixels[j + 3] = maxAlphas[i];
296-
j += 4;
297-
}
298-
tmpCtx.putImageData(imgData, 0, 0);
348+
putBinaryImageData(tmpCtx, pixels.subarray(0, width * height * 4),
349+
width, height);
350+
299351
return tmpCanvas;
300352
}
301353

@@ -1314,19 +1366,19 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
13141366
var tmpCanvas = createScratchCanvas(width, height);
13151367
var tmpCtx = tmpCanvas.getContext('2d');
13161368

1317-
if (widthScale >= 2 || heightScale >= 2) {
1369+
if (widthScale > 2 || heightScale > 2) {
13181370
// canvas does not resize well large images to small -- using simple
13191371
// algorithm to perform pre-scaling
1320-
tmpCanvas = rescaleImage(imgData.data,
1372+
tmpCanvas = prescaleImage(imgData.data,
13211373
width, height,
13221374
widthScale, heightScale);
1323-
ctx.scale(widthScale, heightScale);
1324-
ctx.drawImage(tmpCanvas, 0, -height / heightScale);
1375+
ctx.drawImage(tmpCanvas, 0, 0, tmpCanvas.width, tmpCanvas.height,
1376+
0, -height, width, height);
13251377
} else {
13261378
if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
13271379
tmpCtx.putImageData(imgData, 0, 0);
13281380
} else {
1329-
this.putBinaryImageData(tmpCtx, imgData);
1381+
putBinaryImageData(tmpCtx, imgData.data, width, height);
13301382
}
13311383
ctx.drawImage(tmpCanvas, 0, -height);
13321384
}
@@ -1341,7 +1393,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
13411393

13421394
var tmpCanvas = createScratchCanvas(w, h);
13431395
var tmpCtx = tmpCanvas.getContext('2d');
1344-
this.putBinaryImageData(tmpCtx, imgData);
1396+
putBinaryImageData(tmpCtx, imgData.data, w, h);
13451397

13461398
for (var i = 0, ii = map.length; i < ii; i++) {
13471399
var entry = map[i];
@@ -1354,25 +1406,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
13541406
}
13551407
},
13561408

1357-
putBinaryImageData: function CanvasGraphics_putBinaryImageData(ctx,
1358-
imgData) {
1359-
var w = imgData.width, h = imgData.height;
1360-
var tmpImgData = 'createImageData' in ctx ? ctx.createImageData(w, h) :
1361-
ctx.getImageData(0, 0, w, h);
1362-
1363-
var tmpImgDataPixels = tmpImgData.data;
1364-
var data = imgData.data;
1365-
if ('set' in tmpImgDataPixels)
1366-
tmpImgDataPixels.set(data);
1367-
else {
1368-
// Copy over the imageData pixel by pixel.
1369-
for (var i = 0, ii = tmpImgDataPixels.length; i < ii; i++)
1370-
tmpImgDataPixels[i] = data[i];
1371-
}
1372-
1373-
ctx.putImageData(tmpImgData, 0, 0);
1374-
},
1375-
13761409
// Marked content
13771410

13781411
markPoint: function CanvasGraphics_markPoint(tag) {

0 commit comments

Comments
 (0)