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