Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
587 lines
35 KiB
JavaScript
Executable file
587 lines
35 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
/* global ImageUtils */
|
|
|
|
require('/shared/js/image_utils.js');
|
|
|
|
suite('ImageUtils', function() {
|
|
|
|
const testImages = [
|
|
{
|
|
type: 'image/jpeg',
|
|
width: 25,
|
|
height: 24,
|
|
base64: '/9j/4AAQSkZJRgABAQEASABIAAD/4ge4SUNDX1BST0ZJTEUAAQEAAAeoYXBwbAIgAABtbnRyUkdCIFhZWiAH2QACABkACwAaAAthY3NwQVBQTAAAAABhcHBsAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtkZXNjAAABCAAAAG9kc2NtAAABeAAABWxjcHJ0AAAG5AAAADh3dHB0AAAHHAAAABRyWFlaAAAHMAAAABRnWFlaAAAHRAAAABRiWFlaAAAHWAAAABRyVFJDAAAHbAAAAA5jaGFkAAAHfAAAACxiVFJDAAAHbAAAAA5nVFJDAAAHbAAAAA5kZXNjAAAAAAAAABRHZW5lcmljIFJHQiBQcm9maWxlAAAAAAAAAAAAAAAUR2VuZXJpYyBSR0IgUHJvZmlsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbWx1YwAAAAAAAAAeAAAADHNrU0sAAAAoAAABeGhySFIAAAAoAAABoGNhRVMAAAAkAAAByHB0QlIAAAAmAAAB7HVrVUEAAAAqAAACEmZyRlUAAAAoAAACPHpoVFcAAAAWAAACZGl0SVQAAAAoAAACem5iTk8AAAAmAAAComtvS1IAAAAWAAACyGNzQ1oAAAAiAAAC3mhlSUwAAAAeAAADAGRlREUAAAAsAAADHmh1SFUAAAAoAAADSnN2U0UAAAAmAAAConpoQ04AAAAWAAADcmphSlAAAAAaAAADiHJvUk8AAAAkAAADomVsR1IAAAAiAAADxnB0UE8AAAAmAAAD6G5sTkwAAAAoAAAEDmVzRVMAAAAmAAAD6HRoVEgAAAAkAAAENnRyVFIAAAAiAAAEWmZpRkkAAAAoAAAEfHBsUEwAAAAsAAAEpHJ1UlUAAAAiAAAE0GFyRUcAAAAmAAAE8mVuVVMAAAAmAAAFGGRhREsAAAAuAAAFPgBWAWEAZQBvAGIAZQBjAG4A/QAgAFIARwBCACAAcAByAG8AZgBpAGwARwBlAG4AZQByAGkBDQBrAGkAIABSAEcAQgAgAHAAcgBvAGYAaQBsAFAAZQByAGYAaQBsACAAUgBHAEIAIABnAGUAbgDoAHIAaQBjAFAAZQByAGYAaQBsACAAUgBHAEIAIABHAGUAbgDpAHIAaQBjAG8EFwQwBDMEMAQ7BEwEPQQ4BDkAIAQ/BEAEPgREBDAEOQQ7ACAAUgBHAEIAUAByAG8AZgBpAGwAIABnAOkAbgDpAHIAaQBxAHUAZQAgAFIAVgBCkBp1KAAgAFIARwBCACCCcl9pY8+P8ABQAHIAbwBmAGkAbABvACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjAG8ARwBlAG4AZQByAGkAcwBrACAAUgBHAEIALQBwAHIAbwBmAGkAbMd8vBgAIABSAEcAQgAg1QS4XNMMx3wATwBiAGUAYwBuAP0AIABSAEcAQgAgAHAAcgBvAGYAaQBsBeQF6AXVBeQF2QXcACAAUgBHAEIAIAXbBdwF3AXZAEEAbABsAGcAZQBtAGUAaQBuAGUAcwAgAFIARwBCAC0AUAByAG8AZgBpAGwAwQBsAHQAYQBsAOEAbgBvAHMAIABSAEcAQgAgAHAAcgBvAGYAaQBsZm6QGgAgAFIARwBCACBjz4/wZYdO9k4AgiwAIABSAEcAQgAgMNcw7TDVMKEwpDDrAFAAcgBvAGYAaQBsACAAUgBHAEIAIABnAGUAbgBlAHIAaQBjA5MDtQO9A7kDugPMACADwAPBA78DxgOvA7sAIABSAEcAQgBQAGUAcgBmAGkAbAAgAFIARwBCACAAZwBlAG4A6QByAGkAYwBvAEEAbABnAGUAbQBlAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGUAbA5CDhsOIw5EDh8OJQ5MACAAUgBHAEIAIA4XDjEOSA4nDkQOGwBHAGUAbgBlAGwAIABSAEcAQgAgAFAAcgBvAGYAaQBsAGkAWQBsAGUAaQBuAGUAbgAgAFIARwBCAC0AcAByAG8AZgBpAGkAbABpAFUAbgBpAHcAZQByAHMAYQBsAG4AeQAgAHAAcgBvAGYAaQBsACAAUgBHAEIEHgQxBEkEOAQ5ACAEPwRABD4ERAQ4BDsETAAgAFIARwBCBkUGRAZBACAGKgY5BjEGSgZBACAAUgBHAEIAIAYnBkQGOQYnBkUARwBlAG4AZQByAGkAYwAgAFIARwBCACAAUAByAG8AZgBpAGwAZQBHAGUAbgBlAHIAZQBsACAAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABzAGV0ZXh0AAAAAENvcHlyaWdodCAyMDA3IEFwcGxlIEluYy4sIGFsbCByaWdodHMgcmVzZXJ2ZWQuAFhZWiAAAAAAAADzUgABAAAAARbPWFlaIAAAAAAAAHRNAAA97gAAA9BYWVogAAAAAAAAWnUAAKxzAAAXNFhZWiAAAAAAAAAoGgAAFZ8AALg2Y3VydgAAAAAAAAABAc0AAHNmMzIAAAAAAAEMQgAABd7///MmAAAHkgAA/ZH///ui///9owAAA9wAAMBs/+EAnkV4aWYAAE1NACoAAAAIAAYBEgADAAAAAQABAAABGgAFAAAAAQAAAFYBGwAFAAAAAQAAAF4BKAADAAAAAQACAAABMQACAAAAEQAAAGaHaQAEAAAAAQAAAHgAAAAAAAAASAAAAAEAAABIAAAAAUFkb2JlIEltYWdlUmVhZHkAAAACoAIABAAAAAEAAAAZoAMABAAAAAEAAAAYAAAAAP/bAEMAIBYYHBgUIBwaHCQiICYwUDQwLCwwYkZKOlB0Znp4cmZwboCQuJyAiK6KbnCg2qKuvsTO0M58muLy4MjwuMrOxv/bAEMBIiQkMCowXjQ0XsaEcITGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxv/AABEIABgAGQMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/ANu5m8iEyYzjoPWsuXU5YzuJHrjHFaF/C01qyp94cgetc5JGJDtOFJ4OQcikpcstdi/Z88dNzqIZBLGrjuM1JVWxD+SGcbc4CjvirVEW2tSWrMrOblXYqoYZOAfSmZuASxhRienHSiimIlJm38Zxnpip6KKAP//Z' // jshint ignore:line
|
|
},
|
|
{
|
|
type: 'image/png',
|
|
width: 25,
|
|
height: 24,
|
|
base64: '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' // jshint ignore:line
|
|
},
|
|
{
|
|
type: 'image/gif',
|
|
width: 25,
|
|
height: 24,
|
|
base64: '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' // jshint ignore:line
|
|
},
|
|
{
|
|
type: 'image/bmp',
|
|
width: 25,
|
|
height: 24,
|
|
base64: 'Qk1WBwAAAAAAADYAAAAoAAAAGQAAABgAAAABABgAAAAAACAHAAATCwAAEwsAAAAAAAAAAAAAAAAA////////////////////////////////////////////////////////////////////////////////////////////////AP///0c5OUBAQAAAAEY+PkBAQFUrK0BAQEU9PUQ8PEBAQEY6OkRAPEBAOkNDNkY+Pkc9Pf///0M/P0BAQEBAQERAPEY+Pv///wAAAAD///9DPT1DPj7///9CPjxEPj5DPz9CPT1CPzxDQD1EPz1CPj5DPz1CPT1CPjxDPjxHPT1EQDxEPj5DPj5CPz9AQEBDPj5EPj7///8A////Qj87Qj48QTw8Qz89Qj09QT09Qz49Qz09RD49Qz89Qj48Qj87Qz09Qz09Qz48////RD4+////Qz09Qj09RDw8Qj48QTs7////AP///0M8PEM+PUM9PUI8PEM/PUI+PEQ9PUE9PUM9PUM+PEM+PkM9PUI/PEI9PUM+PkNDQ0I9PUU7O0E8PEM+PkI+PkBAQP///wAAAAD///9CPT1DPjxDPj5CPT1HOTn///9AQED///9EPj5CPT9COkoAAP8AAP////////////9EPj5DPj5EPj5CPT1DPj5EPT1GRkb///8A////////Szw8QEBAQjk5QEBA////////////////SkoggIAA////////////////////////REQzVSsr////SUk3Q0ND////////AP///////////////////////wAAAAAAqgBAAAAAAAAAMgoAXBcNaBAKVg4AOwAAAP///0BAv////////0BAQP///////////////wD///8AAABVVVVVVVVVVVX///8AAGYAAAAMAD8XEHobGJAcH5sdJKQcJaMcJqMeIIoYAEgAAAAkJEmAgID///+AgICAgID///////8A////////////////////AAD/////Fw5xHBaRHyOsHyuyHzK4Hze9HzvCHz3EIznAHyyhFCKM////////AAAA////////////////AP///////////////wAAv////x0Xlx0bnCAosR8yth06wRxDyxxIzB1Lyx1X0x5a1B9JzSA6uiIxrP///wAAgP///////////////wD///////////8AAID///////8dIaIgKrYeNLkePsMgRMIqPJ8xLGkuKWMpP5Uha84gctgkWM4lNKAhC5D///8AAID///////////8A////////////MzOZ////HCiqHimrHjS7Hj7DH0TGID+1JSmEMwA9PAAAPAAAL0VjGaLcGpjdI2bFJBuy////QEC/////////////AP///////wAA/wAAgP///x8zsyA1wR09wR9CwxlPzCFUxS1TuhxnxDVmm0whIz0NAC+WuyHL8Ra37Rh84P///wCAv////////////wD///////8AgP////////8eOrsgPcshRcUYPr4tP7BeNVdmMCdeVG9eUmZWJyJIAAA7Z3o02/cq5P4plMn///////8AAP////////8A////////AID/////////HkPFIEPQIU3IHE/GKkmsZExihC4AhSsAeSUAajEjVgAAdnp5d+36cO//SpK2////////AP//////////AP///////wD//////////x5PzSFL1x9QyCRczAd42j5+x5JiX51RKJNKMII9JnQnAI1aUHrN14r6/0y33v///////wAAAP///////wD///////////8AQL////8dVdUeT84iU8smZM8jfdAlaMdeer6iaEuuXRGcVjKYRBWMQyOL1d1r/P8A6vz///8zzMz///////////8A////////////AEC/////IjOqIES+AFTaAGjZAIzheXSTrHBTu3w4w3oct28upEwAwJmNv///ZN7wANbr////Vaqq////////////AP///////////wCA/////1VVqg5Ev3JgoIxogGGDq3ycpNaPBNmRI9COLsF6HrNlIszHv7Td7Wedx////////////////////////wD///////////////////////8AY+/BlHXRhiLXiTDGnGjaoFbmoUfin0TfljDYo3HOx7mfpsL///////8AAP////////////////8A/////////////////////////////////9eu5KVb56BC5aRT5ahb46VW36BV57qG/+Nx////////AAD/////////////////////AP///////////////////////wCA/7+AgP//////////0fO/eu22bfO7dv//tv///////8zMzP///////////////////////////wD/////////////////////////////////qlWqVQD///////////////////+/v4D/qqr///////////////////////////////8A' // jshint ignore:line
|
|
}
|
|
];
|
|
|
|
suite('MIME type constants', function() {
|
|
test('JPEG', function() { assert.equal(ImageUtils.JPEG, 'image/jpeg'); });
|
|
test('PNG', function() { assert.equal(ImageUtils.PNG, 'image/png'); });
|
|
test('GIF', function() { assert.equal(ImageUtils.GIF, 'image/gif'); });
|
|
test('BMP', function() { assert.equal(ImageUtils.BMP, 'image/bmp'); });
|
|
});
|
|
|
|
|
|
suite('getSizeAndType', function() {
|
|
|
|
function makeBlobFromString(binaryString, type) {
|
|
var buffer = new ArrayBuffer(binaryString.length);
|
|
var bytes = new Uint8Array(buffer);
|
|
for (var i = 0; i < binaryString.length; i++) {
|
|
bytes[i] = binaryString.charCodeAt(i);
|
|
}
|
|
return new Blob([buffer], { type: type });
|
|
}
|
|
|
|
suiteSetup(function() {
|
|
// Create blobs for each of the test images
|
|
testImages.forEach(function(image) {
|
|
image.blob = makeBlobFromString(atob(image.base64), image.type);
|
|
});
|
|
});
|
|
|
|
test('rejects string argument', function(done) {
|
|
ImageUtils.getSizeAndType('data:foo').then(null, function() { done(); });
|
|
});
|
|
|
|
test('rejects numeric argument', function(done) {
|
|
ImageUtils.getSizeAndType(1).then(null, function() { done(); });
|
|
});
|
|
|
|
test('rejects object argument', function(done) {
|
|
ImageUtils.getSizeAndType({size:100}).then(null, function() { done(); });
|
|
});
|
|
|
|
// Test that we can get the width and height of each test image
|
|
testImages.forEach(function(image) {
|
|
test(image.type, function(done) {
|
|
ImageUtils.getSizeAndType(image.blob).then(function resolve(data) {
|
|
try {
|
|
assert.equal(data.type, image.type);
|
|
assert.equal(data.width, image.width);
|
|
assert.equal(data.height, image.height);
|
|
done();
|
|
}
|
|
catch (e) {
|
|
done(e);
|
|
}
|
|
}, function reject(e) { done(new Error(e)); });
|
|
});
|
|
});
|
|
|
|
// Test that we can get the size of a JPEG file even if it has a large
|
|
// EXIF metadata segment before the segments that hold image size.
|
|
test('jpeg with large EXIF segments', function(done) {
|
|
// Splice some fake EXIF data in at the start of the JPEG file
|
|
// to force the image size data to be beyond the 32kb boundary
|
|
var jpeg = testImages[0];
|
|
var exif = new ArrayBuffer(40 * 1024);
|
|
var view = new DataView(exif);
|
|
view.setUint8(0, 0xFF);
|
|
view.setUint8(1, 0xE0); // APP0 segment for EXIF
|
|
view.setUint16(2, (40 * 1024) - 2);
|
|
var newblob = new Blob([jpeg.blob.slice(0, 2),
|
|
exif, exif, jpeg.blob.slice(2)],
|
|
{ type: 'image/jpeg' });
|
|
ImageUtils.getSizeAndType(newblob).then(
|
|
function resolve(data) {
|
|
try {
|
|
assert.equal(data.type, jpeg.type);
|
|
assert.equal(data.width, jpeg.width);
|
|
assert.equal(data.height, jpeg.height);
|
|
done();
|
|
}
|
|
catch (e) {
|
|
done(e);
|
|
}
|
|
},
|
|
function reject(e) {
|
|
done(new Error(e));
|
|
}
|
|
);
|
|
});
|
|
|
|
test('reject zero-length blob', function(done) {
|
|
var empty = new Blob([''], { type: 'image/jpeg' });
|
|
ImageUtils.getSizeAndType(empty).then(
|
|
function resolve() {
|
|
done(new Error('failed to reject an empty blob'));
|
|
},
|
|
function reject() {
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
test('reject any blob < 16 bytes', function(done) {
|
|
var empty = new Blob(['0123456789012345'], { type: 'image/jpeg' });
|
|
ImageUtils.getSizeAndType(empty).then(
|
|
function resolve() {
|
|
done(new Error('failed to reject a short blob'));
|
|
},
|
|
function reject() {
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
|
|
// Test that if we truncate the test images we can't get their size
|
|
testImages.forEach(function(image) {
|
|
// We can't meaningfully test truncated gif files
|
|
if (image.type === 'image/gif') {
|
|
return;
|
|
}
|
|
|
|
test('reject truncated ' + image.type, function(done) {
|
|
var truncated = image.blob.slice(0, 20);
|
|
ImageUtils.getSizeAndType(truncated).then(
|
|
function resolve(data) {
|
|
done(new Error('failed to reject a truncated blob'));
|
|
},
|
|
function reject() {
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
// Here are some files that are not image files and should be rejected
|
|
var bogusFiles = [
|
|
'This is a text file not an image file. It does not have a size',
|
|
'GIF85a This is not a gif: the header is not correct',
|
|
'\xff\xd8 Not a jpeg file because it does not have segments',
|
|
'BM not really a BMP file since. File length data is not correct',
|
|
'\x89PNG\r\n\x1a\n Not a PNG file: does not have an ihdr segment'
|
|
];
|
|
|
|
bogusFiles.forEach(function(file, index) {
|
|
test('Reject bogus file ' + index, function(done) {
|
|
var bogus = makeBlobFromString(file, 'text/plain');
|
|
ImageUtils.getSizeAndType(bogus).then(
|
|
function resolve(data) {
|
|
done(new Error('failed to reject bogus text:' + file));
|
|
},
|
|
function reject() {
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
suite('Downsample tests', function() {
|
|
test('Check API', function() {
|
|
assert.typeOf(ImageUtils.Downsample, 'object');
|
|
assert.typeOf(ImageUtils.Downsample.sizeAtLeast, 'function');
|
|
assert.typeOf(ImageUtils.Downsample.sizeNoMoreThan, 'function');
|
|
assert.typeOf(ImageUtils.Downsample.areaAtLeast, 'function');
|
|
assert.typeOf(ImageUtils.Downsample.areaNoMoreThan, 'function');
|
|
assert.typeOf(ImageUtils.Downsample.MAX_SIZE_REDUCTION, 'number');
|
|
assert.typeOf(ImageUtils.Downsample.MAX_AREA_REDUCTION, 'number');
|
|
assert.typeOf(ImageUtils.Downsample.NONE, 'object');
|
|
assert.equal(ImageUtils.Downsample.NONE.dimensionScale, 1);
|
|
assert.equal(ImageUtils.Downsample.NONE.areaScale, 1);
|
|
assert.equal(ImageUtils.Downsample.NONE.toString(), '');
|
|
});
|
|
|
|
var scales = [
|
|
10, 2, 1,
|
|
1 / 8, 2 / 8, 3 / 8, 4 / 8, 5 / 8, 6 / 8, 7 / 8,
|
|
0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1,
|
|
0.09, 0.08, 0.07, 0.06, 0.05, 0.04, 0.03, 0.02, 0.01
|
|
];
|
|
|
|
// This is the same function we use in downsample.js
|
|
function round(x) {
|
|
return Math.round(x * 100) / 100;
|
|
}
|
|
|
|
test('sizeAtLeast', function() {
|
|
var min = 1 / ImageUtils.Downsample.MAX_SIZE_REDUCTION;
|
|
scales.forEach(function(scale) {
|
|
var ds = ImageUtils.Downsample.sizeAtLeast(scale);
|
|
assert.operator(ds.dimensionScale, '<=', round(Math.max(scale, min)));
|
|
assert.equal(ds.scale(100), ds.dimensionScale * 100);
|
|
assert.ok(ds === ImageUtils.Downsample.NONE ||
|
|
ds.toString()[0] === '#');
|
|
});
|
|
});
|
|
|
|
test('areaAtLeast', function() {
|
|
var min = 1 / ImageUtils.Downsample.MAX_AREA_REDUCTION;
|
|
scales.forEach(function(scale) {
|
|
var ds = ImageUtils.Downsample.areaAtLeast(scale);
|
|
assert.operator(ds.areaScale, '<=', round(Math.max(scale, min)));
|
|
|
|
ds = ImageUtils.Downsample.areaAtLeast(scale * scale);
|
|
assert.operator(ds.areaScale, '<=',
|
|
round(Math.max(scale * scale, min)));
|
|
assert.equal(ds.scale(100), ds.dimensionScale * 100);
|
|
assert.ok(ds === ImageUtils.Downsample.NONE ||
|
|
ds.toString()[0] === '#');
|
|
});
|
|
});
|
|
|
|
test('sizeNoMoreThan', function() {
|
|
scales.forEach(function(scale) {
|
|
var ds = ImageUtils.Downsample.sizeNoMoreThan(scale);
|
|
assert.operator(ds.dimensionScale, '>=', round(Math.min(scale, 1)));
|
|
assert.equal(ds.scale(100), ds.dimensionScale * 100);
|
|
assert.ok(ds === ImageUtils.Downsample.NONE ||
|
|
ds.toString()[0] === '#');
|
|
});
|
|
});
|
|
|
|
test('areaNoMoreThan', function() {
|
|
scales.forEach(function(scale) {
|
|
var ds = ImageUtils.Downsample.areaNoMoreThan(scale);
|
|
assert.operator(ds.areaScale, '>=', round(Math.min(scale, 1)));
|
|
|
|
ds = ImageUtils.Downsample.areaNoMoreThan(scale * scale);
|
|
assert.operator(ds.areaScale, '>=', round(Math.min(scale * scale, 1)));
|
|
assert.equal(ds.scale(100), ds.dimensionScale * 100);
|
|
assert.ok(ds === ImageUtils.Downsample.NONE ||
|
|
ds.toString()[0] === '#');
|
|
});
|
|
});
|
|
|
|
test('max reduction amounts', function() {
|
|
var ds = ImageUtils.Downsample.sizeNoMoreThan(0);
|
|
assert.equal(ds.dimensionScale,
|
|
round(1 / ImageUtils.Downsample.MAX_SIZE_REDUCTION));
|
|
assert.equal(ds.areaScale,
|
|
round(1 / ImageUtils.Downsample.MAX_AREA_REDUCTION));
|
|
});
|
|
});
|
|
|
|
|
|
function runResizeAndCropToCoverTests(imageWidth, imageHeight, imageType,
|
|
encoderOptions) {
|
|
var suitename = 'resizeAndCropToCover ' + imageType + ' ' +
|
|
imageWidth + 'x' + imageHeight;
|
|
suite(suitename, function() {
|
|
const W = imageWidth, H = imageHeight; // The size of the test image
|
|
const inputImageType = imageType;
|
|
const encoderOptions = encoderOptions;
|
|
|
|
suiteSetup(function(done) {
|
|
// We begin by creating a special image where each pixel value
|
|
// encodes the coordinates of that pixel. This allows us to inspect
|
|
// the pixels in the output image to verify that cropping
|
|
// was done correctly.
|
|
var canvas = document.createElement('canvas');
|
|
canvas.width = W;
|
|
canvas.height = H;
|
|
var context = canvas.getContext('2d');
|
|
var imageData = context.createImageData(W, H);
|
|
var pixels = imageData.data;
|
|
|
|
for (var x = 0; x < canvas.width; x++) {
|
|
for (var y = 0; y < canvas.height; y++) {
|
|
var offset = (x + y * canvas.width) * 4;
|
|
pixels[offset] = x; // Encode x coordinate as red value
|
|
pixels[offset + 1] = y; // Encode y coodrdinate as green value
|
|
imageData.data[offset + 3] = 255; // Make it opaque
|
|
}
|
|
}
|
|
|
|
context.putImageData(imageData, 0, 0);
|
|
|
|
// Create an encoded version of the input image
|
|
var self = this;
|
|
canvas.toBlob(function(blob) {
|
|
self.inputBlob = blob;
|
|
done();
|
|
}, inputImageType);
|
|
});
|
|
|
|
// Decode the image blob and verify that its size is as expected and
|
|
// that its upper-left pixel has red and green values near r0 and g0
|
|
// and that its lower-right pixel has values near r1 and g1. We do a
|
|
// fuzzy match because jpeg is a lossy format and because sometimes
|
|
// we're working with downsampled or upscaled images.
|
|
function verifyImage(blob, expectedWidth, expectedHeight,
|
|
r0, g0, r1, g1, tolerance, callback) {
|
|
var image = new Image();
|
|
var url = URL.createObjectURL(blob);
|
|
image.src = url;
|
|
image.onerror = function() {
|
|
callback(Error('failed to load image from blob'));
|
|
};
|
|
|
|
image.onload = function() {
|
|
try {
|
|
assert.equal(image.width, expectedWidth, 'image width');
|
|
assert.equal(image.height, expectedHeight, 'image height');
|
|
|
|
var canvas = document.createElement('canvas');
|
|
canvas.width = image.width;
|
|
canvas.height = image.height;
|
|
var context = canvas.getContext('2d');
|
|
context.drawImage(image, 0, 0);
|
|
var imageData = context.getImageData(0, 0,
|
|
canvas.width, canvas.height);
|
|
var pixels = imageData.data;
|
|
|
|
isNear(pixels[0], r0, 'red component of upper-left');
|
|
isNear(pixels[1], g0, 'green component of upper-left');
|
|
|
|
var offset = 4 * (image.width * image.height - 1);
|
|
isNear(pixels[offset], r1,
|
|
'red component of lower right');
|
|
isNear(pixels[offset + 1], g1,
|
|
'green component of lower right');
|
|
|
|
image.src = '';
|
|
URL.revokeObjectURL(url);
|
|
canvas.width = 0;
|
|
callback();
|
|
}
|
|
catch (e) {
|
|
callback(e);
|
|
}
|
|
};
|
|
|
|
// Return true if x is close enough to the target
|
|
function isNear(x, target, message) {
|
|
if (target - tolerance <= x && x <= target + tolerance) {
|
|
return;
|
|
}
|
|
throw Error(message + ' ' + x + ' is not near ' + target);
|
|
}
|
|
}
|
|
|
|
test('test images created', function(done) {
|
|
assert.instanceOf(this.inputBlob, Blob, 'got Blob');
|
|
assert.equal(this.inputBlob.type, inputImageType);
|
|
verifyImage(this.inputBlob, W, H, 0, 0, W - 1, H - 1, 4, done);
|
|
});
|
|
|
|
function onResolve(value) {
|
|
throw Error('Must be rejected');
|
|
}
|
|
|
|
test('throw for invalid input image', function(done) {
|
|
|
|
function checkNotBlob(promise) {
|
|
return promise.then(
|
|
onResolve,
|
|
(err) => { assert.equal(err.name, 'TypeError'); }
|
|
);
|
|
}
|
|
|
|
Promise.all([
|
|
checkNotBlob(ImageUtils.resizeAndCropToCover(null, 1, 10)),
|
|
checkNotBlob(ImageUtils.resizeAndCropToCover('not a blob', 1, 10))
|
|
]).then(() => done(), done);
|
|
|
|
});
|
|
|
|
test('throw for invalid input dimension', function(done) {
|
|
|
|
function ensureReject(promise) {
|
|
return promise.then(
|
|
onResolve,
|
|
(err) => { assert.equal(err.message, 'invalid output dimensions'); }
|
|
);
|
|
}
|
|
|
|
Promise.all([
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob, 0, 10)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob, 10, 0)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob,
|
|
-10, 10)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob,
|
|
10, -10)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob,
|
|
Infinity, 10)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob,
|
|
10, Infinity)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob, {}, 10)),
|
|
ensureReject(ImageUtils.resizeAndCropToCover(this.inputBlob, 10, {}))
|
|
]).then(() => done(), done);
|
|
});
|
|
|
|
// Verify that if the output size is the same as the input size then
|
|
// the output blob is the same as the input blob
|
|
test('input returned if no resize needed', function(done) {
|
|
var self = this;
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W, H).then(
|
|
function resolve(outputBlob) {
|
|
try {
|
|
assert.equal(self.inputBlob, outputBlob);
|
|
done();
|
|
}
|
|
catch (ex) {
|
|
done(ex);
|
|
}
|
|
});
|
|
});
|
|
|
|
test('unspecified output type', function(done) {
|
|
var self = this;
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, 10, 10)
|
|
.then(function resolve(outputBlob) {
|
|
assert.equal(outputBlob.type, self.inputBlob.type);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('jpeg output type with encondig options', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, 10, 10, ImageUtils.JPEG,
|
|
encoderOptions)
|
|
.then(function resolve(outputBlob) {
|
|
assert.equal(outputBlob.type, ImageUtils.JPEG);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('png output type', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, 10, 10, ImageUtils.PNG)
|
|
.then(function resolve(outputBlob) {
|
|
assert.equal(outputBlob.type, ImageUtils.PNG);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('shrink no crop', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W / 3, H / 3)
|
|
.then(function resolve(outputBlob) {
|
|
verifyImage(outputBlob, Math.round(W / 3), Math.round(H / 3),
|
|
0, 0, W, H, 4, done);
|
|
});
|
|
});
|
|
|
|
test('enlarge no crop', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W * 3, H * 3)
|
|
.then(function resolve(outputBlob) {
|
|
verifyImage(outputBlob, W * 3, H * 3, 0, 0, W, H, 4, done);
|
|
});
|
|
});
|
|
|
|
var delta = 100;
|
|
|
|
test('reduce height', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W, H - delta)
|
|
.then(function resolve(outputBlob) {
|
|
verifyImage(outputBlob, W, H - delta,
|
|
0, delta / 2, W, H - delta / 2, 4, done);
|
|
});
|
|
});
|
|
|
|
test('reduce width', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W - delta, H)
|
|
.then(function resolve(outputBlob) {
|
|
verifyImage(outputBlob, W - delta, H,
|
|
delta / 2, 0, W - delta / 2, H, 4, done);
|
|
});
|
|
});
|
|
|
|
test('enlarge height', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W, H + delta)
|
|
.then(function resolve(outputBlob) {
|
|
var ratio = (H + delta) / H;
|
|
var margin = ((ratio - 1) * W) / (2 * ratio);
|
|
verifyImage(outputBlob, W, H + delta,
|
|
margin, 0, W - margin, H,
|
|
4, done);
|
|
});
|
|
});
|
|
|
|
test('enlarge width', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W + delta, H)
|
|
.then(function resolve(outputBlob) {
|
|
var ratio = (W + delta) / W;
|
|
var margin = ((ratio - 1) * H) / (2 * ratio);
|
|
verifyImage(outputBlob, W + delta, H,
|
|
0, margin, W, H - margin,
|
|
4, done);
|
|
});
|
|
});
|
|
|
|
suite('#-moz-samplesize', function() {
|
|
var spy;
|
|
setup(function() {
|
|
spy = this.sinon.spy(ImageUtils.Downsample, 'sizeNoMoreThan');
|
|
});
|
|
|
|
function checkResolve(promise, checkValue) {
|
|
return promise.then(
|
|
function resolve(outputBlob) {
|
|
sinon.assert.calledOnce(spy);
|
|
assert.equal(spy.returnValues[0].toString(), checkValue);
|
|
},
|
|
function reject(outputBlob) {
|
|
throw Error('Must be resolved');
|
|
}
|
|
);
|
|
}
|
|
|
|
if (inputImageType === 'image/jpeg') {
|
|
test('uses it at half size', function(done) {
|
|
checkResolve(ImageUtils.resizeAndCropToCover(
|
|
this.inputBlob, W / 2, H / 2),
|
|
'#-moz-samplesize=2').then(done,done);
|
|
});
|
|
|
|
test('uses it at third size', function(done) {
|
|
checkResolve(ImageUtils.resizeAndCropToCover(
|
|
this.inputBlob, W / 3, H / 3),
|
|
'#-moz-samplesize=3').then(done,done);
|
|
});
|
|
test('uses it at fifth size', function(done) {
|
|
checkResolve(ImageUtils.resizeAndCropToCover(
|
|
this.inputBlob, W / 5, H / 5),
|
|
'#-moz-samplesize=4').then(done,done);
|
|
});
|
|
|
|
test('does not use at three quarters size', function(done) {
|
|
checkResolve(ImageUtils.resizeAndCropToCover(
|
|
this.inputBlob, W * 0.75, H * 0.75),
|
|
'').then(done,done);
|
|
});
|
|
}
|
|
else {
|
|
test('does not use for non-jpeg', function(done) {
|
|
ImageUtils.resizeAndCropToCover(this.inputBlob, W / 2, H / 2)
|
|
.then(() => sinon.assert.notCalled(spy)).then(done, done);
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
// We run the resizeAndCropToCover test suite six times for for three
|
|
// different input image sizes and two different input image types
|
|
([
|
|
[240, 180], // 4x3
|
|
[180, 240], // 3x4
|
|
[250, 250] // square
|
|
]).forEach(function(size) {
|
|
runResizeAndCropToCoverTests(size[0], size[1], 'image/jpeg', 0.95);
|
|
runResizeAndCropToCoverTests(size[0], size[1], 'image/png');
|
|
});
|
|
});
|