/src/libwebsockets/lib/misc/upng.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * LWS PNG -- derived from uPNG -- derived from LodePNG version 20100808 |
3 | | * Stateful, linewise PNG decode requiring ~36KB fixed heap |
4 | | * |
5 | | * Copyright (c) 2005-2010 Lode Vandevenne (LodePNG) |
6 | | * Copyright (c) 2010 Sean Middleditch (uPNG) |
7 | | * Copyright (c) 2021 Andy Green <andy@warmcat.com> (Stateful, incremental) |
8 | | * |
9 | | * This software is provided 'as-is', without any express or implied |
10 | | * warranty. In no event will the authors be held liable for any damages |
11 | | * arising from the use of this software. |
12 | | |
13 | | * Permission is granted to anyone to use this software for any purpose, |
14 | | * including commercial applications, and to alter it and redistribute it |
15 | | * freely, subject to the following restrictions: |
16 | | * |
17 | | * 1. The origin of this software must not be misrepresented; you must not |
18 | | * claim that you wrote the original software. If you use this software |
19 | | * in a product, an acknowledgment in the product documentation would be |
20 | | * appreciated but is not required. |
21 | | * |
22 | | * 2. Altered source versions must be plainly marked as such, and must not be |
23 | | * misrepresented as being the original software. |
24 | | * |
25 | | * 3. This notice may not be removed or altered from any source |
26 | | * distribution. |
27 | | * |
28 | | * AG: The above notice is the ZLIB license, libpng also uses it. |
29 | | * |
30 | | * This version was rewritten from the upng project's fork of lodepng and |
31 | | * adapted to be a stateful stream parser. This rewrite retains the ZLIB |
32 | | * license of the source material for simplicity. |
33 | | * |
34 | | * That allows it to use a fixed 32KB ringbuffer to hold decodes, and |
35 | | * incrementally decode chunks into it as we want output lines that are not yet |
36 | | * present there. The input png nor the output bitmap need to be all in one |
37 | | * place at one time. |
38 | | */ |
39 | | |
40 | | #include <private-lib-core.h> |
41 | | |
42 | | #include <stdio.h> |
43 | | #include <stdlib.h> |
44 | | #include <string.h> |
45 | | #include <limits.h> |
46 | | |
47 | | typedef enum upng_color { |
48 | | LWS_UPNG_LUM = 0, |
49 | | LWS_UPNG_RGB = 2, |
50 | | LWS_UPNG_LUMA = 4, |
51 | | LWS_UPNG_RGBA = 6 |
52 | | } upng_color; |
53 | | |
54 | | struct upng_unfline { |
55 | | uint8_t *recon; |
56 | | const uint8_t *scanline; |
57 | | const uint8_t *precon; |
58 | | uint8_t filterType; |
59 | | unsigned int bypp; |
60 | | unsigned int bypl; |
61 | | |
62 | | const uint8_t *in; |
63 | | uint8_t *lines; |
64 | | unsigned int bpp; |
65 | | |
66 | | unsigned int y; |
67 | | unsigned long diff; |
68 | | unsigned long ibp; |
69 | | unsigned long sp; |
70 | | |
71 | | char padded; |
72 | | char alt; |
73 | | }; |
74 | | |
75 | | typedef enum { |
76 | | UOF_MAGIC, |
77 | | UOF_SKIP, |
78 | | UOF_TYPE4, |
79 | | UOF_WIDTH4, |
80 | | UOF_HEIGHT4, |
81 | | UOF_CDEPTH, |
82 | | UOF_CTYPE, |
83 | | UOF_ONLY_ZERO3, |
84 | | UOF_SKIP4, |
85 | | |
86 | | UOF_CHUNK_LEN, |
87 | | UOF_CHUNK_TYPE, |
88 | | UOF_INSIDE, |
89 | | |
90 | | UOF_SKIP_CHUNK_LEN, |
91 | | } upng_outer_framing_t; |
92 | | |
93 | | |
94 | | |
95 | | struct lws_upng_t { |
96 | | struct upng_unfline u; |
97 | | inflator_ctx_t inf; |
98 | | |
99 | | unsigned int width; |
100 | | unsigned int height; |
101 | | |
102 | | upng_color color_type; |
103 | | unsigned int color_depth; |
104 | | lws_upng_format_t format; |
105 | | |
106 | | const uint8_t *chunk; |
107 | | |
108 | | int sctr; |
109 | | uint32_t acc; |
110 | | |
111 | | uint32_t chunklen; |
112 | | uint32_t ctype; |
113 | | |
114 | | upng_outer_framing_t of; |
115 | | |
116 | | uint8_t no_more_input; |
117 | | char hold_at_metadata; |
118 | | }; |
119 | | |
120 | | static lws_stateful_ret_t |
121 | | lws_upng_decode(lws_upng_t *upng, const uint8_t **buf, size_t *size); |
122 | | |
123 | | static int |
124 | | paeth(int a, int b, int c) |
125 | 0 | { |
126 | 0 | int p = a + b - c; |
127 | 0 | int pa = p > a ? p - a : a - p; |
128 | 0 | int pb = p > b ? p - b : b - p; |
129 | 0 | int pc = p > c ? p - c : c - p; |
130 | |
|
131 | 0 | if (pa <= pb && pa <= pc) |
132 | 0 | return a; |
133 | | |
134 | 0 | if (pb <= pc) |
135 | 0 | return b; |
136 | | |
137 | 0 | return c; |
138 | 0 | } |
139 | | |
140 | | static lws_stateful_ret_t |
141 | | unfilter_scanline(lws_upng_t *u) |
142 | 0 | { |
143 | 0 | struct upng_unfline *uf = &u->u; |
144 | 0 | unsigned long i; |
145 | |
|
146 | 0 | switch (uf->filterType) { |
147 | 0 | case 0: /* None */ |
148 | 0 | for (i = 0; i < uf->bypl; i++) |
149 | 0 | uf->recon[i] = u->inf.out[(uf->sp + i) % |
150 | 0 | u->inf.info_size]; |
151 | 0 | break; |
152 | 0 | case 1: /* Sub */ |
153 | 0 | for (i = 0; i < uf->bypp; i++) |
154 | 0 | uf->recon[i] = u->inf.out[(uf->sp + i) % |
155 | 0 | u->inf.info_size]; |
156 | |
|
157 | 0 | for (i = uf->bypp; i < uf->bypl; i++) |
158 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
159 | 0 | u->inf.info_size] + |
160 | 0 | uf->recon[i - uf->bypp]); |
161 | 0 | break; |
162 | 0 | case 2: /* Up */ |
163 | 0 | if (uf->y) |
164 | 0 | for (i = 0; i < uf->bypl; i++) |
165 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
166 | 0 | u->inf.info_size] + uf->precon[i]); |
167 | 0 | else |
168 | 0 | for (i = 0; i < uf->bypl; i++) |
169 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
170 | 0 | u->inf.info_size]); |
171 | 0 | break; |
172 | 0 | case 3: /* Average */ |
173 | 0 | if (uf->y) { |
174 | 0 | for (i = 0; i < uf->bypp; i++) |
175 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
176 | 0 | u->inf.info_size] + uf->precon[i] / 2); |
177 | 0 | for (i = uf->bypp; i < uf->bypl; i++) |
178 | 0 | uf->recon[i] = (uint8_t) |
179 | 0 | (u->inf.out[(uf->sp + i) % |
180 | 0 | u->inf.info_size] + |
181 | 0 | ((uf->recon[i - uf->bypp] + |
182 | 0 | uf->precon[i]) / 2)); |
183 | 0 | } else { |
184 | 0 | for (i = 0; i < uf->bypp; i++) |
185 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
186 | 0 | u->inf.info_size]); |
187 | 0 | for (i = uf->bypp; i < uf->bypl; i++) |
188 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
189 | 0 | u->inf.info_size] + |
190 | 0 | uf->recon[i - uf->bypp] / 2); |
191 | 0 | } |
192 | 0 | break; |
193 | 0 | case 4: /* Paeth */ |
194 | 0 | if (uf->y) { |
195 | 0 | for (i = 0; i < uf->bypp; i++) |
196 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
197 | 0 | u->inf.info_size] + |
198 | 0 | paeth(0, uf->precon[i], 0)); |
199 | 0 | for (i = uf->bypp; i < uf->bypl; i++) |
200 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
201 | 0 | u->inf.info_size] + |
202 | 0 | paeth(uf->recon[i - uf->bypp], |
203 | 0 | uf->precon[i], |
204 | 0 | uf->precon[i - uf->bypp])); |
205 | 0 | break; |
206 | 0 | } |
207 | | |
208 | 0 | for (i = 0; i < uf->bypp; i++) |
209 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
210 | 0 | u->inf.info_size]); |
211 | 0 | for (i = uf->bypp; i < uf->bypl; i++) |
212 | 0 | uf->recon[i] = (uint8_t)(u->inf.out[(uf->sp + i) % |
213 | 0 | u->inf.info_size] + |
214 | 0 | paeth(uf->recon[i - uf->bypp], 0, 0)); |
215 | 0 | break; |
216 | 0 | default: |
217 | 0 | lwsl_err("%s: line start is broken %d\n", __func__, |
218 | 0 | uf->filterType); |
219 | 0 | return LWS_SRET_FATAL + 12; |
220 | 0 | } |
221 | | |
222 | 0 | u->inf.consumed_linear += uf->bypl; |
223 | |
|
224 | 0 | return LWS_SRET_OK; |
225 | 0 | } |
226 | | |
227 | | lws_stateful_ret_t |
228 | | lws_upng_emit_next_line(lws_upng_t *u, const uint8_t **ppix, |
229 | | const uint8_t **pos, size_t *size, char hold_at_metadata) |
230 | 0 | { |
231 | 0 | struct upng_unfline *uf = &u->u; |
232 | 0 | unsigned long obp; |
233 | 0 | lws_stateful_ret_t ret = LWS_SRET_OK; |
234 | |
|
235 | 0 | *ppix = NULL; |
236 | |
|
237 | 0 | u->hold_at_metadata = hold_at_metadata; |
238 | |
|
239 | 0 | if (u->height && uf->y >= u->height) |
240 | 0 | goto out; |
241 | | |
242 | | /* |
243 | | * The decoder emits into the 32KB window ringbuffer, if we don't |
244 | | * already have at least one line's worth of output in there, we'll |
245 | | * have to do more inflation |
246 | | */ |
247 | | |
248 | 0 | if (u->inf.outpos_linear - u->inf.consumed_linear < uf->bypl + 1) { |
249 | 0 | ret = lws_upng_decode(u, pos, size); |
250 | 0 | if ((!*size && ret == LWS_SRET_WANT_INPUT) || |
251 | 0 | (ret & (LWS_SRET_FATAL | LWS_SRET_YIELD)) || |
252 | 0 | !u->inf.outpos_linear) |
253 | 0 | return ret; |
254 | | |
255 | 0 | assert(u->inf.info_size); |
256 | 0 | assert(uf->bypl + 1); |
257 | 0 | } |
258 | | |
259 | 0 | if (u->inf.outpos_linear - u->inf.consumed_linear < uf->bypl + 1) |
260 | 0 | return ret; |
261 | | |
262 | 0 | obp = uf->alt ? uf->bypl : 0; |
263 | 0 | uf->precon = uf->alt ? uf->lines : uf->lines + uf->bypl; |
264 | 0 | uf->recon = &uf->lines[obp]; |
265 | 0 | *ppix = uf->recon; |
266 | 0 | uf->filterType = uf->in[(u->inf.consumed_linear++) % u->inf.info_size]; |
267 | 0 | uf->sp = u->inf.consumed_linear % u->inf.info_size; |
268 | |
|
269 | 0 | if (unfilter_scanline(u) != LWS_SRET_OK) { |
270 | 0 | ret = LWS_SRET_FATAL + 13; |
271 | |
|
272 | 0 | goto out; |
273 | 0 | } |
274 | | |
275 | 0 | if (uf->padded) { |
276 | 0 | unsigned long x; |
277 | |
|
278 | 0 | for (x = 0; x < (unsigned long)u->width * (unsigned long)uf->bpp; x++) { |
279 | 0 | uint8_t bit = (uint8_t)((uf->in[(uf->ibp) >> 3] >> |
280 | 0 | (7 - ((uf->ibp) & 7))) & 1); |
281 | 0 | uf->ibp++; |
282 | |
|
283 | 0 | if (!bit) |
284 | 0 | uf->lines[obp >> 3] &= |
285 | 0 | (uint8_t)(~(1 << (7 - (obp & 7)))); |
286 | 0 | else |
287 | 0 | uf->lines[obp >> 3] = (uint8_t)(uf->lines[obp >> 3] | |
288 | 0 | (uint8_t)(1 << (7 - (obp & 7)))); |
289 | |
|
290 | 0 | obp++; |
291 | 0 | } |
292 | |
|
293 | 0 | uf->ibp += uf->diff; |
294 | 0 | } |
295 | |
|
296 | 0 | out: |
297 | 0 | uf->alt ^= 1; |
298 | 0 | uf->y++; |
299 | |
|
300 | 0 | return ret; |
301 | 0 | } |
302 | | |
303 | | static lws_upng_format_t |
304 | 0 | determine_format(lws_upng_t* upng) { |
305 | 0 | switch (upng->color_type) { |
306 | 0 | case LWS_UPNG_LUM: |
307 | 0 | switch (upng->color_depth) { |
308 | 0 | case 1: |
309 | 0 | return LWS_UPNG_LUMINANCE1; |
310 | 0 | case 2: |
311 | 0 | return LWS_UPNG_LUMINANCE2; |
312 | 0 | case 4: |
313 | 0 | return LWS_UPNG_LUMINANCE4; |
314 | 0 | case 8: |
315 | 0 | return LWS_UPNG_LUMINANCE8; |
316 | 0 | default: |
317 | 0 | return LWS_UPNG_BADFORMAT; |
318 | 0 | } |
319 | 0 | case LWS_UPNG_RGB: |
320 | 0 | switch (upng->color_depth) { |
321 | 0 | case 8: |
322 | 0 | return LWS_UPNG_RGB8; |
323 | 0 | case 16: |
324 | 0 | return LWS_UPNG_RGB16; |
325 | 0 | default: |
326 | 0 | return LWS_UPNG_BADFORMAT; |
327 | 0 | } |
328 | 0 | case LWS_UPNG_LUMA: |
329 | 0 | switch (upng->color_depth) { |
330 | 0 | case 1: |
331 | 0 | return LWS_UPNG_LUMINANCE_ALPHA1; |
332 | 0 | case 2: |
333 | 0 | return LWS_UPNG_LUMINANCE_ALPHA2; |
334 | 0 | case 4: |
335 | 0 | return LWS_UPNG_LUMINANCE_ALPHA4; |
336 | 0 | case 8: |
337 | 0 | return LWS_UPNG_LUMINANCE_ALPHA8; |
338 | 0 | default: |
339 | 0 | return LWS_UPNG_BADFORMAT; |
340 | 0 | } |
341 | 0 | case LWS_UPNG_RGBA: |
342 | 0 | switch (upng->color_depth) { |
343 | 0 | case 8: |
344 | 0 | return LWS_UPNG_RGBA8; |
345 | 0 | case 16: |
346 | 0 | return LWS_UPNG_RGBA16; |
347 | 0 | default: |
348 | 0 | return LWS_UPNG_BADFORMAT; |
349 | 0 | } |
350 | 0 | default: |
351 | 0 | return LWS_UPNG_BADFORMAT; |
352 | 0 | } |
353 | 0 | } |
354 | | |
355 | | static const uint8_t magic[] = { 137, 80, 78, 71, 13, 10, 26, 10 }; |
356 | | |
357 | | static lws_stateful_ret_t |
358 | | lws_upng_decode(lws_upng_t* u, const uint8_t **_pos, size_t *_size) |
359 | 0 | { |
360 | 0 | const uint8_t *pos = _pos ? *_pos : NULL, *end = pos + *_size; |
361 | 0 | lws_stateful_ret_t r = LWS_SRET_FATAL + 60; |
362 | 0 | size_t m; |
363 | |
|
364 | 0 | if (u->of == UOF_INSIDE && !u->inf.in) { |
365 | 0 | u->inf.inpos = 0; |
366 | 0 | u->inf.in = pos; |
367 | 0 | u->inf.bp = 0; |
368 | 0 | m = lws_ptr_diff_size_t(end, pos); |
369 | 0 | if (m > u->chunklen) |
370 | 0 | m = u->chunklen; |
371 | 0 | u->inf.inlen = m; |
372 | 0 | } |
373 | |
|
374 | 0 | while (!u->no_more_input && |
375 | 0 | ((u->of == UOF_INSIDE && _pos == NULL) || pos < end)) { |
376 | 0 | switch (u->of) { |
377 | 0 | case UOF_MAGIC: |
378 | 0 | if (*pos++ != magic[u->sctr++]) |
379 | 0 | return LWS_SRET_FATAL + 17; |
380 | 0 | if (u->sctr == sizeof(magic)) { |
381 | 0 | u->of++; |
382 | 0 | u->sctr = 0; |
383 | 0 | } |
384 | 0 | break; |
385 | | |
386 | 0 | case UOF_SKIP: |
387 | 0 | pos++; |
388 | 0 | if (++u->sctr == 4) { |
389 | 0 | u->of++; |
390 | 0 | u->sctr = 0; |
391 | 0 | } |
392 | 0 | break; |
393 | | |
394 | 0 | case UOF_TYPE4: |
395 | 0 | u->acc = (u->acc << 8) | *pos++; |
396 | 0 | if (++u->sctr == 4) { |
397 | 0 | if (u->acc != LWS_FOURCC('I','H','D','R')) |
398 | 0 | return LWS_SRET_FATAL + 18; |
399 | 0 | u->of++; |
400 | 0 | u->sctr = 0; |
401 | 0 | } |
402 | 0 | break; |
403 | | |
404 | 0 | case UOF_WIDTH4: |
405 | 0 | u->acc = (u->acc << 8) | *pos++; |
406 | 0 | if (++u->sctr == 4) { |
407 | 0 | u->width = u->acc; |
408 | 0 | if (!u->acc) |
409 | 0 | return LWS_SRET_FATAL + 18; |
410 | 0 | u->of++; |
411 | 0 | u->sctr = 0; |
412 | 0 | } |
413 | 0 | break; |
414 | | |
415 | 0 | case UOF_HEIGHT4: |
416 | 0 | u->acc = (u->acc << 8) | *pos++; |
417 | 0 | if (++u->sctr == 4) { |
418 | 0 | u->height = u->acc; |
419 | 0 | u->of++; |
420 | 0 | u->sctr = 0; |
421 | 0 | } |
422 | 0 | break; |
423 | | |
424 | 0 | case UOF_CDEPTH: |
425 | 0 | u->color_depth =*pos++; |
426 | 0 | u->of++; |
427 | 0 | break; |
428 | | |
429 | 0 | case UOF_CTYPE: |
430 | 0 | u->color_type = *pos++; |
431 | | //lwsl_notice("w %d, h %d, depth %d, type %d\n", |
432 | | // u->width, u->height, |
433 | | // u->color_depth, u->color_type); |
434 | 0 | u->format = determine_format(u); |
435 | 0 | if (u->format == LWS_UPNG_BADFORMAT) |
436 | 0 | return LWS_SRET_FATAL + 19; |
437 | 0 | u->of++; |
438 | 0 | break; |
439 | | |
440 | 0 | case UOF_ONLY_ZERO3: |
441 | 0 | if (*pos++) |
442 | 0 | return LWS_SRET_FATAL + 20; |
443 | 0 | if (++u->sctr == 3) { |
444 | 0 | u->of++; |
445 | 0 | u->sctr = 0; |
446 | 0 | } |
447 | 0 | break; |
448 | | |
449 | 0 | case UOF_SKIP4: |
450 | 0 | pos++; |
451 | 0 | if (++u->sctr != 4) |
452 | 0 | break; |
453 | | |
454 | | /* takes us to +33 */ |
455 | | |
456 | 0 | memset(&u->inf, 0, sizeof(u->inf)); |
457 | | |
458 | | /* 32KB gz sliding window */ |
459 | 0 | u->inf.info_size = 32768 + 512; |
460 | 0 | u->u.bpp = lws_upng_get_bpp(u); |
461 | 0 | if (!u->u.bpp) |
462 | 0 | return LWS_SRET_FATAL + 14; |
463 | | |
464 | 0 | u->u.y = 0; |
465 | 0 | u->u.ibp = 0; |
466 | 0 | u->u.bypp = (u->u.bpp + 7) / 8; |
467 | 0 | u->inf.bypl = u->u.bypl = u->width * u->u.bypp; |
468 | |
|
469 | 0 | u->inf.outlen = u->inf.info_size; |
470 | 0 | u->inf.outpos = 0; |
471 | 0 | u->inf.state = UPNS_ID_BL_GB_DONE; |
472 | 0 | u->inf.upng = u; |
473 | |
|
474 | 0 | u->u.alt = 0; /* which of the two lines to write to */ |
475 | 0 | u->u.padded = u->u.bpp < 8 && |
476 | 0 | u->width * u->u.bpp != |
477 | 0 | ((u->width * u->u.bpp + 7) / 8) * 8; |
478 | 0 | u->u.diff = (((u->width * u->u.bpp + 7) / 8) * 8) - |
479 | 0 | (u->width * u->u.bpp); |
480 | |
|
481 | 0 | u->of++; |
482 | 0 | u->sctr = 0; |
483 | 0 | break; |
484 | | |
485 | 0 | case UOF_CHUNK_LEN: |
486 | 0 | if (!u->inf.out) { |
487 | 0 | size_t ims = (u->u.bypl * 2) + u->inf.info_size; |
488 | |
|
489 | 0 | if (u->u.bypl > UINT_MAX / 2 || u->inf.info_size > UINT_MAX - (u->u.bypl * 2)) { |
490 | 0 | lwsl_err("%s: integer overflow occur in ims %llu", |
491 | 0 | __func__, (unsigned long long)ims); |
492 | 0 | return LWS_SRET_FATAL + 27; |
493 | 0 | } |
494 | | |
495 | 0 | if (u->hold_at_metadata) |
496 | 0 | return LWS_SRET_AWAIT_RETRY; |
497 | | |
498 | 0 | u->inf.out = (uint8_t *)lws_malloc(ims, __func__); |
499 | 0 | if (!u->inf.out) { |
500 | 0 | lwsl_notice("%s: inf malloc %u OOM\n", |
501 | 0 | __func__, (unsigned int)ims); |
502 | |
|
503 | 0 | return LWS_SRET_YIELD; |
504 | 0 | } |
505 | 0 | u->u.lines = u->inf.out + u->inf.info_size; |
506 | 0 | u->u.in = u->inf.out; |
507 | 0 | } |
508 | 0 | u->chunklen = (u->chunklen << 8) | *pos++; |
509 | 0 | if (++u->sctr == 4) { |
510 | 0 | u->of++; |
511 | 0 | u->sctr = 0; |
512 | 0 | } |
513 | 0 | break; |
514 | | |
515 | 0 | case UOF_CHUNK_TYPE: |
516 | 0 | u->ctype = (u->ctype << 8) | *pos++; |
517 | 0 | if (++u->sctr != 4) |
518 | 0 | break; |
519 | 0 | u->sctr = 0; |
520 | 0 | if (u->ctype == LWS_FOURCC('I','E','N','D')) { |
521 | 0 | u->no_more_input = 1; |
522 | 0 | break; |
523 | 0 | } |
524 | 0 | if (u->ctype != LWS_FOURCC('I','D','A','T')) { |
525 | 0 | if (!(u->ctype & (32 << 24))) |
526 | | /* says it is critical... */ |
527 | 0 | return LWS_SRET_FATAL + 27; |
528 | | |
529 | 0 | u->chunklen += 4; /* chunk-end CRC */ |
530 | | |
531 | | /* noncritical, skip */ |
532 | 0 | u->of = UOF_SKIP_CHUNK_LEN; |
533 | 0 | break; |
534 | 0 | } |
535 | | |
536 | 0 | if (u->chunklen < 2) |
537 | 0 | return LWS_SRET_FATAL + 31; |
538 | | |
539 | | /* it's a usable IDAT */ |
540 | | |
541 | 0 | if (!u->inf.subsequent) |
542 | 0 | u->inf.inpos = 2; |
543 | 0 | else |
544 | 0 | u->inf.inpos = 0; |
545 | |
|
546 | 0 | m = lws_ptr_diff_size_t(end, pos); |
547 | 0 | if (m > u->chunklen) |
548 | 0 | m = u->chunklen; |
549 | |
|
550 | 0 | u->inf.in = pos; |
551 | 0 | u->inf.inlen = m; |
552 | 0 | u->inf.bp = 0; |
553 | 0 | u->of++; |
554 | 0 | break; |
555 | | |
556 | 0 | case UOF_INSIDE: |
557 | 0 | if (!u->inf.subsequent) { |
558 | |
|
559 | 0 | switch (u->sctr) { |
560 | 0 | case 0: |
561 | 0 | u->acc = (uint32_t)((*pos++) << 8); |
562 | 0 | u->sctr++; |
563 | 0 | continue; |
564 | | |
565 | 0 | case 1: |
566 | 0 | u->acc |= *pos++; |
567 | |
|
568 | 0 | if (u->acc % 31) |
569 | 0 | return LWS_SRET_FATAL + 31; |
570 | | |
571 | 0 | if (((u->acc >> 8) & 15) != 8 || |
572 | 0 | ((u->acc >> 12) & 15) > 7) |
573 | 0 | return LWS_SRET_FATAL + 31; |
574 | | |
575 | 0 | if ((u->acc >> 5) & 1) |
576 | 0 | return LWS_SRET_FATAL + 31; |
577 | | |
578 | 0 | u->inf.subsequent = 1; |
579 | 0 | break; |
580 | 0 | } |
581 | 0 | } |
582 | | |
583 | 0 | r = _lws_upng_inflate_data(&u->inf); |
584 | 0 | switch (r) { |
585 | | |
586 | 0 | case LWS_SRET_WANT_INPUT: |
587 | | |
588 | | /* indicate no existing to drain */ |
589 | 0 | u->inf.in = NULL; |
590 | |
|
591 | 0 | pos += u->inf.inlen - u->inf.inpos; |
592 | 0 | u->chunklen = u->chunklen - |
593 | 0 | (unsigned int)(u->inf.inlen); |
594 | |
|
595 | 0 | if (!u->chunklen) { |
596 | 0 | u->chunklen = 4; /* skip the 32-bit CRC */ |
597 | |
|
598 | 0 | u->of = UOF_SKIP_CHUNK_LEN; |
599 | 0 | break; |
600 | 0 | } |
601 | 0 | if (pos != end) { |
602 | 0 | u->inf.inpos = 0; |
603 | 0 | u->inf.in = pos; |
604 | 0 | m = lws_ptr_diff_size_t(end, pos); |
605 | 0 | if (m > u->chunklen) |
606 | 0 | m = u->chunklen; |
607 | 0 | u->inf.inlen = m; |
608 | 0 | continue; |
609 | 0 | } |
610 | 0 | goto bail; |
611 | 0 | default: |
612 | 0 | goto bail; |
613 | 0 | } |
614 | 0 | break; |
615 | | |
616 | 0 | case UOF_SKIP_CHUNK_LEN: |
617 | 0 | pos++; |
618 | 0 | if (!--u->chunklen) { |
619 | 0 | u->of = UOF_CHUNK_LEN; |
620 | 0 | u->sctr = 0; |
621 | 0 | break; |
622 | 0 | } |
623 | 0 | break; |
624 | 0 | } |
625 | 0 | } |
626 | | |
627 | 0 | r = LWS_SRET_OK; |
628 | 0 | if (!u->no_more_input) |
629 | 0 | r = LWS_SRET_WANT_INPUT; |
630 | |
|
631 | 0 | bail: |
632 | 0 | *_pos = pos; |
633 | 0 | *_size = lws_ptr_diff_size_t(end, pos); |
634 | |
|
635 | 0 | return r; |
636 | 0 | } |
637 | | |
638 | | lws_upng_t * |
639 | | lws_upng_new(void) |
640 | 0 | { |
641 | 0 | lws_upng_t* upng; |
642 | |
|
643 | 0 | upng = (lws_upng_t*)lws_zalloc(sizeof(lws_upng_t), __func__); |
644 | 0 | if (upng == NULL) |
645 | 0 | return NULL; |
646 | | |
647 | 0 | upng->color_type = LWS_UPNG_RGBA; |
648 | 0 | upng->color_depth = 8; |
649 | 0 | upng->format = LWS_UPNG_RGBA8; |
650 | |
|
651 | 0 | upng->of = UOF_MAGIC; |
652 | 0 | upng->sctr = 0; |
653 | |
|
654 | 0 | upng->inf.upng = upng; |
655 | |
|
656 | 0 | return upng; |
657 | 0 | } |
658 | | |
659 | | void |
660 | | lws_upng_free(lws_upng_t** upng) |
661 | 0 | { |
662 | 0 | if ((*upng)->inf.out) |
663 | 0 | lws_free_set_NULL((*upng)->inf.out); |
664 | |
|
665 | 0 | lws_free_set_NULL(*upng); |
666 | 0 | } |
667 | | |
668 | | |
669 | | unsigned int |
670 | | lws_upng_get_width(const lws_upng_t* upng) |
671 | 0 | { |
672 | 0 | return upng->width; |
673 | 0 | } |
674 | | |
675 | | unsigned int |
676 | | lws_upng_get_height(const lws_upng_t* upng) |
677 | 0 | { |
678 | 0 | return upng->height; |
679 | 0 | } |
680 | | |
681 | | unsigned int |
682 | | lws_upng_get_bpp(const lws_upng_t* upng) |
683 | 0 | { |
684 | 0 | return lws_upng_get_bitdepth(upng) * |
685 | 0 | lws_upng_get_components(upng); |
686 | 0 | } |
687 | | |
688 | | unsigned int |
689 | | lws_upng_get_components(const lws_upng_t* upng) |
690 | 0 | { |
691 | 0 | switch (upng->color_type) { |
692 | 0 | case LWS_UPNG_LUM: |
693 | 0 | return 1; |
694 | 0 | case LWS_UPNG_RGB: |
695 | 0 | return 3; |
696 | 0 | case LWS_UPNG_LUMA: |
697 | 0 | return 2; |
698 | 0 | case LWS_UPNG_RGBA: |
699 | 0 | return 4; |
700 | 0 | default: |
701 | 0 | return 0; |
702 | 0 | } |
703 | 0 | } |
704 | | |
705 | | unsigned int |
706 | | lws_upng_get_bitdepth(const lws_upng_t* upng) |
707 | 0 | { |
708 | 0 | return upng->color_depth; |
709 | 0 | } |
710 | | |
711 | | unsigned int |
712 | | lws_upng_get_pixelsize(const lws_upng_t* upng) |
713 | 0 | { |
714 | 0 | unsigned bits = lws_upng_get_bitdepth(upng) * |
715 | 0 | lws_upng_get_components(upng); |
716 | |
|
717 | 0 | bits += bits % 8; |
718 | |
|
719 | 0 | return bits; |
720 | 0 | } |
721 | | |
722 | | lws_upng_format_t |
723 | | lws_upng_get_format(const lws_upng_t *upng) |
724 | 0 | { |
725 | 0 | return upng->format; |
726 | 0 | } |