Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
