/src/leptonica/src/spixio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file spixio.c |
29 | | * <pre> |
30 | | * |
31 | | * This does fast serialization of a pix in memory to file, |
32 | | * copying the raw data for maximum speed. The underlying |
33 | | * function serializes it to memory, and it is wrapped to be |
34 | | * callable from standard pixRead() and pixWrite() file functions. |
35 | | * |
36 | | * Reading spix from file |
37 | | * PIX *pixReadStreamSpix() |
38 | | * l_int32 readHeaderSpix() |
39 | | * l_int32 freadHeaderSpix() |
40 | | * l_int32 sreadHeaderSpix() |
41 | | * |
42 | | * Writing spix to file |
43 | | * l_int32 pixWriteStreamSpix() |
44 | | * |
45 | | * Low-level serialization of pix to/from memory (uncompressed) |
46 | | * PIX *pixReadMemSpix() |
47 | | * l_int32 pixWriteMemSpix() |
48 | | * l_int32 pixSerializeToMemory() |
49 | | * PIX *pixDeserializeFromMemory() |
50 | | * |
51 | | * Note: these functions have not been extensively tested for fuzzing |
52 | | * (bad input data that can result in, e.g., memory faults). |
53 | | * The spix serialization format is only defined here, in leptonica. |
54 | | * The image data is uncompressed and the serialization is not intended |
55 | | * to be a secure file format from untrusted sources. |
56 | | * </pre> |
57 | | */ |
58 | | |
59 | | #ifdef HAVE_CONFIG_H |
60 | | #include <config_auto.h> |
61 | | #endif /* HAVE_CONFIG_H */ |
62 | | |
63 | | #include <string.h> |
64 | | #include "allheaders.h" |
65 | | |
66 | | /* Image dimension limits */ |
67 | | static const l_int32 MaxAllowedWidth = 1000000; |
68 | | static const l_int32 MaxAllowedHeight = 1000000; |
69 | | static const l_int64 MaxAllowedArea = 400000000LL; |
70 | | |
71 | | #ifndef NO_CONSOLE_IO |
72 | | #define DEBUG_SERIALIZE 0 |
73 | | #endif /* ~NO_CONSOLE_IO */ |
74 | | |
75 | | |
76 | | /*-----------------------------------------------------------------------* |
77 | | * Reading spix from file * |
78 | | *-----------------------------------------------------------------------*/ |
79 | | /*! |
80 | | * \brief pixReadStreamSpix() |
81 | | * |
82 | | * \param[in] fp file stream |
83 | | * \return pix, or NULL on error. |
84 | | * |
85 | | * <pre> |
86 | | * Notes: |
87 | | * (1) If called from pixReadStream(), the stream is positioned |
88 | | * at the beginning of the file. |
89 | | * </pre> |
90 | | */ |
91 | | PIX * |
92 | | pixReadStreamSpix(FILE *fp) |
93 | 0 | { |
94 | 0 | size_t nbytes; |
95 | 0 | l_uint8 *data; |
96 | 0 | PIX *pix; |
97 | |
|
98 | 0 | if (!fp) |
99 | 0 | return (PIX *)ERROR_PTR("stream not defined", __func__, NULL); |
100 | | |
101 | 0 | if ((data = l_binaryReadStream(fp, &nbytes)) == NULL) |
102 | 0 | return (PIX *)ERROR_PTR("data not read", __func__, NULL); |
103 | 0 | pix = pixReadMemSpix(data, nbytes); |
104 | 0 | LEPT_FREE(data); |
105 | 0 | if (!pix) |
106 | 0 | return (PIX *)ERROR_PTR("pix not made", __func__, NULL); |
107 | 0 | return pix; |
108 | 0 | } |
109 | | |
110 | | |
111 | | /*! |
112 | | * \brief readHeaderSpix() |
113 | | * |
114 | | * \param[in] filename |
115 | | * \param[out] pwidth width |
116 | | * \param[out] pheight height |
117 | | * \param[out] pbps bits/sample |
118 | | * \param[out] pspp samples/pixel |
119 | | * \param[out] piscmap [optional] input NULL to ignore |
120 | | * \return 0 if OK, 1 on error |
121 | | * |
122 | | * <pre> |
123 | | * Notes: |
124 | | * (1) If there is a colormap, iscmap is returned as 1; else 0. |
125 | | * </pre> |
126 | | */ |
127 | | l_ok |
128 | | readHeaderSpix(const char *filename, |
129 | | l_int32 *pwidth, |
130 | | l_int32 *pheight, |
131 | | l_int32 *pbps, |
132 | | l_int32 *pspp, |
133 | | l_int32 *piscmap) |
134 | 0 | { |
135 | 0 | l_int32 ret; |
136 | 0 | FILE *fp; |
137 | |
|
138 | 0 | if (!filename) |
139 | 0 | return ERROR_INT("filename not defined", __func__, 1); |
140 | 0 | if (!pwidth || !pheight || !pbps || !pspp) |
141 | 0 | return ERROR_INT("input ptr(s) not defined", __func__, 1); |
142 | 0 | if ((fp = fopenReadStream(filename)) == NULL) |
143 | 0 | return ERROR_INT_1("image file not found", filename, __func__, 1); |
144 | 0 | ret = freadHeaderSpix(fp, pwidth, pheight, pbps, pspp, piscmap); |
145 | 0 | fclose(fp); |
146 | 0 | return ret; |
147 | 0 | } |
148 | | |
149 | | |
150 | | /*! |
151 | | * \brief freadHeaderSpix() |
152 | | * |
153 | | * \param[in] fp file stream |
154 | | * \param[out] pwidth width |
155 | | * \param[out] pheight height |
156 | | * \param[out] pbps bits/sample |
157 | | * \param[out] pspp samples/pixel |
158 | | * \param[out] piscmap [optional] input NULL to ignore |
159 | | * \return 0 if OK, 1 on error |
160 | | * |
161 | | * <pre> |
162 | | * Notes: |
163 | | * (1) If there is a colormap, iscmap is returned as 1; else 0. |
164 | | * </pre> |
165 | | */ |
166 | | l_ok |
167 | | freadHeaderSpix(FILE *fp, |
168 | | l_int32 *pwidth, |
169 | | l_int32 *pheight, |
170 | | l_int32 *pbps, |
171 | | l_int32 *pspp, |
172 | | l_int32 *piscmap) |
173 | 0 | { |
174 | 0 | l_int32 nbytes, ret; |
175 | 0 | l_uint32 data[6]; |
176 | |
|
177 | 0 | if (!fp) |
178 | 0 | return ERROR_INT("stream not defined", __func__, 1); |
179 | 0 | if (!pwidth || !pheight || !pbps || !pspp) |
180 | 0 | return ERROR_INT("input ptr(s) not defined", __func__, 1); |
181 | | |
182 | 0 | nbytes = fnbytesInFile(fp); |
183 | 0 | if (nbytes < 32) |
184 | 0 | return ERROR_INT("file too small to be spix", __func__, 1); |
185 | 0 | if (fread(data, 4, 6, fp) != 6) |
186 | 0 | return ERROR_INT("error reading data", __func__, 1); |
187 | 0 | ret = sreadHeaderSpix(data, nbytes, pwidth, pheight, pbps, pspp, piscmap); |
188 | 0 | return ret; |
189 | 0 | } |
190 | | |
191 | | |
192 | | /*! |
193 | | * \brief sreadHeaderSpix() |
194 | | * |
195 | | * \param[in] data |
196 | | * \param[in] size of data |
197 | | * \param[out] pwidth width |
198 | | * \param[out] pheight height |
199 | | * \param[out] pbps bits/sample |
200 | | * \param[out] pspp samples/pixel |
201 | | * \param[out] piscmap [optional] input NULL to ignore |
202 | | * \return 0 if OK, 1 on error |
203 | | * |
204 | | * <pre> |
205 | | * Notes: |
206 | | * (1) If there is a colormap, iscmap is returned as 1; else 0. |
207 | | * </pre> |
208 | | */ |
209 | | l_ok |
210 | | sreadHeaderSpix(const l_uint32 *data, |
211 | | size_t size, |
212 | | l_int32 *pwidth, |
213 | | l_int32 *pheight, |
214 | | l_int32 *pbps, |
215 | | l_int32 *pspp, |
216 | | l_int32 *piscmap) |
217 | 0 | { |
218 | 0 | char *id; |
219 | 0 | l_int32 d, ncolors; |
220 | |
|
221 | 0 | if (!data) |
222 | 0 | return ERROR_INT("data not defined", __func__, 1); |
223 | 0 | if (!pwidth || !pheight || !pbps || !pspp) |
224 | 0 | return ERROR_INT("input ptr(s) not defined", __func__, 1); |
225 | 0 | *pwidth = *pheight = *pbps = *pspp = 0; |
226 | 0 | if (piscmap) |
227 | 0 | *piscmap = 0; |
228 | 0 | if (size < 28) |
229 | 0 | return ERROR_INT("size too small", __func__, 1); |
230 | | |
231 | | /* Check file id */ |
232 | 0 | id = (char *)data; |
233 | 0 | if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x') |
234 | 0 | return ERROR_INT("not a valid spix file", __func__, 1); |
235 | | |
236 | 0 | *pwidth = data[1]; |
237 | 0 | *pheight = data[2]; |
238 | 0 | d = data[3]; |
239 | 0 | if (d <= 16) { |
240 | 0 | *pbps = d; |
241 | 0 | *pspp = 1; |
242 | 0 | } else { |
243 | 0 | *pbps = 8; |
244 | 0 | *pspp = d / 8; /* if the pix is 32 bpp, call it 4 samples */ |
245 | 0 | } |
246 | 0 | ncolors = data[5]; |
247 | 0 | if (piscmap) |
248 | 0 | *piscmap = (ncolors == 0) ? 0 : 1; |
249 | |
|
250 | 0 | return 0; |
251 | 0 | } |
252 | | |
253 | | |
254 | | /*-----------------------------------------------------------------------* |
255 | | * Writing spix to file * |
256 | | *-----------------------------------------------------------------------*/ |
257 | | /*! |
258 | | * \brief pixWriteStreamSpix() |
259 | | * |
260 | | * \param[in] fp file stream |
261 | | * \param[in] pix |
262 | | * \return 0 if OK; 1 on error |
263 | | */ |
264 | | l_ok |
265 | | pixWriteStreamSpix(FILE *fp, |
266 | | PIX *pix) |
267 | 0 | { |
268 | 0 | l_uint8 *data; |
269 | 0 | size_t size; |
270 | |
|
271 | 0 | if (!fp) |
272 | 0 | return ERROR_INT("stream not defined", __func__, 1); |
273 | 0 | if (!pix) |
274 | 0 | return ERROR_INT("pix not defined", __func__, 1); |
275 | | |
276 | 0 | if (pixWriteMemSpix(&data, &size, pix)) |
277 | 0 | return ERROR_INT("failure to write pix to memory", __func__, 1); |
278 | 0 | fwrite(data, 1, size, fp); |
279 | 0 | LEPT_FREE(data); |
280 | 0 | return 0; |
281 | 0 | } |
282 | | |
283 | | |
284 | | /*-----------------------------------------------------------------------* |
285 | | * Low-level serialization of pix to/from memory (uncompressed) * |
286 | | *-----------------------------------------------------------------------*/ |
287 | | /*! |
288 | | * \brief pixReadMemSpix() |
289 | | * |
290 | | * \param[in] data const; uncompressed |
291 | | * \param[in] size bytes of data |
292 | | * \return pix, or NULL on error |
293 | | */ |
294 | | PIX * |
295 | | pixReadMemSpix(const l_uint8 *data, |
296 | | size_t size) |
297 | 1.61k | { |
298 | 1.61k | return pixDeserializeFromMemory((l_uint32 *)data, size); |
299 | 1.61k | } |
300 | | |
301 | | |
302 | | /*! |
303 | | * \brief pixWriteMemSpix() |
304 | | * |
305 | | * \param[out] pdata data of serialized, uncompressed pix |
306 | | * \param[out] psize size of returned data |
307 | | * \param[in] pix all depths; colormap OK |
308 | | * \return 0 if OK, 1 on error |
309 | | */ |
310 | | l_ok |
311 | | pixWriteMemSpix(l_uint8 **pdata, |
312 | | size_t *psize, |
313 | | PIX *pix) |
314 | 0 | { |
315 | 0 | return pixSerializeToMemory(pix, (l_uint32 **)pdata, psize); |
316 | 0 | } |
317 | | |
318 | | |
319 | | /*! |
320 | | * \brief pixSerializeToMemory() |
321 | | * |
322 | | * \param[in] pixs all depths, colormap OK |
323 | | * \param[out] pdata serialized data in memory |
324 | | * \param[out] pnbytes number of bytes in data string |
325 | | * \return 0 if OK, 1 on error |
326 | | * |
327 | | * <pre> |
328 | | * Notes: |
329 | | * (1) This does a fast serialization of the principal elements |
330 | | * of the pix, as follows: |
331 | | * "spix" (4 bytes) -- ID for file type |
332 | | * w (4 bytes) |
333 | | * h (4 bytes) |
334 | | * d (4 bytes) |
335 | | * wpl (4 bytes) |
336 | | * ncolors (4 bytes) -- in colormap; 0 if there is no colormap |
337 | | * cdata (4 * ncolors) -- size of serialized colormap array |
338 | | * rdatasize (4 bytes) -- size of serialized raster data |
339 | | * = 4 * wpl * h |
340 | | * rdata (rdatasize) |
341 | | * </pre> |
342 | | */ |
343 | | l_ok |
344 | | pixSerializeToMemory(PIX *pixs, |
345 | | l_uint32 **pdata, |
346 | | size_t *pnbytes) |
347 | 0 | { |
348 | 0 | char *id; |
349 | 0 | l_int32 w, h, d, wpl, rdatasize, ncolors, nbytes, index, valid; |
350 | 0 | l_uint8 *cdata; /* data in colormap array (4 bytes/color table entry) */ |
351 | 0 | l_uint32 *data; |
352 | 0 | l_uint32 *rdata; /* data in pix raster */ |
353 | 0 | PIXCMAP *cmap; |
354 | |
|
355 | 0 | if (!pdata || !pnbytes) |
356 | 0 | return ERROR_INT("&data and &nbytes not both defined", __func__, 1); |
357 | 0 | *pdata = NULL; |
358 | 0 | *pnbytes = 0; |
359 | 0 | if (!pixs) |
360 | 0 | return ERROR_INT("pixs not defined", __func__, 1); |
361 | | |
362 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
363 | 0 | wpl = pixGetWpl(pixs); |
364 | 0 | rdata = pixGetData(pixs); |
365 | 0 | rdatasize = 4 * wpl * h; |
366 | 0 | ncolors = 0; |
367 | 0 | cdata = NULL; |
368 | 0 | if ((cmap = pixGetColormap(pixs)) != NULL) { |
369 | 0 | pixcmapIsValid(cmap, pixs, &valid); |
370 | 0 | if (!valid) |
371 | 0 | return ERROR_INT("colormap not valid", __func__, 1); |
372 | 0 | pixcmapSerializeToMemory(cmap, 4, &ncolors, &cdata); |
373 | 0 | } |
374 | | |
375 | 0 | nbytes = 24 + 4 * ncolors + 4 + rdatasize; |
376 | 0 | if ((data = (l_uint32 *)LEPT_CALLOC(nbytes / 4, sizeof(l_uint32))) |
377 | 0 | == NULL) { |
378 | 0 | LEPT_FREE(cdata); |
379 | 0 | return ERROR_INT("data not made", __func__, 1); |
380 | 0 | } |
381 | 0 | *pdata = data; |
382 | 0 | *pnbytes = nbytes; |
383 | 0 | id = (char *)data; |
384 | 0 | id[0] = 's'; |
385 | 0 | id[1] = 'p'; |
386 | 0 | id[2] = 'i'; |
387 | 0 | id[3] = 'x'; |
388 | 0 | data[1] = w; |
389 | 0 | data[2] = h; |
390 | 0 | data[3] = d; |
391 | 0 | data[4] = wpl; |
392 | 0 | data[5] = ncolors; |
393 | 0 | if (ncolors > 0) |
394 | 0 | memcpy(data + 6, cdata, 4 * ncolors); |
395 | 0 | index = 6 + ncolors; |
396 | 0 | data[index] = rdatasize; |
397 | 0 | memcpy(data + index + 1, rdata, rdatasize); |
398 | |
|
399 | | #if DEBUG_SERIALIZE |
400 | | lept_stderr("Serialize: " |
401 | | "raster size = %d, ncolors in cmap = %d, total bytes = %d\n", |
402 | | rdatasize, ncolors, nbytes); |
403 | | #endif /* DEBUG_SERIALIZE */ |
404 | |
|
405 | 0 | LEPT_FREE(cdata); |
406 | 0 | return 0; |
407 | 0 | } |
408 | | |
409 | | |
410 | | /*! |
411 | | * \brief pixDeserializeFromMemory() |
412 | | * |
413 | | * \param[in] data serialized data in memory |
414 | | * \param[in] nbytes number of bytes in data string |
415 | | * \return pix, or NULL on error |
416 | | * |
417 | | * <pre> |
418 | | * Notes: |
419 | | * (1) See pixSerializeToMemory() for the binary format. |
420 | | * (2) Note the image size limits. |
421 | | * </pre> |
422 | | */ |
423 | | PIX * |
424 | | pixDeserializeFromMemory(const l_uint32 *data, |
425 | | size_t nbytes) |
426 | 1.61k | { |
427 | 1.61k | char *id; |
428 | 1.61k | l_int32 w, h, d, pixdata_size, memdata_size, imdata_size, ncolors, valid; |
429 | 1.61k | l_uint32 *imdata; /* data in pix raster */ |
430 | 1.61k | PIX *pix1, *pixd; |
431 | 1.61k | PIXCMAP *cmap = NULL; |
432 | | |
433 | 1.61k | if (!data) |
434 | 0 | return (PIX *)ERROR_PTR("data not defined", __func__, NULL); |
435 | 1.61k | if (nbytes < 28 || nbytes > ((1LL << 31) - 1)) { |
436 | 10 | L_ERROR("invalid nbytes = %zu\n", __func__, nbytes); |
437 | 10 | return NULL; |
438 | 10 | } |
439 | | |
440 | 1.60k | id = (char *)data; |
441 | 1.60k | if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x') |
442 | 56 | return (PIX *)ERROR_PTR("invalid id string", __func__, NULL); |
443 | 1.54k | w = data[1]; |
444 | 1.54k | h = data[2]; |
445 | 1.54k | d = data[3]; |
446 | 1.54k | ncolors = data[5]; |
447 | | |
448 | | /* Sanity checks on the amount of image data */ |
449 | 1.54k | if (w < 1 || w > MaxAllowedWidth) |
450 | 52 | return (PIX *)ERROR_PTR("invalid width", __func__, NULL); |
451 | 1.49k | if (h < 1 || h > MaxAllowedHeight) |
452 | 57 | return (PIX *)ERROR_PTR("invalid height", __func__, NULL); |
453 | 1.43k | if (1LL * w * h > MaxAllowedArea) |
454 | 27 | return (PIX *)ERROR_PTR("area too large", __func__, NULL); |
455 | 1.41k | if (ncolors < 0 || ncolors > 256 || ncolors + 7 >= nbytes/sizeof(l_int32)) |
456 | 92 | return (PIX *)ERROR_PTR("invalid ncolors", __func__, NULL); |
457 | 1.32k | if ((pix1 = pixCreateHeader(w, h, d)) == NULL) /* just make the header */ |
458 | 116 | return (PIX *)ERROR_PTR("failed to make header", __func__, NULL); |
459 | 1.20k | pixdata_size = 4 * h * pixGetWpl(pix1); |
460 | 1.20k | memdata_size = nbytes - 24 - 4 * ncolors - 4; |
461 | 1.20k | imdata_size = data[6 + ncolors]; |
462 | 1.20k | pixDestroy(&pix1); |
463 | 1.20k | if (pixdata_size != memdata_size || pixdata_size != imdata_size) { |
464 | 125 | L_ERROR("pixdata_size = %d, memdata_size = %d, imdata_size = %d " |
465 | 125 | "not all equal!\n", __func__, pixdata_size, memdata_size, |
466 | 125 | imdata_size); |
467 | 125 | return NULL; |
468 | 125 | } |
469 | | |
470 | 1.07k | if ((pixd = pixCreate(w, h, d)) == NULL) |
471 | 0 | return (PIX *)ERROR_PTR("pix not made", __func__, NULL); |
472 | 1.07k | if (ncolors > 0) { |
473 | 223 | cmap = pixcmapDeserializeFromMemory((l_uint8 *)(&data[6]), 4, ncolors); |
474 | 223 | if (!cmap) { |
475 | 0 | pixDestroy(&pixd); |
476 | 0 | return (PIX *)ERROR_PTR("cmap not made", __func__, NULL); |
477 | 0 | } |
478 | 223 | if (pixSetColormap(pixd, cmap)) { |
479 | 0 | pixDestroy(&pixd); |
480 | 0 | return (PIX *)ERROR_PTR("cmap is not valid", __func__, NULL); |
481 | 0 | } |
482 | 223 | } |
483 | | |
484 | | /* Read the raster data */ |
485 | 1.07k | imdata = pixGetData(pixd); |
486 | 1.07k | memcpy(imdata, data + 7 + ncolors, imdata_size); |
487 | | |
488 | | /* Verify that the colormap is valid with the pix */ |
489 | 1.07k | if (ncolors > 0) { |
490 | 223 | pixcmapIsValid(cmap, pixd, &valid); |
491 | 223 | if (!valid) { |
492 | 149 | pixDestroy(&pixd); |
493 | 149 | return (PIX *)ERROR_PTR("cmap is invalid with pix", __func__, NULL); |
494 | 149 | } |
495 | 223 | } |
496 | | |
497 | | #if DEBUG_SERIALIZE |
498 | | lept_stderr("Deserialize: " |
499 | | "raster size = %d, ncolors in cmap = %d, total bytes = %zu\n", |
500 | | imdata_size, ncolors, nbytes); |
501 | | #endif /* DEBUG_SERIALIZE */ |
502 | | |
503 | 930 | return pixd; |
504 | 1.07k | } |