comparison mupdf-source/thirdparty/leptonica/src/spixio.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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 {
94 size_t nbytes;
95 l_uint8 *data;
96 PIX *pix;
97
98 if (!fp)
99 return (PIX *)ERROR_PTR("stream not defined", __func__, NULL);
100
101 if ((data = l_binaryReadStream(fp, &nbytes)) == NULL)
102 return (PIX *)ERROR_PTR("data not read", __func__, NULL);
103 pix = pixReadMemSpix(data, nbytes);
104 LEPT_FREE(data);
105 if (!pix)
106 return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
107 return pix;
108 }
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 {
135 l_int32 ret;
136 FILE *fp;
137
138 if (!filename)
139 return ERROR_INT("filename not defined", __func__, 1);
140 if (!pwidth || !pheight || !pbps || !pspp)
141 return ERROR_INT("input ptr(s) not defined", __func__, 1);
142 if ((fp = fopenReadStream(filename)) == NULL)
143 return ERROR_INT_1("image file not found", filename, __func__, 1);
144 ret = freadHeaderSpix(fp, pwidth, pheight, pbps, pspp, piscmap);
145 fclose(fp);
146 return ret;
147 }
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 {
174 l_int32 nbytes, ret;
175 l_uint32 data[6];
176
177 if (!fp)
178 return ERROR_INT("stream not defined", __func__, 1);
179 if (!pwidth || !pheight || !pbps || !pspp)
180 return ERROR_INT("input ptr(s) not defined", __func__, 1);
181
182 nbytes = fnbytesInFile(fp);
183 if (nbytes < 32)
184 return ERROR_INT("file too small to be spix", __func__, 1);
185 if (fread(data, 4, 6, fp) != 6)
186 return ERROR_INT("error reading data", __func__, 1);
187 ret = sreadHeaderSpix(data, nbytes, pwidth, pheight, pbps, pspp, piscmap);
188 return ret;
189 }
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 {
218 char *id;
219 l_int32 d, ncolors;
220
221 if (!data)
222 return ERROR_INT("data not defined", __func__, 1);
223 if (!pwidth || !pheight || !pbps || !pspp)
224 return ERROR_INT("input ptr(s) not defined", __func__, 1);
225 *pwidth = *pheight = *pbps = *pspp = 0;
226 if (piscmap)
227 *piscmap = 0;
228 if (size < 28)
229 return ERROR_INT("size too small", __func__, 1);
230
231 /* Check file id */
232 id = (char *)data;
233 if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x')
234 return ERROR_INT("not a valid spix file", __func__, 1);
235
236 *pwidth = data[1];
237 *pheight = data[2];
238 d = data[3];
239 if (d <= 16) {
240 *pbps = d;
241 *pspp = 1;
242 } else {
243 *pbps = 8;
244 *pspp = d / 8; /* if the pix is 32 bpp, call it 4 samples */
245 }
246 ncolors = data[5];
247 if (piscmap)
248 *piscmap = (ncolors == 0) ? 0 : 1;
249
250 return 0;
251 }
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 {
268 l_uint8 *data;
269 size_t size;
270
271 if (!fp)
272 return ERROR_INT("stream not defined", __func__, 1);
273 if (!pix)
274 return ERROR_INT("pix not defined", __func__, 1);
275
276 if (pixWriteMemSpix(&data, &size, pix))
277 return ERROR_INT("failure to write pix to memory", __func__, 1);
278 fwrite(data, 1, size, fp);
279 LEPT_FREE(data);
280 return 0;
281 }
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 {
298 return pixDeserializeFromMemory((l_uint32 *)data, size);
299 }
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 {
315 return pixSerializeToMemory(pix, (l_uint32 **)pdata, psize);
316 }
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 {
348 char *id;
349 l_int32 w, h, d, wpl, rdatasize, ncolors, nbytes, index, valid;
350 l_uint8 *cdata; /* data in colormap array (4 bytes/color table entry) */
351 l_uint32 *data;
352 l_uint32 *rdata; /* data in pix raster */
353 PIXCMAP *cmap;
354
355 if (!pdata || !pnbytes)
356 return ERROR_INT("&data and &nbytes not both defined", __func__, 1);
357 *pdata = NULL;
358 *pnbytes = 0;
359 if (!pixs)
360 return ERROR_INT("pixs not defined", __func__, 1);
361
362 pixGetDimensions(pixs, &w, &h, &d);
363 wpl = pixGetWpl(pixs);
364 rdata = pixGetData(pixs);
365 rdatasize = 4 * wpl * h;
366 ncolors = 0;
367 cdata = NULL;
368 if ((cmap = pixGetColormap(pixs)) != NULL) {
369 pixcmapIsValid(cmap, pixs, &valid);
370 if (!valid)
371 return ERROR_INT("colormap not valid", __func__, 1);
372 pixcmapSerializeToMemory(cmap, 4, &ncolors, &cdata);
373 }
374
375 nbytes = 24 + 4 * ncolors + 4 + rdatasize;
376 if ((data = (l_uint32 *)LEPT_CALLOC(nbytes / 4, sizeof(l_uint32)))
377 == NULL) {
378 LEPT_FREE(cdata);
379 return ERROR_INT("data not made", __func__, 1);
380 }
381 *pdata = data;
382 *pnbytes = nbytes;
383 id = (char *)data;
384 id[0] = 's';
385 id[1] = 'p';
386 id[2] = 'i';
387 id[3] = 'x';
388 data[1] = w;
389 data[2] = h;
390 data[3] = d;
391 data[4] = wpl;
392 data[5] = ncolors;
393 if (ncolors > 0)
394 memcpy(data + 6, cdata, 4 * ncolors);
395 index = 6 + ncolors;
396 data[index] = rdatasize;
397 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 LEPT_FREE(cdata);
406 return 0;
407 }
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 {
427 char *id;
428 l_int32 w, h, d, pixdata_size, memdata_size, imdata_size, ncolors, valid;
429 l_uint32 *imdata; /* data in pix raster */
430 PIX *pix1, *pixd;
431 PIXCMAP *cmap = NULL;
432
433 if (!data)
434 return (PIX *)ERROR_PTR("data not defined", __func__, NULL);
435 if (nbytes < 28 || nbytes > ((1LL << 31) - 1)) {
436 L_ERROR("invalid nbytes = %zu\n", __func__, nbytes);
437 return NULL;
438 }
439
440 id = (char *)data;
441 if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x')
442 return (PIX *)ERROR_PTR("invalid id string", __func__, NULL);
443 w = data[1];
444 h = data[2];
445 d = data[3];
446 ncolors = data[5];
447
448 /* Sanity checks on the amount of image data */
449 if (w < 1 || w > MaxAllowedWidth)
450 return (PIX *)ERROR_PTR("invalid width", __func__, NULL);
451 if (h < 1 || h > MaxAllowedHeight)
452 return (PIX *)ERROR_PTR("invalid height", __func__, NULL);
453 if (1LL * w * h > MaxAllowedArea)
454 return (PIX *)ERROR_PTR("area too large", __func__, NULL);
455 if (ncolors < 0 || ncolors > 256 || ncolors + 7 >= nbytes/sizeof(l_int32))
456 return (PIX *)ERROR_PTR("invalid ncolors", __func__, NULL);
457 if ((pix1 = pixCreateHeader(w, h, d)) == NULL) /* just make the header */
458 return (PIX *)ERROR_PTR("failed to make header", __func__, NULL);
459 pixdata_size = 4 * h * pixGetWpl(pix1);
460 memdata_size = nbytes - 24 - 4 * ncolors - 4;
461 imdata_size = data[6 + ncolors];
462 pixDestroy(&pix1);
463 if (pixdata_size != memdata_size || pixdata_size != imdata_size) {
464 L_ERROR("pixdata_size = %d, memdata_size = %d, imdata_size = %d "
465 "not all equal!\n", __func__, pixdata_size, memdata_size,
466 imdata_size);
467 return NULL;
468 }
469
470 if ((pixd = pixCreate(w, h, d)) == NULL)
471 return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
472 if (ncolors > 0) {
473 cmap = pixcmapDeserializeFromMemory((l_uint8 *)(&data[6]), 4, ncolors);
474 if (!cmap) {
475 pixDestroy(&pixd);
476 return (PIX *)ERROR_PTR("cmap not made", __func__, NULL);
477 }
478 if (pixSetColormap(pixd, cmap)) {
479 pixDestroy(&pixd);
480 return (PIX *)ERROR_PTR("cmap is not valid", __func__, NULL);
481 }
482 }
483
484 /* Read the raster data */
485 imdata = pixGetData(pixd);
486 memcpy(imdata, data + 7 + ncolors, imdata_size);
487
488 /* Verify that the colormap is valid with the pix */
489 if (ncolors > 0) {
490 pixcmapIsValid(cmap, pixd, &valid);
491 if (!valid) {
492 pixDestroy(&pixd);
493 return (PIX *)ERROR_PTR("cmap is invalid with pix", __func__, NULL);
494 }
495 }
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 return pixd;
504 }