test_encoder_png.js (7127B)
1 /* 2 * Test for PNG encoding in ImageLib 3 * 4 */ 5 6 const { AppConstants } = ChromeUtils.importESModule( 7 "resource://gre/modules/AppConstants.sys.mjs" 8 ); 9 10 var png1A = { 11 // A 3x3 image, rows are red, green, blue. 12 // RGB format, transparency defaults. 13 14 transparency: null, 15 16 frames: [ 17 { 18 width: 3, 19 height: 3, 20 21 format: Ci.imgIEncoder.INPUT_FORMAT_RGB, 22 stride: 9, 23 24 pixels: [ 25 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 26 255, 0, 0, 255, 0, 0, 255, 27 ], 28 }, 29 ], 30 expected: 31 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAFElEQVQIW2P8zwAFjAwwJiMDjAkANiQDAUpvlioAAAAASUVORK5CYII=", 32 }; 33 34 var png1B = { 35 // A 3x3 image, rows are red, green, blue. 36 // RGB format, transparency=none. 37 38 transparency: "none", 39 40 frames: [ 41 { 42 width: 3, 43 height: 3, 44 45 format: Ci.imgIEncoder.INPUT_FORMAT_RGB, 46 stride: 9, 47 48 pixels: [ 49 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 255, 0, 0, 255, 0, 0, 255, 0, 0, 0, 50 255, 0, 0, 255, 0, 0, 255, 51 ], 52 }, 53 ], 54 expected: 55 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAFElEQVQIW2P8zwAFjAwwJiMDjAkANiQDAUpvlioAAAAASUVORK5CYII=", 56 }; 57 58 var png2A = { 59 // A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent. 60 61 transparency: null, 62 63 frames: [ 64 { 65 width: 3, 66 height: 3, 67 68 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 69 stride: 12, 70 71 pixels: [ 72 255, 0, 0, 255, 255, 0, 0, 170, 255, 0, 0, 85, 0, 255, 0, 255, 0, 255, 73 0, 170, 0, 255, 0, 85, 0, 0, 255, 255, 0, 0, 255, 170, 0, 0, 255, 85, 74 ], 75 }, 76 ], 77 expected: AppConstants.USE_LIBZ_RS 78 ? "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAG0lEQVQIW2P8z8Dwn4GBYTUIMzL8R+Yw/IdzAMoLCgDGywYPAAAAAElFTkSuQmCC" 79 : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAYAAABWKLW/AAAAGUlEQVQIW2P8z8AARAyrQZgRyETiMPyHcwDKCwoAGxxLEQAAAABJRU5ErkJggg==", 80 }; 81 82 var png2B = { 83 // A 3x3 image, rows are: red, green, blue. Columns are: 0%, 33%, 66% transparent, 84 // but transparency will be ignored. 85 86 transparency: "none", 87 88 frames: [ 89 { 90 width: 3, 91 height: 3, 92 93 format: Ci.imgIEncoder.INPUT_FORMAT_RGBA, 94 stride: 12, 95 96 pixels: [ 97 255, 0, 0, 255, 255, 0, 0, 170, 255, 0, 0, 85, 0, 255, 0, 255, 0, 255, 98 0, 170, 0, 255, 0, 85, 0, 0, 255, 255, 0, 0, 255, 170, 0, 0, 255, 85, 99 ], 100 }, 101 ], 102 expected: 103 "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAFElEQVQIW2P8zwAFjAwwJiMDjAkANiQDAUpvlioAAAAASUVORK5CYII=", 104 }; 105 106 // Main test entry point. 107 function run_test() { 108 dump("Checking png1A...\n"); 109 run_test_for(png1A); 110 dump("Checking png1B...\n"); 111 run_test_for(png1B); 112 dump("Checking png2A...\n"); 113 run_test_for(png2A); 114 dump("Checking png2B...\n"); 115 run_test_for(png2B); 116 } 117 118 function run_test_for(input) { 119 var encoder, dataURL; 120 121 encoder = encodeImage(input); 122 dataURL = makeDataURL(encoder, "image/png"); 123 Assert.equal(dataURL, input.expected); 124 125 encoder = encodeImageAsync(input); 126 dataURL = makeDataURLFromAsync(encoder, "image/png", input.expected); 127 } 128 129 function encodeImage(input) { 130 var encoder = 131 Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance(); 132 encoder.QueryInterface(Ci.imgIEncoder); 133 134 var options = ""; 135 if (input.transparency) { 136 options += "transparency=" + input.transparency; 137 } 138 139 var frame = input.frames[0]; 140 encoder.initFromData( 141 frame.pixels, 142 frame.pixels.length, 143 frame.width, 144 frame.height, 145 frame.stride, 146 frame.format, 147 options, 148 null 149 ); 150 return encoder; 151 } 152 153 function _encodeImageAsyncFactory(frame, options, encoder) { 154 function finishEncode() { 155 encoder.addImageFrame( 156 frame.pixels, 157 frame.pixels.length, 158 frame.width, 159 frame.height, 160 frame.stride, 161 frame.format, 162 options 163 ); 164 encoder.endImageEncode(); 165 } 166 return finishEncode; 167 } 168 169 function encodeImageAsync(input) { 170 var encoder = 171 Cc["@mozilla.org/image/encoder;2?type=image/png"].createInstance(); 172 encoder.QueryInterface(Ci.imgIEncoder); 173 174 var options = ""; 175 if (input.transparency) { 176 options += "transparency=" + input.transparency; 177 } 178 179 var frame = input.frames[0]; 180 encoder.startImageEncode(frame.width, frame.height, frame.format, options); 181 182 do_timeout(50, _encodeImageAsyncFactory(frame, options, encoder)); 183 return encoder; 184 } 185 186 function makeDataURL(encoder, mimetype) { 187 var rawStream = encoder.QueryInterface(Ci.nsIInputStream); 188 189 var stream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(); 190 stream.QueryInterface(Ci.nsIBinaryInputStream); 191 192 stream.setInputStream(rawStream); 193 194 var bytes = stream.readByteArray(stream.available()); // returns int[] 195 196 var base64String = toBase64(bytes); 197 198 return "data:" + mimetype + ";base64," + base64String; 199 } 200 201 function makeDataURLFromAsync(encoder, mimetype, expected) { 202 do_test_pending(); 203 var rawStream = encoder.QueryInterface(Ci.nsIAsyncInputStream); 204 205 var currentThread = 206 Cc["@mozilla.org/thread-manager;1"].getService().currentThread; 207 208 var bytes = []; 209 210 var binarystream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(); 211 binarystream.QueryInterface(Ci.nsIBinaryInputStream); 212 213 var asyncReader = { 214 onInputStreamReady(stream) { 215 binarystream.setInputStream(stream); 216 var available = 0; 217 try { 218 available = stream.available(); 219 } catch (e) {} 220 221 if (available > 0) { 222 bytes = bytes.concat(binarystream.readByteArray(available)); 223 stream.asyncWait(this, 0, 0, currentThread); 224 } else { 225 var base64String = toBase64(bytes); 226 var dataURL = "data:" + mimetype + ";base64," + base64String; 227 Assert.equal(dataURL, expected); 228 do_test_finished(); 229 } 230 }, 231 }; 232 rawStream.asyncWait(asyncReader, 0, 0, currentThread); 233 } 234 235 /* toBase64 copied from extensions/xml-rpc/src/nsXmlRpcClient.js */ 236 237 /* Convert data (an array of integers) to a Base64 string. */ 238 const toBase64Table = 239 // eslint-disable-next-line no-useless-concat 240 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/"; 241 const base64Pad = "="; 242 function toBase64(data) { 243 var result = ""; 244 var length = data.length; 245 var i; 246 // Convert every three bytes to 4 ascii characters. 247 for (i = 0; i < length - 2; i += 3) { 248 result += toBase64Table[data[i] >> 2]; 249 result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; 250 result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)]; 251 result += toBase64Table[data[i + 2] & 0x3f]; 252 } 253 254 // Convert the remaining 1 or 2 bytes, pad out to 4 characters. 255 if (length % 3) { 256 i = length - (length % 3); 257 result += toBase64Table[data[i] >> 2]; 258 if (length % 3 == 2) { 259 result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; 260 result += toBase64Table[(data[i + 1] & 0x0f) << 2]; 261 result += base64Pad; 262 } else { 263 result += toBase64Table[(data[i] & 0x03) << 4]; 264 result += base64Pad + base64Pad; 265 } 266 } 267 268 return result; 269 }