0
|
1 /*
|
|
2 * BPG Javascript decoder
|
|
3 *
|
|
4 * Copyright (c) 2014 Fabrice Bellard
|
|
5 *
|
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7 * of this software and associated documentation files (the "Software"), to deal
|
|
8 * in the Software without restriction, including without limitation the rights
|
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10 * copies of the Software, and to permit persons to whom the Software is
|
|
11 * furnished to do so, subject to the following conditions:
|
|
12 *
|
|
13 * The above copyright notice and this permission notice shall be included in
|
|
14 * all copies or substantial portions of the Software.
|
|
15 *
|
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
22 * THE SOFTWARE.
|
|
23 */
|
|
24 window['BPGDecoder'] = function(ctx) {
|
|
25 this.ctx = ctx;
|
|
26 this['imageData'] = null;
|
|
27 this['onload'] = null;
|
|
28 this['frames'] = null;
|
|
29 this['loop_count'] = 0;
|
|
30 }
|
|
31
|
|
32 window['BPGDecoder'].prototype = {
|
|
33
|
|
34 malloc: Module['cwrap']('malloc', 'number', [ 'number' ]),
|
|
35
|
|
36 free: Module['cwrap']('free', 'void', [ 'number' ]),
|
|
37
|
|
38 bpg_decoder_open: Module['cwrap']('bpg_decoder_open', 'number', [ ]),
|
|
39
|
|
40 bpg_decoder_decode: Module['cwrap']('bpg_decoder_decode', 'number', [ 'number', 'array', 'number' ]),
|
|
41
|
|
42 bpg_decoder_get_info: Module['cwrap']('bpg_decoder_get_info', 'number', [ 'number', 'number' ]),
|
|
43
|
|
44 bpg_decoder_start: Module['cwrap']('bpg_decoder_start', 'number', [ 'number', 'number' ]),
|
|
45
|
|
46 bpg_decoder_get_frame_duration: Module['cwrap']('bpg_decoder_get_frame_duration', 'void', [ 'number', 'number', 'number' ]),
|
|
47
|
|
48 bpg_decoder_get_line: Module['cwrap']('bpg_decoder_get_line', 'number', [ 'number', 'number' ]),
|
|
49
|
|
50 bpg_decoder_close: Module['cwrap']('bpg_decoder_close', 'void', [ 'number' ] ),
|
|
51
|
|
52 load: function(url)
|
|
53 {
|
|
54 var request = new XMLHttpRequest();
|
|
55 var this1 = this;
|
|
56
|
|
57 request.open("get", url, true);
|
|
58 request.responseType = "arraybuffer";
|
|
59 request.onload = function(event) {
|
|
60 this1._onload(request, event);
|
|
61 };
|
|
62 request.send();
|
|
63 },
|
|
64
|
|
65 _onload: function(request, event)
|
|
66 {
|
|
67 var data = request.response;
|
|
68 var array = new Uint8Array(data);
|
|
69 var img, w, h, img_info_buf, cimg, p0, rgba_line, w4, frame_count;
|
|
70 var heap8, heap16, heap32, dst, v, i, y, func, duration, frames, loop_count;
|
|
71
|
|
72 // console.log("loaded " + data.byteLength + " bytes");
|
|
73
|
|
74 img = this.bpg_decoder_open();
|
|
75
|
|
76 if (this.bpg_decoder_decode(img, array, array.length) < 0) {
|
|
77 console.log("could not decode image");
|
|
78 return;
|
|
79 }
|
|
80
|
|
81 img_info_buf = this.malloc(5 * 4);
|
|
82 this.bpg_decoder_get_info(img, img_info_buf);
|
|
83 /* extract the image info */
|
|
84 heap8 = Module['HEAPU8'];
|
|
85 heap16 = Module['HEAPU16'];
|
|
86 heap32 = Module['HEAPU32'];
|
|
87 w = heap32[img_info_buf >> 2];
|
|
88 h = heap32[(img_info_buf + 4) >> 2];
|
|
89 loop_count = heap16[(img_info_buf + 16) >> 1];
|
|
90 // console.log("image: w=" + w + " h=" + h + " loop_count=" + loop_count);
|
|
91
|
|
92 w4 = w * 4;
|
|
93 rgba_line = this.malloc(w4);
|
|
94
|
|
95 frame_count = 0;
|
|
96 frames = [];
|
|
97 for(;;) {
|
|
98 /* select RGBA32 output */
|
|
99 if (this.bpg_decoder_start(img, 1) < 0)
|
|
100 break;
|
|
101 this.bpg_decoder_get_frame_duration(img, img_info_buf,
|
|
102 img_info_buf + 4);
|
|
103 duration = (heap32[img_info_buf >> 2] * 1000) / heap32[(img_info_buf + 4) >> 2];
|
|
104
|
|
105 cimg = this.ctx.createImageData(w, h);
|
|
106 dst = cimg.data;
|
|
107 p0 = 0;
|
|
108 for(y = 0; y < h; y++) {
|
|
109 this.bpg_decoder_get_line(img, rgba_line);
|
|
110 for(i = 0; i < w4; i = (i + 1) | 0) {
|
|
111 dst[p0] = heap8[(rgba_line + i) | 0] | 0;
|
|
112 p0 = (p0 + 1) | 0;
|
|
113 }
|
|
114 }
|
|
115 frames[frame_count++] = { 'img': cimg, 'duration': duration };
|
|
116 }
|
|
117
|
|
118 this.free(rgba_line);
|
|
119 this.free(img_info_buf);
|
|
120
|
|
121 this.bpg_decoder_close(img);
|
|
122
|
|
123 this['loop_count'] = loop_count;
|
|
124 this['frames'] = frames;
|
|
125 this['imageData'] = frames[0]['img'];
|
|
126
|
|
127 if (this['onload'])
|
|
128 this['onload']();
|
|
129 }
|
|
130
|
|
131 };
|
|
132
|
|
133 window.onload = function() {
|
|
134 var i, n, el, tab, tab1, url, dec, canvas, id, style, ctx, dw, dh;
|
|
135
|
|
136 /* put all images to load in a separate array */
|
|
137 tab = document.images;
|
|
138 n = tab.length;
|
|
139 tab1 = [];
|
|
140 for(i = 0; i < n; i++) {
|
|
141 el = tab[i];
|
|
142 url = el.src;
|
|
143 if (url.substr(-4,4).toLowerCase() == ".bpg") {
|
|
144 tab1[tab1.length] = el;
|
|
145 }
|
|
146 }
|
|
147
|
|
148 /* change the tags to canvas */
|
|
149 n = tab1.length;
|
|
150 for(i = 0; i < n; i++) {
|
|
151 el = tab1[i];
|
|
152 url = el.src;
|
|
153 canvas = document.createElement("canvas");
|
|
154
|
|
155 if (el.id)
|
|
156 canvas.id = el.id;
|
|
157 if (el.className)
|
|
158 canvas.className = el.className;
|
|
159
|
|
160 /* handle simple attribute cases to resize the canvas */
|
|
161 dw = el.getAttribute("width") | 0;
|
|
162 if (dw) {
|
|
163 canvas.style.width = dw + "px";
|
|
164 }
|
|
165 dh = el.getAttribute("height") | 0;
|
|
166 if (dh) {
|
|
167 canvas.style.height = dh + "px";
|
|
168 }
|
|
169
|
|
170 el.parentNode.replaceChild(canvas, el);
|
|
171
|
|
172 ctx = canvas.getContext("2d");
|
|
173 dec = new BPGDecoder(ctx);
|
|
174 dec.onload = (function(canvas, ctx) {
|
|
175 var dec = this;
|
|
176 var frames = this['frames'];
|
|
177 var imageData = frames[0]['img'];
|
|
178 function next_frame() {
|
|
179 var frame_index = dec.frame_index;
|
|
180
|
|
181 /* compute next frame index */
|
|
182 if (++frame_index >= frames.length) {
|
|
183 if (dec['loop_count'] == 0 ||
|
|
184 dec.loop_counter < dec['loop_count']) {
|
|
185 frame_index = 0;
|
|
186 dec.loop_counter++;
|
|
187 } else {
|
|
188 frame_index = -1;
|
|
189 }
|
|
190 }
|
|
191 if (frame_index >= 0) {
|
|
192 dec.frame_index = frame_index;
|
|
193 ctx.putImageData(frames[frame_index]['img'], 0, 0);
|
|
194 setTimeout(next_frame, frames[frame_index]['duration']);
|
|
195 }
|
|
196 };
|
|
197
|
|
198 /* resize the canvas to the image size */
|
|
199 canvas.width = imageData.width;
|
|
200 canvas.height = imageData.height;
|
|
201
|
|
202 /* draw the image */
|
|
203 ctx.putImageData(imageData, 0, 0);
|
|
204
|
|
205 /* if it is an animation, add a timer to display the next frame */
|
|
206 if (frames.length > 1) {
|
|
207 dec.frame_index = 0;
|
|
208 dec.loop_counter = 0;
|
|
209 setTimeout(next_frame, frames[0]['duration']);
|
|
210 }
|
|
211 }).bind(dec, canvas, ctx);
|
|
212 dec.load(url);
|
|
213 }
|
|
214 };
|
|
215
|
|
216 /* end of dummy function enclosing all the emscripten code */
|
|
217 })();
|