import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
214
image/test/unit/async_load_tests.js
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Test to ensure that image loading/decoding notifications are always
|
||||
* delivered async, and in the order we expect.
|
||||
*
|
||||
* Must be included from a file that has a uri of the image to test defined in
|
||||
* var uri.
|
||||
*/
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
var Cr = Components.results;
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
var server = new HttpServer();
|
||||
server.registerDirectory("/", do_get_file(''));
|
||||
server.registerContentType("sjs", "sjs");
|
||||
server.start(-1);
|
||||
|
||||
|
||||
load('image_load_helpers.js');
|
||||
|
||||
var requests = [];
|
||||
|
||||
// Return a closure that holds on to the listener from the original
|
||||
// imgIRequest, and compares its results to the cloned one.
|
||||
function getCloneStopCallback(original_listener)
|
||||
{
|
||||
return function cloneStop(listener) {
|
||||
do_check_eq(original_listener.state, listener.state);
|
||||
|
||||
// Sanity check to make sure we didn't accidentally use the same listener
|
||||
// twice.
|
||||
do_check_neq(original_listener, listener);
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure that cloned requests get all the same callbacks as the original,
|
||||
// but they aren't synchronous right now.
|
||||
function checkClone(other_listener, aRequest)
|
||||
{
|
||||
do_test_pending();
|
||||
|
||||
// For as long as clone notification is synchronous, we can't test the clone state reliably.
|
||||
var listener = new ImageListener(null, function(foo, bar) { do_test_finished(); } /*getCloneStopCallback(other_listener)*/);
|
||||
listener.synchronous = false;
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var clone = aRequest.clone(outer);
|
||||
requests.push(clone);
|
||||
}
|
||||
|
||||
// Ensure that all the callbacks were called on aRequest.
|
||||
function checkSizeAndLoad(listener, aRequest)
|
||||
{
|
||||
do_check_neq(listener.state & SIZE_AVAILABLE, 0);
|
||||
do_check_neq(listener.state & LOAD_COMPLETE, 0);
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
function secondLoadDone(oldlistener, aRequest)
|
||||
{
|
||||
do_test_pending();
|
||||
|
||||
try {
|
||||
var staticrequest = aRequest.getStaticRequest();
|
||||
|
||||
// For as long as clone notification is synchronous, we can't test the
|
||||
// clone state reliably.
|
||||
var listener = new ImageListener(null, checkSizeAndLoad);
|
||||
listener.synchronous = false;
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var staticrequestclone = staticrequest.clone(outer);
|
||||
requests.push(staticrequestclone);
|
||||
} catch(e) {
|
||||
// We can't create a static request. Most likely the request we started
|
||||
// with didn't load successfully.
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
run_loadImageWithChannel_tests();
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
// Load the request a second time. This should come from the image cache, and
|
||||
// therefore would be at most risk of being served synchronously.
|
||||
function checkSecondLoad()
|
||||
{
|
||||
do_test_pending();
|
||||
|
||||
var listener = new ImageListener(checkClone, secondLoadDone);
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null));
|
||||
listener.synchronous = false;
|
||||
}
|
||||
|
||||
function firstLoadDone(oldlistener, aRequest)
|
||||
{
|
||||
checkSecondLoad(uri);
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
// Return a closure that allows us to check the stream listener's status when the
|
||||
// image finishes loading.
|
||||
function getChannelLoadImageStopCallback(streamlistener, next)
|
||||
{
|
||||
return function channelLoadStop(imglistener, aRequest) {
|
||||
|
||||
next();
|
||||
|
||||
do_test_finished();
|
||||
}
|
||||
}
|
||||
|
||||
// Load the request a second time. This should come from the image cache, and
|
||||
// therefore would be at most risk of being served synchronously.
|
||||
function checkSecondChannelLoad()
|
||||
{
|
||||
do_test_pending();
|
||||
var channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true});
|
||||
var channellistener = new ChannelListener();
|
||||
channel.asyncOpen2(channellistener);
|
||||
|
||||
var listener = new ImageListener(null,
|
||||
getChannelLoadImageStopCallback(channellistener,
|
||||
all_done_callback));
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var outlistener = {};
|
||||
requests.push(gCurrentLoader.loadImageWithChannelXPCOM(channel, outer, null, outlistener));
|
||||
channellistener.outputListener = outlistener.value;
|
||||
|
||||
listener.synchronous = false;
|
||||
}
|
||||
|
||||
function run_loadImageWithChannel_tests()
|
||||
{
|
||||
// To ensure we're testing what we expect to, create a new loader and cache.
|
||||
gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
|
||||
|
||||
do_test_pending();
|
||||
var channel = NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true});
|
||||
var channellistener = new ChannelListener();
|
||||
channel.asyncOpen2(channellistener);
|
||||
|
||||
var listener = new ImageListener(null,
|
||||
getChannelLoadImageStopCallback(channellistener,
|
||||
checkSecondChannelLoad));
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var outlistener = {};
|
||||
requests.push(gCurrentLoader.loadImageWithChannelXPCOM(channel, outer, null, outlistener));
|
||||
channellistener.outputListener = outlistener.value;
|
||||
|
||||
listener.synchronous = false;
|
||||
}
|
||||
|
||||
function all_done_callback()
|
||||
{
|
||||
server.stop(function() { do_test_finished(); });
|
||||
}
|
||||
|
||||
function startImageCallback(otherCb)
|
||||
{
|
||||
return function(listener, request)
|
||||
{
|
||||
// Make sure we can load the same image immediately out of the cache.
|
||||
do_test_pending();
|
||||
var listener2 = new ImageListener(null, function(foo, bar) { do_test_finished(); });
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener2);
|
||||
requests.push(gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null));
|
||||
listener2.synchronous = false;
|
||||
|
||||
// Now that we've started another load, chain to the callback.
|
||||
otherCb(listener, request);
|
||||
}
|
||||
}
|
||||
|
||||
var gCurrentLoader;
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
for (var i = 0; i < requests.length; ++i) {
|
||||
requests[i].cancelAndForgetObserver(0);
|
||||
}
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
gCurrentLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
|
||||
|
||||
do_test_pending();
|
||||
var listener = new ImageListener(startImageCallback(checkClone), firstLoadDone);
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var req = gCurrentLoader.loadImageXPCOM(uri, null, null, "default", null, null, outer, null, 0, null);
|
||||
requests.push(req);
|
||||
|
||||
// Ensure that we don't cause any mayhem when we lock an image.
|
||||
req.lockImage();
|
||||
|
||||
listener.synchronous = false;
|
||||
}
|
||||
BIN
image/test/unit/bug413512.ico
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
image/test/unit/bug815359.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
image/test/unit/image1.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
image/test/unit/image1png16x16.jpg
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
image/test/unit/image1png64x64.jpg
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
image/test/unit/image2.jpg
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
image/test/unit/image2jpg16x16-win.png
Normal file
|
After Width: | Height: | Size: 948 B |
BIN
image/test/unit/image2jpg16x16.png
Normal file
|
After Width: | Height: | Size: 950 B |
BIN
image/test/unit/image2jpg16x16cropped.jpg
Normal file
|
After Width: | Height: | Size: 879 B |
BIN
image/test/unit/image2jpg16x16cropped2.jpg
Normal file
|
After Width: | Height: | Size: 878 B |
BIN
image/test/unit/image2jpg16x32cropped3.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
image/test/unit/image2jpg16x32scaled.jpg
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
image/test/unit/image2jpg32x16cropped4.jpg
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
image/test/unit/image2jpg32x16scaled.jpg
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
image/test/unit/image2jpg32x32-win.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
image/test/unit/image2jpg32x32.jpg
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
image/test/unit/image2jpg32x32.png
Normal file
|
After Width: | Height: | Size: 3 KiB |
BIN
image/test/unit/image3.ico
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
image/test/unit/image3ico16x16.png
Normal file
|
After Width: | Height: | Size: 330 B |
BIN
image/test/unit/image3ico32x32.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
image/test/unit/image4.gif
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
image/test/unit/image4gif16x16bmp24bpp.ico
Normal file
|
After Width: | Height: | Size: 894 B |
BIN
image/test/unit/image4gif16x16bmp32bpp.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
image/test/unit/image4gif32x32bmp24bpp.ico
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
image/test/unit/image4gif32x32bmp32bpp.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
122
image/test/unit/image_load_helpers.js
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Helper structures to track callbacks from image and channel loads.
|
||||
*/
|
||||
|
||||
// START_REQUEST and STOP_REQUEST are used by ChannelListener, and
|
||||
// stored in ChannelListener.requestStatus.
|
||||
const START_REQUEST = 0x01;
|
||||
const STOP_REQUEST = 0x02;
|
||||
const DATA_AVAILABLE = 0x04;
|
||||
|
||||
// One bit per callback that imageListener below implements. Stored in
|
||||
// ImageListener.state.
|
||||
const SIZE_AVAILABLE = 0x01;
|
||||
const FRAME_UPDATE = 0x02;
|
||||
const FRAME_COMPLETE = 0x04;
|
||||
const LOAD_COMPLETE = 0x08;
|
||||
const DECODE_COMPLETE = 0x10;
|
||||
|
||||
// Safebrowsing requires that the profile dir is set.
|
||||
do_get_profile();
|
||||
|
||||
// An implementation of imgIScriptedNotificationObserver with the ability to
|
||||
// call specified functions on onStartRequest and onStopRequest.
|
||||
function ImageListener(start_callback, stop_callback)
|
||||
{
|
||||
this.sizeAvailable = function onSizeAvailable(aRequest)
|
||||
{
|
||||
do_check_false(this.synchronous);
|
||||
|
||||
this.state |= SIZE_AVAILABLE;
|
||||
|
||||
if (this.start_callback)
|
||||
this.start_callback(this, aRequest);
|
||||
}
|
||||
this.frameComplete = function onFrameComplete(aRequest)
|
||||
{
|
||||
do_check_false(this.synchronous);
|
||||
|
||||
this.state |= FRAME_COMPLETE;
|
||||
}
|
||||
this.decodeComplete = function onDecodeComplete(aRequest)
|
||||
{
|
||||
do_check_false(this.synchronous);
|
||||
|
||||
this.state |= DECODE_COMPLETE;
|
||||
}
|
||||
this.loadComplete = function onLoadcomplete(aRequest)
|
||||
{
|
||||
do_check_false(this.synchronous);
|
||||
|
||||
this.state |= LOAD_COMPLETE;
|
||||
|
||||
if (this.stop_callback)
|
||||
this.stop_callback(this, aRequest);
|
||||
}
|
||||
this.frameUpdate = function onFrameUpdate(aRequest)
|
||||
{
|
||||
}
|
||||
this.isAnimated = function onIsAnimated()
|
||||
{
|
||||
}
|
||||
|
||||
// Initialize the synchronous flag to true to start. This must be set to
|
||||
// false before exiting to the event loop!
|
||||
this.synchronous = true;
|
||||
|
||||
// A function to call when onStartRequest is called.
|
||||
this.start_callback = start_callback;
|
||||
|
||||
// A function to call when onStopRequest is called.
|
||||
this.stop_callback = stop_callback;
|
||||
|
||||
// The image load/decode state.
|
||||
// A bitfield that tracks which callbacks have been called. Takes the bits
|
||||
// defined above.
|
||||
this.state = 0;
|
||||
}
|
||||
|
||||
function NS_FAILED(val)
|
||||
{
|
||||
return !!(val & 0x80000000);
|
||||
}
|
||||
|
||||
function ChannelListener()
|
||||
{
|
||||
this.onStartRequest = function onStartRequest(aRequest, aContext)
|
||||
{
|
||||
if (this.outputListener)
|
||||
this.outputListener.onStartRequest(aRequest, aContext);
|
||||
|
||||
this.requestStatus |= START_REQUEST;
|
||||
}
|
||||
|
||||
this.onDataAvailable = function onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount)
|
||||
{
|
||||
if (this.outputListener)
|
||||
this.outputListener.onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
|
||||
|
||||
this.requestStatus |= DATA_AVAILABLE;
|
||||
}
|
||||
|
||||
this.onStopRequest = function onStopRequest(aRequest, aContext, aStatusCode)
|
||||
{
|
||||
if (this.outputListener)
|
||||
this.outputListener.onStopRequest(aRequest, aContext, aStatusCode);
|
||||
|
||||
// If we failed (or were canceled - failure is implied if canceled),
|
||||
// there's no use tracking our state, since it's meaningless.
|
||||
if (NS_FAILED(aStatusCode))
|
||||
this.requestStatus = 0;
|
||||
else
|
||||
this.requestStatus |= STOP_REQUEST;
|
||||
}
|
||||
|
||||
// A listener to pass the notifications we get to.
|
||||
this.outputListener = null;
|
||||
|
||||
// The request's status. A bitfield that holds one or both of START_REQUEST
|
||||
// and STOP_REQUEST, according to which callbacks have been called on the
|
||||
// associated request.
|
||||
this.requestStatus = 0;
|
||||
}
|
||||
10
image/test/unit/test_async_notification.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* Test for asynchronous image load/decode notifications in the case that the image load works.
|
||||
*/
|
||||
|
||||
// A simple 3x3 png; rows go red, green, blue. Stolen from the PNG encoder test.
|
||||
var pngspec = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
|
||||
var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
||||
var uri = ioService.newURI(pngspec, null, null);
|
||||
|
||||
load('async_load_tests.js');
|
||||
16
image/test/unit/test_async_notification_404.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Test to ensure that load/decode notifications are delivered completely and
|
||||
* asynchronously when dealing with a file that's a 404.
|
||||
*/
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "uri", function() {
|
||||
return ioService.newURI("http://localhost:" +
|
||||
server.identity.primaryPort +
|
||||
"/async-notification-never-here.jpg", null, null);
|
||||
});
|
||||
|
||||
load('async_load_tests.js');
|
||||
14
image/test/unit/test_async_notification_animated.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Test for asynchronous image load/decode notifications in the case that the
|
||||
* image load works, but for an animated image.
|
||||
*
|
||||
* If this fails because a request wasn't cancelled, it's possible that
|
||||
* imgContainer::ExtractFrame didn't set the new image's status correctly.
|
||||
*/
|
||||
|
||||
// transparent-animation.gif from the gif reftests.
|
||||
var spec = "data:image/gif;base64,R0lGODlhZABkAIABAP8AAP///yH5BAkBAAEALAAAAABLAGQAAAK8jI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8YhMKpchgPMJjUqnVOipis1ir9qul+sNV8HistVkTj/JajG7/UXDy+95tm4fy/NdPF/q93dWIqgVWAhwWKgoyPjnyAeZJ2lHOWcJh9mmqcaZ5mkGSreHOCXqRloadRrGGkeoapoa6+TaN0tra4gbq3vHq+q7BVwqrMeEnKy8zNzs/AwdLT1NXW19jZ1tUgAAIfkECQEAAQAsAAAAADQAZAAAArCMj6nL7Q+jnLTai7PevPsPhuJIluaJpurKtu4Lx/JM1/aN5/rO9/7vAAiHxKLxiCRCkswmc+mMSqHSapJqzSof2u4Q67WCw1MuOTs+N9Pqq7kdZcON8vk2aF+/88g6358HaCc4Rwhn2IaopnjGSOYYBukl2UWpZYm2x0enuXnX4NnXGQqAKTYaalqlWoZH+snwWsQah+pJ64Sr5ypbCvQLHCw8TFxsfIycrLzM3PxQAAAh+QQJAQABACwAAAAAGwBkAAACUIyPqcvtD6OctNqLs968+w+G4kiW5omm6sq27gTE8kzX9o3n+s73/g8MCofEovGITCqXzKbzCY1Kp9Sq9YrNarfcrvdrfYnH5LL5jE6r16sCADs=";
|
||||
var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
|
||||
var uri = ioService.newURI(spec, null, null);
|
||||
|
||||
load('async_load_tests.js');
|
||||
470
image/test/unit/test_encoder_apng.js
Normal file
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* Test for APNG encoding in ImageLib
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
|
||||
// dispose=[none|background|previous]
|
||||
// blend=[source|over]
|
||||
|
||||
var apng1A = {
|
||||
// A 3x3 image with 3 frames, alternating red, green, blue. RGB format.
|
||||
width : 3, height : 3, skipFirstFrame : false,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB,
|
||||
transparency : null,
|
||||
plays : 0,
|
||||
|
||||
frames : [
|
||||
{ // frame #1
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
transparency : null,
|
||||
|
||||
pixels : [
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
255,0,0, 255,0,0, 255,0,0
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #2
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
transparency : null,
|
||||
|
||||
pixels : [
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,255,0, 0,255,0, 0,255,0
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #3
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
transparency : null,
|
||||
|
||||
pixels : [
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
0,0,255, 0,0,255, 0,0,255
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABBJREFUCJlj+M/AAEEMWFgAj44I+H2CySsAAAAaZmNUTAAAAAEAAAADAAAAAwAAAAAAAAAAAfQD6AAA7mXKzAAAABNmZEFUAAAAAgiZY2D4zwBFWFgAhpcI+I731VcAAAAaZmNUTAAAAAMAAAADAAAAAwAAAAAAAAAAAfQD6AAAA/MZJQAAABNmZEFUAAAABAiZY2Bg+A9DmCwAfaAI+AGmQVoAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
|
||||
var apng1B = {
|
||||
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
|
||||
width : 3, height : 3, skipFirstFrame : false,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
|
||||
transparency : null,
|
||||
plays : 0,
|
||||
|
||||
frames : [
|
||||
{ // frame #1
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #2
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,255,0,255, 0,255,0,255, 0,255,0,255,
|
||||
0,255,0,255, 0,255,0,255, 0,255,0,255,
|
||||
0,255,0,255, 0,255,0,255, 0,255,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #3
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255,
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255,
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABFJREFUCJlj+M/A8B+GGXByAF3XEe/CoiJ1AAAAGmZjVEwAAAABAAAAAwAAAAMAAAAAAAAAAAH0A+gAAO5lyswAAAASZmRBVAAAAAIImWNg+I8EcXIAVOAR77Vyl9QAAAAaZmNUTAAAAAMAAAADAAAAAwAAAAAAAAAAAfQD6AAAA/MZJQAAABRmZEFUAAAABAiZY2Bg+P8fgXFxAEvpEe8rClxSAAAAAElFTkSuQmCC"
|
||||
};
|
||||
|
||||
|
||||
var apng1C = {
|
||||
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
|
||||
// The first frame is skipped, so it will only flash green/blue (or static red in an APNG-unaware viewer)
|
||||
width : 3, height : 3, skipFirstFrame : true,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
|
||||
transparency : null,
|
||||
plays : 0,
|
||||
|
||||
frames : [
|
||||
{ // frame #1
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #2
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,255,0,255, 0,255,0,255, 0,255,0,255,
|
||||
0,255,0,255, 0,255,0,255, 0,255,0,255,
|
||||
0,255,0,255, 0,255,0,255, 0,255,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #3
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255,
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255,
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAACAAAAAPONk3AAAAARSURBVAiZY/jPwPAfhhlwcgBd1xHvwqIidQAAABpmY1RMAAAAAAAAAAMAAAADAAAAAAAAAAAB9APoAAB1FiAYAAAAEmZkQVQAAAABCJljYPiPBHFyAFTgEe+kD/2tAAAAGmZjVEwAAAACAAAAAwAAAAMAAAAAAAAAAAH0A+gAAJiA8/EAAAAUZmRBVAAAAAMImWNgYPj/H4FxcQBL6RHvC5ggGQAAAABJRU5ErkJggg=="
|
||||
};
|
||||
|
||||
|
||||
var apng2A = {
|
||||
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
|
||||
// blend = over mode
|
||||
// (The green frame is a horizontal gradient, and the blue frame is a
|
||||
// vertical gradient. They stack as they animate.)
|
||||
width : 3, height : 3, skipFirstFrame : false,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
|
||||
transparency : null,
|
||||
plays : 0,
|
||||
|
||||
frames : [
|
||||
{ // frame #1
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #2
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "over", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,255,0,255, 0,255,0,180, 0,255,0,75,
|
||||
0,255,0,255, 0,255,0,180, 0,255,0,75,
|
||||
0,255,0,255, 0,255,0,180, 0,255,0,75
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #3
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "over", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,0,255,75, 0,0,255,75, 0,0,255,75,
|
||||
0,0,255,180, 0,0,255,180, 0,0,255,180,
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAABFJREFUCJlj+M/A8B+GGXByAF3XEe/CoiJ1AAAAGmZjVEwAAAABAAAAAwAAAAMAAAAAAAAAAAH0A+gAAZli+loAAAAcZmRBVAAAAAIImWNg+M/wn+E/wxaG/wzeDDg5ACeGDvKVa3pyAAAAGmZjVEwAAAADAAAAAwAAAAMAAAAAAAAAAAH0A+gAAXT0KbMAAAAcZmRBVAAAAAQImWNgYPjvjcAM/7cgMMP//zAMAPqkDvLn1m3SAAAAAElFTkSuQmCC"
|
||||
};
|
||||
|
||||
|
||||
var apng2B = {
|
||||
// A 3x3 image with 3 frames, alternating red, green, blue. RGBA format.
|
||||
// blend = over, dispose = background
|
||||
// (The green frame is a horizontal gradient, and the blue frame is a
|
||||
// vertical gradient. Each frame is displayed individually, blended to
|
||||
// whatever the background is.)
|
||||
width : 3, height : 3, skipFirstFrame : false,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
|
||||
transparency : null,
|
||||
plays : 0,
|
||||
|
||||
frames : [
|
||||
{ // frame #1
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "background", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255,
|
||||
255,0,0,255, 255,0,0,255, 255,0,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #2
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "background", blend : "over", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,255,0,255, 0,255,0,180, 0,255,0,75,
|
||||
0,255,0,255, 0,255,0,180, 0,255,0,75,
|
||||
0,255,0,255, 0,255,0,180, 0,255,0,75
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #3
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "background", blend : "over", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,0,255,75, 0,0,255,75, 0,0,255,75,
|
||||
0,0,255,180, 0,0,255,180, 0,0,255,180,
|
||||
0,0,255,255, 0,0,255,255, 0,0,255,255
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAADAAAAAM7tusAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AEAbA0RWQAAABFJREFUCJlj+M/A8B+GGXByAF3XEe/CoiJ1AAAAGmZjVEwAAAABAAAAAwAAAAMAAAAAAAAAAAH0A+gBAYB5yxsAAAAcZmRBVAAAAAIImWNg+M/wn+E/wxaG/wzeDDg5ACeGDvKVa3pyAAAAGmZjVEwAAAADAAAAAwAAAAMAAAAAAAAAAAH0A+gBAW3vGPIAAAAcZmRBVAAAAAQImWNgYPjvjcAM/7cgMMP//zAMAPqkDvLn1m3SAAAAAElFTkSuQmCC"
|
||||
};
|
||||
|
||||
|
||||
var apng3 = {
|
||||
// A 3x3 image with 4 frames. First frame is white, then 1x1 frames draw a diagonal line
|
||||
width : 3, height : 3, skipFirstFrame : false,
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA,
|
||||
transparency : null,
|
||||
plays : 0,
|
||||
|
||||
frames : [
|
||||
{ // frame #1
|
||||
width : 3, height : 3,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
|
||||
255,255,255,255, 255,255,255,255, 255,255,255,255,
|
||||
255,255,255,255, 255,255,255,255, 255,255,255,255,
|
||||
255,255,255,255, 255,255,255,255, 255,255,255,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #2
|
||||
width : 1, height : 1,
|
||||
x_offset : 0, y_offset : 0,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,0,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #3
|
||||
width : 1, height : 1,
|
||||
x_offset : 1, y_offset : 1,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,0,0,255
|
||||
]
|
||||
},
|
||||
|
||||
{ // frame #4
|
||||
width : 1, height : 1,
|
||||
x_offset : 2, y_offset : 2,
|
||||
dispose : "none", blend : "source", delay : 500,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
0,0,0,255
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAACGFjVEwAAAAEAAAAAHzNZtAAAAAaZmNUTAAAAAAAAAADAAAAAwAAAAAAAAAAAfQD6AAAdRYgGAAAAA5JREFUCJlj+I8EGHByALuHI91kueRqAAAAGmZjVEwAAAABAAAAAQAAAAEAAAAAAAAAAAH0A+gAADJXfawAAAARZmRBVAAAAAIImWNgYGD4DwABBAEAbr5mpgAAABpmY1RMAAAAAwAAAAEAAAABAAAAAQAAAAEB9APoAAC4OHoxAAAAEWZkQVQAAAAECJljYGBg+A8AAQQBAJZ8LRAAAAAaZmNUTAAAAAUAAAABAAAAAQAAAAIAAAACAfQD6AAA/fh01wAAABFmZEFUAAAABgiZY2BgYPgPAAEEAQB3Eum9AAAAAElFTkSuQmCC"
|
||||
};
|
||||
|
||||
// Main test entry point.
|
||||
function run_test() {
|
||||
dump("Checking apng1A...\n");
|
||||
run_test_for(apng1A);
|
||||
dump("Checking apng1B...\n");
|
||||
run_test_for(apng1B);
|
||||
dump("Checking apng1C...\n");
|
||||
run_test_for(apng1C);
|
||||
|
||||
dump("Checking apng2A...\n");
|
||||
run_test_for(apng2A);
|
||||
dump("Checking apng2B...\n");
|
||||
run_test_for(apng2B);
|
||||
|
||||
dump("Checking apng3...\n");
|
||||
run_test_for(apng3);
|
||||
};
|
||||
|
||||
|
||||
function run_test_for(input) {
|
||||
var encoder, dataURL;
|
||||
|
||||
encoder = encodeImage(input);
|
||||
dataURL = makeDataURL(encoder, "image/png");
|
||||
do_check_eq(dataURL, input.expected);
|
||||
};
|
||||
|
||||
|
||||
function encodeImage(input) {
|
||||
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
|
||||
encoder.QueryInterface(Ci.imgIEncoder);
|
||||
|
||||
var options = "";
|
||||
if (input.transparency) { options += "transparency=" + input.transparency; }
|
||||
options += ";frames=" + input.frames.length;
|
||||
options += ";skipfirstframe=" + (input.skipFirstFrame ? "yes" : "no");
|
||||
options += ";plays=" + input.plays;
|
||||
encoder.startImageEncode(input.width, input.height, input.format, options);
|
||||
|
||||
for (var i = 0; i < input.frames.length; i++) {
|
||||
var frame = input.frames[i];
|
||||
|
||||
options = "";
|
||||
if (frame.transparency) { options += "transparency=" + input.transparency; }
|
||||
options += ";delay=" + frame.delay;
|
||||
options += ";dispose=" + frame.dispose;
|
||||
options += ";blend=" + frame.blend;
|
||||
if (frame.x_offset > 0) { options += ";xoffset=" + frame.x_offset; }
|
||||
if (frame.y_offset > 0) { options += ";yoffset=" + frame.y_offset; }
|
||||
|
||||
encoder.addImageFrame(frame.pixels, frame.pixels.length,
|
||||
frame.width, frame.height, frame.stride, frame.format, options);
|
||||
}
|
||||
|
||||
encoder.endImageEncode();
|
||||
|
||||
return encoder;
|
||||
}
|
||||
|
||||
|
||||
function makeDataURL(encoder, mimetype) {
|
||||
var rawStream = encoder.QueryInterface(Ci.nsIInputStream);
|
||||
|
||||
var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
|
||||
stream.QueryInterface(Ci.nsIBinaryInputStream);
|
||||
|
||||
stream.setInputStream(rawStream);
|
||||
|
||||
var bytes = stream.readByteArray(stream.available()); // returns int[]
|
||||
|
||||
var base64String = toBase64(bytes);
|
||||
|
||||
return "data:" + mimetype + ";base64," + base64String;
|
||||
}
|
||||
|
||||
/* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */
|
||||
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
|
||||
'0123456789+/';
|
||||
const base64Pad = '=';
|
||||
function toBase64(data) {
|
||||
var result = '';
|
||||
var length = data.length;
|
||||
var i;
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
for (i = 0; i < (length - 2); i += 3) {
|
||||
result += toBase64Table[data[i] >> 2];
|
||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
||||
result += toBase64Table[data[i+2] & 0x3f];
|
||||
}
|
||||
|
||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||
if (length%3) {
|
||||
i = length - (length%3);
|
||||
result += toBase64Table[data[i] >> 2];
|
||||
if ((length%3) == 2) {
|
||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += toBase64Table[(data[i+1] & 0x0f) << 2];
|
||||
result += base64Pad;
|
||||
} else {
|
||||
result += toBase64Table[(data[i] & 0x03) << 4];
|
||||
result += base64Pad + base64Pad;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
256
image/test/unit/test_encoder_png.js
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
* Test for PNG encoding in ImageLib
|
||||
*
|
||||
*/
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
|
||||
var png1A = {
|
||||
// A 3x3 image, rows are red, green, blue.
|
||||
// RGB format, transparency defaults.
|
||||
|
||||
transparency : null,
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
|
||||
pixels : [
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
var png1B = {
|
||||
// A 3x3 image, rows are red, green, blue.
|
||||
// RGB format, transparency=none.
|
||||
|
||||
transparency : "none",
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9,
|
||||
|
||||
pixels : [
|
||||
255,0,0, 255,0,0, 255,0,0,
|
||||
0,255,0, 0,255,0, 0,255,0,
|
||||
0,0,255, 0,0,255, 0,0,255,
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
var png2A = {
|
||||
// A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent.
|
||||
|
||||
transparency : null,
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,170, 255,0,0,85,
|
||||
0,255,0,255, 0,255,0,170, 0,255,0,85,
|
||||
0,0,255,255, 0,0,255,170, 0,0,255,85
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAIElEQVQImSXJMQEAMAwEIUy+yZi8DmVFFBcjycn86GgPJw4O8v9DkkEAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
var png2B = {
|
||||
// A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent,
|
||||
// but transparency will be ignored.
|
||||
|
||||
transparency : "none",
|
||||
|
||||
frames : [
|
||||
{
|
||||
width : 3, height : 3,
|
||||
|
||||
format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12,
|
||||
|
||||
pixels : [
|
||||
255,0,0,255, 255,0,0,170, 255,0,0,85,
|
||||
0,255,0,255, 0,255,0,170, 0,255,0,85,
|
||||
0,0,255,255, 0,0,255,170, 0,0,255,85
|
||||
]
|
||||
}
|
||||
|
||||
],
|
||||
expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII="
|
||||
};
|
||||
|
||||
// Main test entry point.
|
||||
function run_test() {
|
||||
dump("Checking png1A...\n")
|
||||
run_test_for(png1A);
|
||||
dump("Checking png1B...\n")
|
||||
run_test_for(png1B);
|
||||
dump("Checking png2A...\n")
|
||||
run_test_for(png2A);
|
||||
dump("Checking png2B...\n")
|
||||
run_test_for(png2B);
|
||||
};
|
||||
|
||||
|
||||
function run_test_for(input) {
|
||||
var encoder, dataURL;
|
||||
|
||||
encoder = encodeImage(input);
|
||||
dataURL = makeDataURL(encoder, "image/png");
|
||||
do_check_eq(dataURL, input.expected);
|
||||
|
||||
encoder = encodeImageAsync(input);
|
||||
dataURL = makeDataURLFromAsync(encoder, "image/png", input.expected);
|
||||
};
|
||||
|
||||
|
||||
function encodeImage(input) {
|
||||
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
|
||||
encoder.QueryInterface(Ci.imgIEncoder);
|
||||
|
||||
var options = "";
|
||||
if (input.transparency) {
|
||||
options += "transparency=" + input.transparency;
|
||||
}
|
||||
|
||||
var frame = input.frames[0];
|
||||
encoder.initFromData(frame.pixels, frame.pixels.length,
|
||||
frame.width, frame.height, frame.stride,
|
||||
frame.format, options);
|
||||
return encoder;
|
||||
}
|
||||
|
||||
function _encodeImageAsyncFactory(frame, options, encoder)
|
||||
{
|
||||
function finishEncode() {
|
||||
encoder.addImageFrame(frame.pixels, frame.pixels.length,
|
||||
frame.width, frame.height, frame.stride,
|
||||
frame.format, options);
|
||||
encoder.endImageEncode();
|
||||
}
|
||||
return finishEncode;
|
||||
}
|
||||
|
||||
function encodeImageAsync(input)
|
||||
{
|
||||
var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance();
|
||||
encoder.QueryInterface(Ci.imgIEncoder);
|
||||
|
||||
var options = "";
|
||||
if (input.transparency) {
|
||||
options += "transparency=" + input.transparency;
|
||||
}
|
||||
|
||||
var frame = input.frames[0];
|
||||
encoder.startImageEncode(frame.width, frame.height,
|
||||
frame.format, options);
|
||||
|
||||
do_timeout(50, _encodeImageAsyncFactory(frame, options, encoder));
|
||||
return encoder;
|
||||
}
|
||||
|
||||
|
||||
function makeDataURL(encoder, mimetype) {
|
||||
var rawStream = encoder.QueryInterface(Ci.nsIInputStream);
|
||||
|
||||
var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
|
||||
stream.QueryInterface(Ci.nsIBinaryInputStream);
|
||||
|
||||
stream.setInputStream(rawStream);
|
||||
|
||||
var bytes = stream.readByteArray(stream.available()); // returns int[]
|
||||
|
||||
var base64String = toBase64(bytes);
|
||||
|
||||
return "data:" + mimetype + ";base64," + base64String;
|
||||
}
|
||||
|
||||
function makeDataURLFromAsync(encoder, mimetype, expected) {
|
||||
do_test_pending();
|
||||
var rawStream = encoder.QueryInterface(Ci.nsIAsyncInputStream);
|
||||
|
||||
var currentThread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread;
|
||||
|
||||
var bytes = [];
|
||||
|
||||
var binarystream = Cc["@mozilla.org/binaryinputstream;1"].createInstance();
|
||||
binarystream.QueryInterface(Ci.nsIBinaryInputStream);
|
||||
|
||||
var asyncReader =
|
||||
{
|
||||
onInputStreamReady: function(stream)
|
||||
{
|
||||
binarystream.setInputStream(stream);
|
||||
var available = 0;
|
||||
try {
|
||||
available = stream.available();
|
||||
} catch(e) { }
|
||||
|
||||
if (available > 0)
|
||||
{
|
||||
bytes = bytes.concat(binarystream.readByteArray(available));
|
||||
stream.asyncWait(this, 0, 0, currentThread);
|
||||
} else {
|
||||
var base64String = toBase64(bytes);
|
||||
var dataURL = "data:" + mimetype + ";base64," + base64String;
|
||||
do_check_eq(dataURL, expected);
|
||||
do_test_finished();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
rawStream.asyncWait(asyncReader, 0, 0, currentThread);
|
||||
}
|
||||
|
||||
/* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */
|
||||
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' +
|
||||
'0123456789+/';
|
||||
const base64Pad = '=';
|
||||
function toBase64(data) {
|
||||
var result = '';
|
||||
var length = data.length;
|
||||
var i;
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
for (i = 0; i < (length - 2); i += 3) {
|
||||
result += toBase64Table[data[i] >> 2];
|
||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)];
|
||||
result += toBase64Table[data[i+2] & 0x3f];
|
||||
}
|
||||
|
||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||
if (length%3) {
|
||||
i = length - (length%3);
|
||||
result += toBase64Table[data[i] >> 2];
|
||||
if ((length%3) == 2) {
|
||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)];
|
||||
result += toBase64Table[(data[i+1] & 0x0f) << 2];
|
||||
result += base64Pad;
|
||||
} else {
|
||||
result += toBase64Table[(data[i] & 0x03) << 4];
|
||||
result += base64Pad + base64Pad;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
737
image/test/unit/test_imgtools.js
Normal file
|
|
@ -0,0 +1,737 @@
|
|||
/*
|
||||
* Tests for imgITools
|
||||
*/
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
|
||||
|
||||
/*
|
||||
* dumpToFile()
|
||||
*
|
||||
* For test development, dumps the specified array to a file.
|
||||
* Call |dumpToFile(outData);| in a test to file to a file.
|
||||
*/
|
||||
function dumpToFile(aData) {
|
||||
var outputFile = do_get_tempdir();
|
||||
outputFile.append("testdump.png");
|
||||
|
||||
var outputStream = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
// WR_ONLY|CREAT|TRUNC
|
||||
outputStream.init(outputFile, 0x02 | 0x08 | 0x20, 0o644, null);
|
||||
|
||||
var bos = Cc["@mozilla.org/binaryoutputstream;1"].
|
||||
createInstance(Ci.nsIBinaryOutputStream);
|
||||
bos.setOutputStream(outputStream);
|
||||
|
||||
bos.writeByteArray(aData, aData.length);
|
||||
|
||||
outputStream.close();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* getFileInputStream()
|
||||
*
|
||||
* Returns an input stream for the specified file.
|
||||
*/
|
||||
function getFileInputStream(aFile) {
|
||||
var inputStream = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
// init the stream as RD_ONLY, -1 == default permissions.
|
||||
inputStream.init(aFile, 0x01, -1, null);
|
||||
|
||||
// Blah. The image decoders use ReadSegments, which isn't implemented on
|
||||
// file input streams. Use a buffered stream to make it work.
|
||||
var bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
|
||||
createInstance(Ci.nsIBufferedInputStream);
|
||||
bis.init(inputStream, 1024);
|
||||
|
||||
return bis;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* streamToArray()
|
||||
*
|
||||
* Consumes an input stream, and returns its bytes as an array.
|
||||
*/
|
||||
function streamToArray(aStream) {
|
||||
var size = aStream.available();
|
||||
|
||||
// use a binary input stream to grab the bytes.
|
||||
var bis = Cc["@mozilla.org/binaryinputstream;1"].
|
||||
createInstance(Ci.nsIBinaryInputStream);
|
||||
bis.setInputStream(aStream);
|
||||
|
||||
var bytes = bis.readByteArray(size);
|
||||
if (size != bytes.length)
|
||||
throw "Didn't read expected number of bytes";
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* compareArrays
|
||||
*
|
||||
* Compares two arrays, and throws if there's a difference.
|
||||
*/
|
||||
function compareArrays(aArray1, aArray2) {
|
||||
do_check_eq(aArray1.length, aArray2.length);
|
||||
|
||||
for (var i = 0; i < aArray1.length; i++)
|
||||
if (aArray1[i] != aArray2[i])
|
||||
throw "arrays differ at index " + i;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* checkExpectedError
|
||||
*
|
||||
* Checks to see if a thrown error was expected or not, and if it
|
||||
* matches the expected value.
|
||||
*/
|
||||
function checkExpectedError (aExpectedError, aActualError) {
|
||||
if (aExpectedError) {
|
||||
if (!aActualError)
|
||||
throw "Didn't throw as expected (" + aExpectedError + ")";
|
||||
|
||||
if (!aExpectedError.test(aActualError))
|
||||
throw "Threw (" + aActualError + "), not (" + aExpectedError;
|
||||
|
||||
// We got the expected error, so make a note in the test log.
|
||||
dump("...that error was expected.\n\n");
|
||||
} else if (aActualError) {
|
||||
throw "Threw unexpected error: " + aActualError;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function run_test() {
|
||||
|
||||
try {
|
||||
|
||||
|
||||
/* ========== 0 ========== */
|
||||
var testnum = 0;
|
||||
var testdesc = "imgITools setup";
|
||||
var err = null;
|
||||
|
||||
var imgTools = Cc["@mozilla.org/image/tools;1"].
|
||||
getService(Ci.imgITools);
|
||||
|
||||
if (!imgTools)
|
||||
throw "Couldn't get imgITools service"
|
||||
|
||||
// Ugh, this is an ugly hack. The pixel values we get in Windows are sometimes
|
||||
// +/- 1 value compared to other platforms, so we need to compare against a
|
||||
// different set of reference images. nsIXULRuntime.OS doesn't seem to be
|
||||
// available in xpcshell, so we'll use this as a kludgy way to figure out if
|
||||
// we're running on Windows.
|
||||
var isWindows = mozinfo.os == "win";
|
||||
|
||||
|
||||
/* ========== 1 ========== */
|
||||
testnum++;
|
||||
testdesc = "test decoding a PNG";
|
||||
|
||||
// 64x64 png, 8415 bytes.
|
||||
var imgName = "image1.png";
|
||||
var inMimeType = "image/png";
|
||||
var imgFile = do_get_file(imgName);
|
||||
|
||||
var istream = getFileInputStream(imgFile);
|
||||
do_check_eq(istream.available(), 8415);
|
||||
|
||||
// Use decodeImageData for this test even though it's deprecated to ensure that
|
||||
// it correctly forwards to decodeImage and continues to work.
|
||||
var outParam = { value: null };
|
||||
imgTools.decodeImageData(istream, inMimeType, outParam);
|
||||
var container = outParam.value;
|
||||
|
||||
// It's not easy to look at the pixel values from JS, so just
|
||||
// check the container's size.
|
||||
do_check_eq(container.width, 64);
|
||||
do_check_eq(container.height, 64);
|
||||
|
||||
|
||||
/* ========== 2 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding a scaled JPEG";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 16);
|
||||
|
||||
var encodedBytes = streamToArray(istream);
|
||||
// Get bytes for exected result
|
||||
var refName = "image1png16x16.jpg";
|
||||
var refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1051);
|
||||
var referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 3 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding an unscaled JPEG";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeImage(container, "image/jpeg");
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image1png64x64.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 4503);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 4 ========== */
|
||||
testnum++;
|
||||
testdesc = "test decoding a JPEG";
|
||||
|
||||
// 32x32 jpeg, 3494 bytes.
|
||||
imgName = "image2.jpg";
|
||||
inMimeType = "image/jpeg";
|
||||
imgFile = do_get_file(imgName);
|
||||
|
||||
istream = getFileInputStream(imgFile);
|
||||
do_check_eq(istream.available(), 3494);
|
||||
|
||||
container = imgTools.decodeImage(istream, inMimeType);
|
||||
|
||||
// It's not easy to look at the pixel values from JS, so just
|
||||
// check the container's size.
|
||||
do_check_eq(container.width, 32);
|
||||
do_check_eq(container.height, 32);
|
||||
|
||||
|
||||
/* ========== 5 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding a scaled PNG";
|
||||
|
||||
if (!isWindows) {
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container, "image/png", 16, 16);
|
||||
|
||||
encodedBytes = streamToArray(istream);
|
||||
// Get bytes for exected result
|
||||
refName = isWindows ? "image2jpg16x16-win.png" : "image2jpg16x16.png";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 950);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
}
|
||||
|
||||
|
||||
/* ========== 6 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding an unscaled PNG";
|
||||
|
||||
if (!isWindows) {
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeImage(container, "image/png");
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = isWindows ? "image2jpg32x32-win.png" : "image2jpg32x32.png";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 3105);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
}
|
||||
|
||||
|
||||
/* ========== 7 ========== */
|
||||
testnum++;
|
||||
testdesc = "test decoding a ICO";
|
||||
|
||||
// 16x16 ico, 1406 bytes.
|
||||
imgName = "image3.ico";
|
||||
inMimeType = "image/x-icon";
|
||||
imgFile = do_get_file(imgName);
|
||||
|
||||
istream = getFileInputStream(imgFile);
|
||||
do_check_eq(istream.available(), 1406);
|
||||
|
||||
container = imgTools.decodeImage(istream, inMimeType);
|
||||
|
||||
// It's not easy to look at the pixel values from JS, so just
|
||||
// check the container's size.
|
||||
do_check_eq(container.width, 16);
|
||||
do_check_eq(container.height, 16);
|
||||
|
||||
|
||||
/* ========== 8 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding a scaled PNG"; // note that we're scaling UP
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container, "image/png", 32, 32);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image3ico32x32.png";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 2285);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 9 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding an unscaled PNG";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeImage(container, "image/png");
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image3ico16x16.png";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 330);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 10 ========== */
|
||||
testnum++;
|
||||
testdesc = "test decoding a GIF";
|
||||
|
||||
// 32x32 gif, 1809 bytes.
|
||||
imgName = "image4.gif";
|
||||
inMimeType = "image/gif";
|
||||
imgFile = do_get_file(imgName);
|
||||
|
||||
istream = getFileInputStream(imgFile);
|
||||
do_check_eq(istream.available(), 1809);
|
||||
|
||||
container = imgTools.decodeImage(istream, inMimeType);
|
||||
|
||||
// It's not easy to look at the pixel values from JS, so just
|
||||
// check the container's size.
|
||||
do_check_eq(container.width, 32);
|
||||
do_check_eq(container.height, 32);
|
||||
|
||||
/* ========== 11 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding an unscaled ICO with format options " +
|
||||
"(format=bmp;bpp=32)";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeImage(container,
|
||||
"image/vnd.microsoft.icon",
|
||||
"format=bmp;bpp=32");
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image4gif32x32bmp32bpp.ico";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 4286);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
/* ========== 12 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding a scaled ICO with format options " +
|
||||
"(format=bmp;bpp=32)";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container,
|
||||
"image/vnd.microsoft.icon",
|
||||
16,
|
||||
16,
|
||||
"format=bmp;bpp=32");
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image4gif16x16bmp32bpp.ico";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1150);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
/* ========== 13 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding an unscaled ICO with format options " +
|
||||
"(format=bmp;bpp=24)";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeImage(container,
|
||||
"image/vnd.microsoft.icon",
|
||||
"format=bmp;bpp=24");
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image4gif32x32bmp24bpp.ico";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 3262);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
/* ========== 14 ========== */
|
||||
testnum++;
|
||||
testdesc = "test encoding a scaled ICO with format options " +
|
||||
"(format=bmp;bpp=24)";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container,
|
||||
"image/vnd.microsoft.icon",
|
||||
16,
|
||||
16,
|
||||
"format=bmp;bpp=24");
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image4gif16x16bmp24bpp.ico";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 894);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 15 ========== */
|
||||
testnum++;
|
||||
testdesc = "test cropping a JPG";
|
||||
|
||||
// 32x32 jpeg, 3494 bytes.
|
||||
imgName = "image2.jpg";
|
||||
inMimeType = "image/jpeg";
|
||||
imgFile = do_get_file(imgName);
|
||||
|
||||
istream = getFileInputStream(imgFile);
|
||||
do_check_eq(istream.available(), 3494);
|
||||
|
||||
container = imgTools.decodeImage(istream, inMimeType);
|
||||
|
||||
// It's not easy to look at the pixel values from JS, so just
|
||||
// check the container's size.
|
||||
do_check_eq(container.width, 32);
|
||||
do_check_eq(container.height, 32);
|
||||
|
||||
// encode a cropped image
|
||||
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 16, 16);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg16x16cropped.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 879);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 16 ========== */
|
||||
testnum++;
|
||||
testdesc = "test cropping a JPG with an offset";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 16, 16, 16, 16);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg16x16cropped2.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 878);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 17 ========== */
|
||||
testnum++;
|
||||
testdesc = "test cropping a JPG without a given height";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 16, 0);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg16x32cropped3.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1127);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 18 ========== */
|
||||
testnum++;
|
||||
testdesc = "test cropping a JPG without a given width";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 0, 16);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg32x16cropped4.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1135);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 19 ========== */
|
||||
testnum++;
|
||||
testdesc = "test cropping a JPG without a given width and height";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 0, 0);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg32x32.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1634);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 20 ========== */
|
||||
testnum++;
|
||||
testdesc = "test scaling a JPG without a given width";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container, "image/jpeg", 0, 16);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg32x16scaled.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1227);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 21 ========== */
|
||||
testnum++;
|
||||
testdesc = "test scaling a JPG without a given height";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container, "image/jpeg", 16, 0);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg16x32scaled.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1219);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 22 ========== */
|
||||
testnum++;
|
||||
testdesc = "test scaling a JPG without a given width and height";
|
||||
|
||||
// we'll reuse the container from the previous test
|
||||
istream = imgTools.encodeScaledImage(container, "image/jpeg", 0, 0);
|
||||
encodedBytes = streamToArray(istream);
|
||||
|
||||
// Get bytes for exected result
|
||||
refName = "image2jpg32x32.jpg";
|
||||
refFile = do_get_file(refName);
|
||||
istream = getFileInputStream(refFile);
|
||||
do_check_eq(istream.available(), 1634);
|
||||
referenceBytes = streamToArray(istream);
|
||||
|
||||
// compare the encoder's output to the reference file.
|
||||
compareArrays(encodedBytes, referenceBytes);
|
||||
|
||||
|
||||
/* ========== 22 ========== */
|
||||
testnum++;
|
||||
testdesc = "test invalid arguments for cropping";
|
||||
|
||||
var numErrors = 0;
|
||||
|
||||
try {
|
||||
// width/height can't be negative
|
||||
imgTools.encodeScaledImage(container, "image/jpeg", -1, -1);
|
||||
} catch (e) { numErrors++; }
|
||||
|
||||
try {
|
||||
// offsets can't be negative
|
||||
imgTools.encodeCroppedImage(container, "image/jpeg", -1, -1, 16, 16);
|
||||
} catch (e) { numErrors++; }
|
||||
|
||||
try {
|
||||
// width/height can't be negative
|
||||
imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, -1, -1);
|
||||
} catch (e) { numErrors++; }
|
||||
|
||||
try {
|
||||
// out of bounds
|
||||
imgTools.encodeCroppedImage(container, "image/jpeg", 17, 17, 16, 16);
|
||||
} catch (e) { numErrors++; }
|
||||
|
||||
try {
|
||||
// out of bounds
|
||||
imgTools.encodeCroppedImage(container, "image/jpeg", 0, 0, 33, 33);
|
||||
} catch (e) { numErrors++; }
|
||||
|
||||
try {
|
||||
// out of bounds
|
||||
imgTools.encodeCroppedImage(container, "image/jpeg", 1, 1, 0, 0);
|
||||
} catch (e) { numErrors++; }
|
||||
|
||||
do_check_eq(numErrors, 6);
|
||||
|
||||
|
||||
/* ========== bug 363986 ========== */
|
||||
testnum = 363986;
|
||||
testdesc = "test PNG and JPEG encoders' Read/ReadSegments methods";
|
||||
|
||||
var testData =
|
||||
[{preImage: "image3.ico",
|
||||
preImageMimeType: "image/x-icon",
|
||||
refImage: "image3ico16x16.png",
|
||||
refImageMimeType: "image/png"},
|
||||
{preImage: "image1.png",
|
||||
preImageMimeType: "image/png",
|
||||
refImage: "image1png64x64.jpg",
|
||||
refImageMimeType: "image/jpeg"}];
|
||||
|
||||
for(var i=0; i<testData.length; ++i) {
|
||||
var dict = testData[i];
|
||||
|
||||
var imgFile = do_get_file(dict["refImage"]);
|
||||
var istream = getFileInputStream(imgFile);
|
||||
var refBytes = streamToArray(istream);
|
||||
|
||||
imgFile = do_get_file(dict["preImage"]);
|
||||
istream = getFileInputStream(imgFile);
|
||||
|
||||
var container = imgTools.decodeImage(istream, dict["preImageMimeType"]);
|
||||
|
||||
istream = imgTools.encodeImage(container, dict["refImageMimeType"]);
|
||||
|
||||
var sstream = Cc["@mozilla.org/storagestream;1"].
|
||||
createInstance(Ci.nsIStorageStream);
|
||||
sstream.init(4096, 4294967295, null);
|
||||
var ostream = sstream.getOutputStream(0);
|
||||
var bostream = Cc["@mozilla.org/network/buffered-output-stream;1"].
|
||||
createInstance(Ci.nsIBufferedOutputStream);
|
||||
|
||||
//use a tiny buffer to make sure the image data doesn't fully fit in it
|
||||
bostream.init(ostream, 8);
|
||||
|
||||
bostream.writeFrom(istream, istream.available());
|
||||
bostream.flush(); bostream.close();
|
||||
|
||||
var encBytes = streamToArray(sstream.newInputStream(0));
|
||||
|
||||
compareArrays(refBytes, encBytes);
|
||||
}
|
||||
|
||||
|
||||
/* ========== bug 413512 ========== */
|
||||
testnum = 413512;
|
||||
testdesc = "test decoding bad favicon (bug 413512)";
|
||||
|
||||
imgName = "bug413512.ico";
|
||||
inMimeType = "image/x-icon";
|
||||
imgFile = do_get_file(imgName);
|
||||
|
||||
istream = getFileInputStream(imgFile);
|
||||
do_check_eq(istream.available(), 17759);
|
||||
var errsrc = "none";
|
||||
|
||||
try {
|
||||
container = imgTools.decodeImage(istream, inMimeType);
|
||||
|
||||
// We expect to hit an error during encoding because the ICO header of the
|
||||
// image is fine, but the actual resources are corrupt. Since decodeImage()
|
||||
// only performs a metadata decode, it doesn't decode far enough to realize
|
||||
// this, but we'll find out when we do a full decode during encodeImage().
|
||||
try {
|
||||
istream = imgTools.encodeImage(container, "image/png");
|
||||
} catch (e) {
|
||||
err = e;
|
||||
errsrc = "encode";
|
||||
}
|
||||
} catch (e) {
|
||||
err = e;
|
||||
errsrc = "decode";
|
||||
}
|
||||
|
||||
do_check_eq(errsrc, "encode");
|
||||
checkExpectedError(/NS_ERROR_FAILURE/, err);
|
||||
|
||||
|
||||
/* ========== bug 815359 ========== */
|
||||
testnum = 815359;
|
||||
testdesc = "test correct ico hotspots (bug 815359)";
|
||||
|
||||
imgName = "bug815359.ico";
|
||||
inMimeType = "image/x-icon";
|
||||
imgFile = do_get_file(imgName);
|
||||
|
||||
istream = getFileInputStream(imgFile);
|
||||
do_check_eq(istream.available(), 4286);
|
||||
|
||||
container = imgTools.decodeImage(istream, inMimeType);
|
||||
|
||||
var props = container.QueryInterface(Ci.nsIProperties);
|
||||
|
||||
do_check_eq(props.get("hotspotX", Ci.nsISupportsPRUint32).data, 10);
|
||||
do_check_eq(props.get("hotspotY", Ci.nsISupportsPRUint32).data, 9);
|
||||
|
||||
|
||||
/* ========== end ========== */
|
||||
|
||||
} catch (e) {
|
||||
throw "FAILED in test #" + testnum + " -- " + testdesc + ": " + e;
|
||||
}
|
||||
};
|
||||
157
image/test/unit/test_moz_icon_uri.js
Normal file
143
image/test/unit/test_private_channel.js
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cr = Components.results;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
var server = new HttpServer();
|
||||
server.registerPathHandler('/image.png', imageHandler);
|
||||
server.start(-1);
|
||||
|
||||
load('image_load_helpers.js');
|
||||
|
||||
var gHits = 0;
|
||||
|
||||
var gIoService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
var gPublicLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
|
||||
var gPrivateLoader = Cc["@mozilla.org/image/loader;1"].createInstance(Ci.imgILoader);
|
||||
gPrivateLoader.QueryInterface(Ci.imgICache).respectPrivacyNotifications();
|
||||
|
||||
function imageHandler(metadata, response) {
|
||||
gHits++;
|
||||
response.setHeader("Cache-Control", "max-age=10000", false);
|
||||
response.setStatusLine(metadata.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "image/png", false);
|
||||
var body = "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
|
||||
var requests = [];
|
||||
var listeners = [];
|
||||
|
||||
function NotificationCallbacks(isPrivate) {
|
||||
this.originAttributes.privateBrowsingId = isPrivate ? 1 : 0;
|
||||
this.usePrivateBrowsing = isPrivate;
|
||||
}
|
||||
|
||||
NotificationCallbacks.prototype = {
|
||||
QueryInterface: function (iid) {
|
||||
if (iid.equals(Ci.nsISupports) ||
|
||||
iid.equals(Ci.nsILoadContext))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
getInterface: function(iid) {
|
||||
if (iid.equals(Ci.nsILoadContext))
|
||||
return this;
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
originAttributes: {
|
||||
privateBrowsingId: 0
|
||||
}
|
||||
};
|
||||
|
||||
var gImgPath = 'http://localhost:' + server.identity.primaryPort + '/image.png';
|
||||
|
||||
function setup_chan(path, isPrivate, callback) {
|
||||
var uri = NetUtil.newURI(gImgPath);
|
||||
var securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
|
||||
var principal = Services.scriptSecurityManager
|
||||
.createCodebasePrincipal(uri, {privateBrowsingId: isPrivate ? 1 : 0});
|
||||
var chan = NetUtil.newChannel({uri: uri, loadingPrincipal: principal,
|
||||
securityFlags: securityFlags,
|
||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE});
|
||||
chan.notificationCallbacks = new NotificationCallbacks(isPrivate);
|
||||
var channelListener = new ChannelListener();
|
||||
chan.asyncOpen2(channelListener);
|
||||
|
||||
var listener = new ImageListener(null, callback);
|
||||
var outlistener = {};
|
||||
var loader = isPrivate ? gPrivateLoader : gPublicLoader;
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
listeners.push(outer);
|
||||
requests.push(loader.loadImageWithChannelXPCOM(chan, outer, null, outlistener));
|
||||
channelListener.outputListener = outlistener.value;
|
||||
listener.synchronous = false;
|
||||
}
|
||||
|
||||
function loadImage(isPrivate, callback) {
|
||||
var listener = new ImageListener(null, callback);
|
||||
var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
|
||||
.createScriptedObserver(listener);
|
||||
var uri = gIoService.newURI(gImgPath, null, null);
|
||||
var loadGroup = Cc["@mozilla.org/network/load-group;1"].createInstance(Ci.nsILoadGroup);
|
||||
loadGroup.notificationCallbacks = new NotificationCallbacks(isPrivate);
|
||||
var loader = isPrivate ? gPrivateLoader : gPublicLoader;
|
||||
requests.push(loader.loadImageXPCOM(uri, null, null, "default", null, loadGroup, outer, null, 0, null));
|
||||
listener.synchronous = false;
|
||||
}
|
||||
|
||||
function run_loadImage_tests() {
|
||||
function observer() {
|
||||
Services.obs.removeObserver(observer, "cacheservice:empty-cache");
|
||||
gHits = 0;
|
||||
loadImage(false, function() {
|
||||
loadImage(false, function() {
|
||||
loadImage(true, function() {
|
||||
loadImage(true, function() {
|
||||
do_check_eq(gHits, 2);
|
||||
server.stop(do_test_finished);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Services.obs.addObserver(observer, "cacheservice:empty-cache", false);
|
||||
let cs = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
|
||||
.getService(Ci.nsICacheStorageService);
|
||||
cs.clear();
|
||||
}
|
||||
|
||||
function cleanup()
|
||||
{
|
||||
for (var i = 0; i < requests.length; ++i) {
|
||||
requests[i].cancelAndForgetObserver(0);
|
||||
}
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
do_test_pending();
|
||||
|
||||
// We create a public channel that loads an image, then an identical
|
||||
// one that should cause a cache read. We then create a private channel
|
||||
// and load the same image, and do that a second time to ensure a cache
|
||||
// read. In total, we should cause two separate http responses to occur,
|
||||
// since the private channels shouldn't be able to use the public cache.
|
||||
setup_chan('/image.png', false, function() {
|
||||
setup_chan('/image.png', false, function() {
|
||||
setup_chan('/image.png', true, function() {
|
||||
setup_chan('/image.png', true, function() {
|
||||
do_check_eq(gHits, 2);
|
||||
run_loadImage_tests();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
45
image/test/unit/xpcshell.ini
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
support-files =
|
||||
async_load_tests.js
|
||||
bug413512.ico
|
||||
bug815359.ico
|
||||
image1.png
|
||||
image1png16x16.jpg
|
||||
image1png64x64.jpg
|
||||
image2.jpg
|
||||
image2jpg16x16-win.png
|
||||
image2jpg16x16.png
|
||||
image2jpg16x16cropped.jpg
|
||||
image2jpg16x16cropped2.jpg
|
||||
image2jpg16x32cropped3.jpg
|
||||
image2jpg16x32scaled.jpg
|
||||
image2jpg32x16cropped4.jpg
|
||||
image2jpg32x16scaled.jpg
|
||||
image2jpg32x32-win.png
|
||||
image2jpg32x32.jpg
|
||||
image2jpg32x32.png
|
||||
image3.ico
|
||||
image3ico16x16.png
|
||||
image3ico32x32.png
|
||||
image4.gif
|
||||
image4gif16x16bmp24bpp.ico
|
||||
image4gif16x16bmp32bpp.ico
|
||||
image4gif32x32bmp24bpp.ico
|
||||
image4gif32x32bmp32bpp.ico
|
||||
image_load_helpers.js
|
||||
|
||||
|
||||
[test_async_notification.js]
|
||||
# Bug 1156452: frequent crash on Android 4.3
|
||||
skip-if = os == "android"
|
||||
[test_async_notification_404.js]
|
||||
[test_async_notification_animated.js]
|
||||
[test_encoder_apng.js]
|
||||
[test_encoder_png.js]
|
||||
[test_imgtools.js]
|
||||
# Bug 676968
|
||||
skip-if = os == "android"
|
||||
[test_moz_icon_uri.js]
|
||||
[test_private_channel.js]
|
||||