michael@0: /* michael@0: * Test for PNG encoding in libpr0n michael@0: * michael@0: */ michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: michael@0: var png1A = { michael@0: // A 3x3 image, rows are red, green, blue. michael@0: // RGB format, transparency defaults. michael@0: michael@0: transparency : null, michael@0: michael@0: frames : [ michael@0: { michael@0: width : 3, height : 3, michael@0: michael@0: format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9, michael@0: michael@0: pixels : [ michael@0: 255,0,0, 255,0,0, 255,0,0, michael@0: 0,255,0, 0,255,0, 0,255,0, michael@0: 0,0,255, 0,0,255, 0,0,255, michael@0: ] michael@0: } michael@0: michael@0: ], michael@0: expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=" michael@0: }; michael@0: michael@0: var png1B = { michael@0: // A 3x3 image, rows are red, green, blue. michael@0: // RGB format, transparency=none. michael@0: michael@0: transparency : "none", michael@0: michael@0: frames : [ michael@0: { michael@0: width : 3, height : 3, michael@0: michael@0: format : Ci.imgIEncoder.INPUT_FORMAT_RGB, stride : 9, michael@0: michael@0: pixels : [ michael@0: 255,0,0, 255,0,0, 255,0,0, michael@0: 0,255,0, 0,255,0, 0,255,0, michael@0: 0,0,255, 0,0,255, 0,0,255, michael@0: ] michael@0: } michael@0: michael@0: ], michael@0: expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=" michael@0: }; michael@0: michael@0: var png2A = { michael@0: // A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent. michael@0: michael@0: transparency : null, michael@0: michael@0: frames : [ michael@0: { michael@0: width : 3, height : 3, michael@0: michael@0: format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12, michael@0: michael@0: pixels : [ michael@0: 255,0,0,255, 255,0,0,170, 255,0,0,85, michael@0: 0,255,0,255, 0,255,0,170, 0,255,0,85, michael@0: 0,0,255,255, 0,0,255,170, 0,0,255,85 michael@0: ] michael@0: } michael@0: michael@0: ], michael@0: expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAIElEQVQImSXJMQEAMAwEIUy+yZi8DmVFFBcjycn86GgPJw4O8v9DkkEAAAAASUVORK5CYII=" michael@0: }; michael@0: michael@0: var png2B = { michael@0: // A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent, michael@0: // but transparency will be ignored. michael@0: michael@0: transparency : "none", michael@0: michael@0: frames : [ michael@0: { michael@0: width : 3, height : 3, michael@0: michael@0: format : Ci.imgIEncoder.INPUT_FORMAT_RGBA, stride : 12, michael@0: michael@0: pixels : [ michael@0: 255,0,0,255, 255,0,0,170, 255,0,0,85, michael@0: 0,255,0,255, 0,255,0,170, 0,255,0,85, michael@0: 0,0,255,255, 0,0,255,170, 0,0,255,85 michael@0: ] michael@0: } michael@0: michael@0: ], michael@0: expected : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=" michael@0: }; michael@0: michael@0: // Main test entry point. michael@0: function run_test() { michael@0: dump("Checking png1A...\n") michael@0: run_test_for(png1A); michael@0: dump("Checking png1B...\n") michael@0: run_test_for(png1B); michael@0: dump("Checking png2A...\n") michael@0: run_test_for(png2A); michael@0: dump("Checking png2B...\n") michael@0: run_test_for(png2B); michael@0: }; michael@0: michael@0: michael@0: function run_test_for(input) { michael@0: var encoder, dataURL; michael@0: michael@0: encoder = encodeImage(input); michael@0: dataURL = makeDataURL(encoder, "image/png"); michael@0: do_check_eq(dataURL, input.expected); michael@0: michael@0: encoder = encodeImageAsync(input); michael@0: dataURL = makeDataURLFromAsync(encoder, "image/png", input.expected); michael@0: }; michael@0: michael@0: michael@0: function encodeImage(input) { michael@0: var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance(); michael@0: encoder.QueryInterface(Ci.imgIEncoder); michael@0: michael@0: var options = ""; michael@0: if (input.transparency) { michael@0: options += "transparency=" + input.transparency; michael@0: } michael@0: michael@0: var frame = input.frames[0]; michael@0: encoder.initFromData(frame.pixels, frame.pixels.length, michael@0: frame.width, frame.height, frame.stride, michael@0: frame.format, options); michael@0: return encoder; michael@0: } michael@0: michael@0: function _encodeImageAsyncFactory(frame, options, encoder) michael@0: { michael@0: function finishEncode() { michael@0: encoder.addImageFrame(frame.pixels, frame.pixels.length, michael@0: frame.width, frame.height, frame.stride, michael@0: frame.format, options); michael@0: encoder.endImageEncode(); michael@0: } michael@0: return finishEncode; michael@0: } michael@0: michael@0: function encodeImageAsync(input) michael@0: { michael@0: var encoder = Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance(); michael@0: encoder.QueryInterface(Ci.imgIEncoder); michael@0: michael@0: var options = ""; michael@0: if (input.transparency) { michael@0: options += "transparency=" + input.transparency; michael@0: } michael@0: michael@0: var frame = input.frames[0]; michael@0: encoder.startImageEncode(frame.width, frame.height, michael@0: frame.format, options); michael@0: michael@0: do_timeout(50, _encodeImageAsyncFactory(frame, options, encoder)); michael@0: return encoder; michael@0: } michael@0: michael@0: michael@0: function makeDataURL(encoder, mimetype) { michael@0: var rawStream = encoder.QueryInterface(Ci.nsIInputStream); michael@0: michael@0: var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(); michael@0: stream.QueryInterface(Ci.nsIBinaryInputStream); michael@0: michael@0: stream.setInputStream(rawStream); michael@0: michael@0: var bytes = stream.readByteArray(stream.available()); // returns int[] michael@0: michael@0: var base64String = toBase64(bytes); michael@0: michael@0: return "data:" + mimetype + ";base64," + base64String; michael@0: } michael@0: michael@0: function makeDataURLFromAsync(encoder, mimetype, expected) { michael@0: do_test_pending(); michael@0: var rawStream = encoder.QueryInterface(Ci.nsIAsyncInputStream); michael@0: michael@0: var currentThread = Cc["@mozilla.org/thread-manager;1"].getService().currentThread; michael@0: michael@0: var bytes = []; michael@0: michael@0: var binarystream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(); michael@0: binarystream.QueryInterface(Ci.nsIBinaryInputStream); michael@0: michael@0: var asyncReader = michael@0: { michael@0: onInputStreamReady: function(stream) michael@0: { michael@0: binarystream.setInputStream(stream); michael@0: var available = 0; michael@0: try { michael@0: available = stream.available(); michael@0: } catch(e) { } michael@0: michael@0: if (available > 0) michael@0: { michael@0: bytes = bytes.concat(binarystream.readByteArray(available)); michael@0: stream.asyncWait(this, 0, 0, currentThread); michael@0: } else { michael@0: var base64String = toBase64(bytes); michael@0: var dataURL = "data:" + mimetype + ";base64," + base64String; michael@0: do_check_eq(dataURL, expected); michael@0: do_test_finished(); michael@0: } michael@0: michael@0: } michael@0: }; michael@0: rawStream.asyncWait(asyncReader, 0, 0, currentThread); michael@0: } michael@0: michael@0: /* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */ michael@0: michael@0: /* Convert data (an array of integers) to a Base64 string. */ michael@0: const toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + michael@0: '0123456789+/'; michael@0: const base64Pad = '='; michael@0: function toBase64(data) { michael@0: var result = ''; michael@0: var length = data.length; michael@0: var i; michael@0: // Convert every three bytes to 4 ascii characters. michael@0: for (i = 0; i < (length - 2); i += 3) { michael@0: result += toBase64Table[data[i] >> 2]; michael@0: result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; michael@0: result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; michael@0: result += toBase64Table[data[i+2] & 0x3f]; michael@0: } michael@0: michael@0: // Convert the remaining 1 or 2 bytes, pad out to 4 characters. michael@0: if (length%3) { michael@0: i = length - (length%3); michael@0: result += toBase64Table[data[i] >> 2]; michael@0: if ((length%3) == 2) { michael@0: result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; michael@0: result += toBase64Table[(data[i+1] & 0x0f) << 2]; michael@0: result += base64Pad; michael@0: } else { michael@0: result += toBase64Table[(data[i] & 0x03) << 4]; michael@0: result += base64Pad + base64Pad; michael@0: } michael@0: } michael@0: michael@0: return result; michael@0: }