Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: bower_components/gif.js/src/GIFEncoder.js

Issue 786953007: npm_modules: Fork bower_components into Polymer 0.4.0 and 0.5.0 versions (Closed) Base URL: https://chromium.googlesource.com/infra/third_party/npm_modules.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 GIFEncoder.js
3
4 Authors
5 Kevin Weiner (original Java version - kweiner@fmsware.com)
6 Thibault Imbert (AS3 version - bytearray.org)
7 Johan Nordberg (JS version - code@johan-nordberg.com)
8 */
9
10 var NeuQuant = require('./TypedNeuQuant.js');
11 var LZWEncoder = require('./LZWEncoder.js');
12
13 function ByteArray() {
14 this.page = -1;
15 this.pages = [];
16 this.newPage();
17 }
18
19 ByteArray.pageSize = 4096;
20 ByteArray.charMap = {};
21
22 for (var i = 0; i < 256; i++)
23 ByteArray.charMap[i] = String.fromCharCode(i);
24
25 ByteArray.prototype.newPage = function() {
26 this.pages[++this.page] = new Uint8Array(ByteArray.pageSize);
27 this.cursor = 0;
28 };
29
30 ByteArray.prototype.getData = function() {
31 var rv = '';
32 for (var p = 0; p < this.pages.length; p++) {
33 for (var i = 0; i < ByteArray.pageSize; i++) {
34 rv += ByteArray.charMap[this.pages[p][i]];
35 }
36 }
37 return rv;
38 };
39
40 ByteArray.prototype.writeByte = function(val) {
41 if (this.cursor >= ByteArray.pageSize) this.newPage();
42 this.pages[this.page][this.cursor++] = val;
43 };
44
45 ByteArray.prototype.writeUTFBytes = function(string) {
46 for (var l = string.length, i = 0; i < l; i++)
47 this.writeByte(string.charCodeAt(i));
48 };
49
50 ByteArray.prototype.writeBytes = function(array, offset, length) {
51 for (var l = length || array.length, i = offset || 0; i < l; i++)
52 this.writeByte(array[i]);
53 };
54
55 function GIFEncoder(width, height) {
56 // image size
57 this.width = ~~width;
58 this.height = ~~height;
59
60 // transparent color if given
61 this.transparent = null;
62
63 // transparent index in color table
64 this.transIndex = 0;
65
66 // -1 = no repeat, 0 = forever. anything else is repeat count
67 this.repeat = -1;
68
69 // frame delay (hundredths)
70 this.delay = 0;
71
72 this.image = null; // current frame
73 this.pixels = null; // BGR byte array from frame
74 this.indexedPixels = null; // converted frame indexed to palette
75 this.colorDepth = null; // number of bit planes
76 this.colorTab = null; // RGB palette
77 this.usedEntry = new Array(); // active palette entries
78 this.palSize = 7; // color table size (bits-1)
79 this.dispose = -1; // disposal code (-1 = use default)
80 this.firstFrame = true;
81 this.sample = 10; // default sample interval for quantizer
82
83 this.out = new ByteArray();
84 }
85
86 /*
87 Sets the delay time between each frame, or changes it for subsequent frames
88 (applies to last frame added)
89 */
90 GIFEncoder.prototype.setDelay = function(milliseconds) {
91 this.delay = Math.round(milliseconds / 10);
92 };
93
94 /*
95 Sets frame rate in frames per second.
96 */
97 GIFEncoder.prototype.setFrameRate = function(fps) {
98 this.delay = Math.round(100 / fps);
99 };
100
101 /*
102 Sets the GIF frame disposal code for the last added frame and any
103 subsequent frames.
104
105 Default is 0 if no transparent color has been set, otherwise 2.
106 */
107 GIFEncoder.prototype.setDispose = function(disposalCode) {
108 if (disposalCode >= 0) this.dispose = disposalCode;
109 };
110
111 /*
112 Sets the number of times the set of GIF frames should be played.
113
114 -1 = play once
115 0 = repeat indefinitely
116
117 Default is -1
118
119 Must be invoked before the first image is added
120 */
121
122 GIFEncoder.prototype.setRepeat = function(repeat) {
123 this.repeat = repeat;
124 };
125
126 /*
127 Sets the transparent color for the last added frame and any subsequent
128 frames. Since all colors are subject to modification in the quantization
129 process, the color in the final palette for each frame closest to the given
130 color becomes the transparent color for that frame. May be set to null to
131 indicate no transparent color.
132 */
133 GIFEncoder.prototype.setTransparent = function(color) {
134 this.transparent = color;
135 };
136
137 /*
138 Adds next GIF frame. The frame is not written immediately, but is
139 actually deferred until the next frame is received so that timing
140 data can be inserted. Invoking finish() flushes all frames.
141 */
142 GIFEncoder.prototype.addFrame = function(imageData) {
143 this.image = imageData;
144
145 this.getImagePixels(); // convert to correct format if necessary
146 this.analyzePixels(); // build color table & map pixels
147
148 if (this.firstFrame) {
149 this.writeLSD(); // logical screen descriptior
150 this.writePalette(); // global color table
151 if (this.repeat >= 0) {
152 // use NS app extension to indicate reps
153 this.writeNetscapeExt();
154 }
155 }
156
157 this.writeGraphicCtrlExt(); // write graphic control extension
158 this.writeImageDesc(); // image descriptor
159 if (!this.firstFrame) this.writePalette(); // local color table
160 this.writePixels(); // encode and write pixel data
161
162 this.firstFrame = false;
163 };
164
165 /*
166 Adds final trailer to the GIF stream, if you don't call the finish method
167 the GIF stream will not be valid.
168 */
169 GIFEncoder.prototype.finish = function() {
170 this.out.writeByte(0x3b); // gif trailer
171 };
172
173 /*
174 Sets quality of color quantization (conversion of images to the maximum 256
175 colors allowed by the GIF specification). Lower values (minimum = 1)
176 produce better colors, but slow processing significantly. 10 is the
177 default, and produces good color mapping at reasonable speeds. Values
178 greater than 20 do not yield significant improvements in speed.
179 */
180 GIFEncoder.prototype.setQuality = function(quality) {
181 if (quality < 1) quality = 1;
182 this.sample = quality;
183 };
184
185 /*
186 Writes GIF file header
187 */
188 GIFEncoder.prototype.writeHeader = function() {
189 this.out.writeUTFBytes("GIF89a");
190 };
191
192 /*
193 Analyzes current frame colors and creates color map.
194 */
195 GIFEncoder.prototype.analyzePixels = function() {
196 var len = this.pixels.length;
197 var nPix = len / 3;
198
199 this.indexedPixels = new Uint8Array(nPix);
200
201 var imgq = new NeuQuant(this.pixels, this.sample);
202 imgq.buildColormap(); // create reduced palette
203 this.colorTab = imgq.getColormap();
204
205 // map image pixels to new palette
206 var k = 0;
207 for (var j = 0; j < nPix; j++) {
208 var index = imgq.lookupRGB(
209 this.pixels[k++] & 0xff,
210 this.pixels[k++] & 0xff,
211 this.pixels[k++] & 0xff
212 );
213 this.usedEntry[index] = true;
214 this.indexedPixels[j] = index;
215 }
216
217 this.pixels = null;
218 this.colorDepth = 8;
219 this.palSize = 7;
220
221 // get closest match to transparent color if specified
222 if (this.transparent !== null) {
223 this.transIndex = this.findClosest(this.transparent);
224 }
225 };
226
227 /*
228 Returns index of palette color closest to c
229 */
230 GIFEncoder.prototype.findClosest = function(c) {
231 if (this.colorTab === null) return -1;
232
233 var r = (c & 0xFF0000) >> 16;
234 var g = (c & 0x00FF00) >> 8;
235 var b = (c & 0x0000FF);
236 var minpos = 0;
237 var dmin = 256 * 256 * 256;
238 var len = this.colorTab.length;
239
240 for (var i = 0; i < len;) {
241 var dr = r - (this.colorTab[i++] & 0xff);
242 var dg = g - (this.colorTab[i++] & 0xff);
243 var db = b - (this.colorTab[i] & 0xff);
244 var d = dr * dr + dg * dg + db * db;
245 var index = parseInt(i / 3);
246 if (this.usedEntry[index] && (d < dmin)) {
247 dmin = d;
248 minpos = index;
249 }
250 i++;
251 }
252
253 return minpos;
254 };
255
256 /*
257 Extracts image pixels into byte array pixels
258 (removes alphachannel from canvas imagedata)
259 */
260 GIFEncoder.prototype.getImagePixels = function() {
261 var w = this.width;
262 var h = this.height;
263 this.pixels = new Uint8Array(w * h * 3);
264
265 var data = this.image;
266 var count = 0;
267
268 for (var i = 0; i < h; i++) {
269 for (var j = 0; j < w; j++) {
270 var b = (i * w * 4) + j * 4;
271 this.pixels[count++] = data[b];
272 this.pixels[count++] = data[b+1];
273 this.pixels[count++] = data[b+2];
274 }
275 }
276 };
277
278 /*
279 Writes Graphic Control Extension
280 */
281 GIFEncoder.prototype.writeGraphicCtrlExt = function() {
282 this.out.writeByte(0x21); // extension introducer
283 this.out.writeByte(0xf9); // GCE label
284 this.out.writeByte(4); // data block size
285
286 var transp, disp;
287 if (this.transparent === null) {
288 transp = 0;
289 disp = 0; // dispose = no action
290 } else {
291 transp = 1;
292 disp = 2; // force clear if using transparent color
293 }
294
295 if (this.dispose >= 0) {
296 disp = dispose & 7; // user override
297 }
298 disp <<= 2;
299
300 // packed fields
301 this.out.writeByte(
302 0 | // 1:3 reserved
303 disp | // 4:6 disposal
304 0 | // 7 user input - 0 = none
305 transp // 8 transparency flag
306 );
307
308 this.writeShort(this.delay); // delay x 1/100 sec
309 this.out.writeByte(this.transIndex); // transparent color index
310 this.out.writeByte(0); // block terminator
311 };
312
313 /*
314 Writes Image Descriptor
315 */
316 GIFEncoder.prototype.writeImageDesc = function() {
317 this.out.writeByte(0x2c); // image separator
318 this.writeShort(0); // image position x,y = 0,0
319 this.writeShort(0);
320 this.writeShort(this.width); // image size
321 this.writeShort(this.height);
322
323 // packed fields
324 if (this.firstFrame) {
325 // no LCT - GCT is used for first (or only) frame
326 this.out.writeByte(0);
327 } else {
328 // specify normal LCT
329 this.out.writeByte(
330 0x80 | // 1 local color table 1=yes
331 0 | // 2 interlace - 0=no
332 0 | // 3 sorted - 0=no
333 0 | // 4-5 reserved
334 this.palSize // 6-8 size of color table
335 );
336 }
337 };
338
339 /*
340 Writes Logical Screen Descriptor
341 */
342 GIFEncoder.prototype.writeLSD = function() {
343 // logical screen size
344 this.writeShort(this.width);
345 this.writeShort(this.height);
346
347 // packed fields
348 this.out.writeByte(
349 0x80 | // 1 : global color table flag = 1 (gct used)
350 0x70 | // 2-4 : color resolution = 7
351 0x00 | // 5 : gct sort flag = 0
352 this.palSize // 6-8 : gct size
353 );
354
355 this.out.writeByte(0); // background color index
356 this.out.writeByte(0); // pixel aspect ratio - assume 1:1
357 };
358
359 /*
360 Writes Netscape application extension to define repeat count.
361 */
362 GIFEncoder.prototype.writeNetscapeExt = function() {
363 this.out.writeByte(0x21); // extension introducer
364 this.out.writeByte(0xff); // app extension label
365 this.out.writeByte(11); // block size
366 this.out.writeUTFBytes('NETSCAPE2.0'); // app id + auth code
367 this.out.writeByte(3); // sub-block size
368 this.out.writeByte(1); // loop sub-block id
369 this.writeShort(this.repeat); // loop count (extra iterations, 0=repeat foreve r)
370 this.out.writeByte(0); // block terminator
371 };
372
373 /*
374 Writes color table
375 */
376 GIFEncoder.prototype.writePalette = function() {
377 this.out.writeBytes(this.colorTab);
378 var n = (3 * 256) - this.colorTab.length;
379 for (var i = 0; i < n; i++)
380 this.out.writeByte(0);
381 };
382
383 GIFEncoder.prototype.writeShort = function(pValue) {
384 this.out.writeByte(pValue & 0xFF);
385 this.out.writeByte((pValue >> 8) & 0xFF);
386 };
387
388 /*
389 Encodes and writes pixel data
390 */
391 GIFEncoder.prototype.writePixels = function() {
392 var enc = new LZWEncoder(this.width, this.height, this.indexedPixels, this.col orDepth);
393 enc.encode(this.out);
394 };
395
396 /*
397 Retrieves the GIF stream
398 */
399 GIFEncoder.prototype.stream = function() {
400 return this.out;
401 };
402
403 module.exports = GIFEncoder;
OLDNEW
« no previous file with comments | « bower_components/gif.js/site/templates/test.html ('k') | bower_components/gif.js/src/LZWEncoder.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698