Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/gifio.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 gifio.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * Reading gif | |
| 32 * PIX *pixReadStreamGif() | |
| 33 * PIX *pixReadMemGif() | |
| 34 * static l_int32 gifReadFunc() | |
| 35 * static PIX *gifToPix() | |
| 36 * | |
| 37 * Writing gif | |
| 38 * l_int32 pixWriteStreamGif() | |
| 39 * l_int32 pixWriteMemGif() | |
| 40 * static l_int32 gifWriteFunc() | |
| 41 * static l_int32 pixToGif() | |
| 42 * | |
| 43 * The initial version of this module was generously contribued by | |
| 44 * Antony Dovgal. | |
| 45 * | |
| 46 * The functions that read and write from pix to gif-compressed memory, | |
| 47 * using gif internal functions DGifOpen() and EGifOpen() that are | |
| 48 * available in 5.1 and later, were contributed by Tobias Peirick. | |
| 49 * | |
| 50 * Version information: | |
| 51 * | |
| 52 * (1) This supports the gif library, version 5.1 or later, for which | |
| 53 * gif read-from-mem and write-to-mem allow these operations | |
| 54 * without writing temporary files. | |
| 55 * (2) There has never been a gif stream interface. For versions | |
| 56 * before 5.1, it was necessary to use a file descriptor, and to | |
| 57 * generate a file stream from the low-level descriptor. With the | |
| 58 * memory interface in 5.1 that can be used on all platforms, it | |
| 59 * is no longer necessary to use any API code with file descriptors. | |
| 60 * (3) The public interface changed with 5.0 and with 5.1, and we | |
| 61 * no longer support 4.6.1 and 5.0. | |
| 62 * (4) Version 5.1.2 came out on Jan 7, 2016. Leptonica cannot | |
| 63 * successfully read gif files that it writes with this version; | |
| 64 * DGifSlurp() gets an internal error from an uninitialized array | |
| 65 * and returns failure. The problem was fixed in 5.1.3. | |
| 66 * | |
| 67 * Limitations: | |
| 68 * | |
| 69 * (1) We do not support animated gif. If the gif has more than one image, | |
| 70 * an error message is returned. | |
| 71 * | |
| 72 * </pre> | |
| 73 */ | |
| 74 | |
| 75 #ifdef HAVE_CONFIG_H | |
| 76 #include <config_auto.h> | |
| 77 #endif /* HAVE_CONFIG_H */ | |
| 78 | |
| 79 #include <string.h> | |
| 80 #include "allheaders.h" | |
| 81 | |
| 82 /* --------------------------------------------------------------------*/ | |
| 83 #if HAVE_LIBGIF || HAVE_LIBUNGIF /* defined in environ.h */ | |
| 84 /* --------------------------------------------------------------------*/ | |
| 85 | |
| 86 #include "gif_lib.h" | |
| 87 | |
| 88 /* Interface that enables low-level GIF support for reading from memory */ | |
| 89 static PIX * gifToPix(GifFileType *gif); | |
| 90 /* Interface that enables low-level GIF support for writing to memory */ | |
| 91 static l_int32 pixToGif(PIX *pix, GifFileType *gif); | |
| 92 | |
| 93 /*! For in-memory decoding of GIF; 5.1+ */ | |
| 94 typedef struct GifReadBuffer | |
| 95 { | |
| 96 size_t size; /*!< size of buffer */ | |
| 97 size_t pos; /*!< position relative to beginning of buffer */ | |
| 98 const l_uint8 *cdata; /*!< data in the buffer */ | |
| 99 } GifReadBuffer; | |
| 100 | |
| 101 /*! Low-level callback for in-memory decoding */ | |
| 102 static l_int32 gifReadFunc(GifFileType *gif, GifByteType *dest, | |
| 103 l_int32 bytesToRead); | |
| 104 /*! Low-level callback for in-memory encoding */ | |
| 105 static l_int32 gifWriteFunc(GifFileType *gif, const GifByteType *src, | |
| 106 l_int32 bytesToWrite); | |
| 107 | |
| 108 | |
| 109 /*---------------------------------------------------------------------* | |
| 110 * Reading gif * | |
| 111 *---------------------------------------------------------------------*/ | |
| 112 /*! | |
| 113 * \brief pixReadStreamGif() | |
| 114 * | |
| 115 * \param[in] fp file stream opened for reading | |
| 116 * \return pix, or NULL on error | |
| 117 */ | |
| 118 PIX * | |
| 119 pixReadStreamGif(FILE *fp) | |
| 120 { | |
| 121 l_uint8 *filedata; | |
| 122 size_t filesize; | |
| 123 PIX *pix; | |
| 124 | |
| 125 if (!fp) | |
| 126 return (PIX *)ERROR_PTR("fp not defined", __func__, NULL); | |
| 127 | |
| 128 /* Read data into memory from file */ | |
| 129 rewind(fp); | |
| 130 if ((filedata = l_binaryReadStream(fp, &filesize)) == NULL) | |
| 131 return (PIX *)ERROR_PTR("filedata not read", __func__, NULL); | |
| 132 | |
| 133 /* Uncompress from memory */ | |
| 134 pix = pixReadMemGif(filedata, filesize); | |
| 135 LEPT_FREE(filedata); | |
| 136 if (!pix) | |
| 137 L_ERROR("failed to read gif from file data\n", __func__); | |
| 138 return pix; | |
| 139 } | |
| 140 | |
| 141 | |
| 142 /*! | |
| 143 * \brief pixReadMemGif() | |
| 144 * | |
| 145 * \param[in] cdata const; gif-encoded | |
| 146 * \param[in] size bytes data | |
| 147 * \return pix, or NULL on error | |
| 148 * | |
| 149 * <pre> | |
| 150 * Notes: | |
| 151 * (1) For libgif version >= 5.1, this uses the DGifOpen() buffer | |
| 152 * interface. No temp files are required. | |
| 153 * (2) For libgif version < 5.1, it was necessary to write the compressed | |
| 154 * data to file and read it back, and we couldn't use the GNU | |
| 155 * runtime extension fmemopen() because libgif doesn't have a file | |
| 156 * stream interface. | |
| 157 * </pre> | |
| 158 */ | |
| 159 PIX * | |
| 160 pixReadMemGif(const l_uint8 *cdata, | |
| 161 size_t size) | |
| 162 { | |
| 163 GifFileType *gif; | |
| 164 GifReadBuffer buffer; | |
| 165 | |
| 166 /* 5.1+ and not 5.1.2 */ | |
| 167 #if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)) | |
| 168 L_ERROR("Require giflib-5.1 or later\n", __func__); | |
| 169 return NULL; | |
| 170 #endif /* < 5.1 */ | |
| 171 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */ | |
| 172 L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__); | |
| 173 return NULL; | |
| 174 #endif /* 5.1.2 */ | |
| 175 | |
| 176 if (!cdata) | |
| 177 return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL); | |
| 178 | |
| 179 buffer.cdata = cdata; | |
| 180 buffer.size = size; | |
| 181 buffer.pos = 0; | |
| 182 if ((gif = DGifOpen((void*)&buffer, gifReadFunc, NULL)) == NULL) | |
| 183 return (PIX *)ERROR_PTR("could not open gif stream from memory", | |
| 184 __func__, NULL); | |
| 185 | |
| 186 return gifToPix(gif); | |
| 187 } | |
| 188 | |
| 189 | |
| 190 static l_int32 | |
| 191 gifReadFunc(GifFileType *gif, | |
| 192 GifByteType *dest, | |
| 193 l_int32 bytesToRead) | |
| 194 { | |
| 195 GifReadBuffer *buffer; | |
| 196 l_int32 bytesRead; | |
| 197 | |
| 198 if ((buffer = (GifReadBuffer*)gif->UserData) == NULL) | |
| 199 return ERROR_INT("UserData not set", __func__, -1); | |
| 200 | |
| 201 if(buffer->pos >= buffer->size || bytesToRead > buffer->size) | |
| 202 return -1; | |
| 203 | |
| 204 bytesRead = (buffer->pos < buffer->size - bytesToRead) | |
| 205 ? bytesToRead : buffer->size - buffer->pos; | |
| 206 memcpy(dest, buffer->cdata + buffer->pos, bytesRead); | |
| 207 buffer->pos += bytesRead; | |
| 208 return bytesRead; | |
| 209 } | |
| 210 | |
| 211 | |
| 212 /*! | |
| 213 * \brief gifToPix() | |
| 214 * | |
| 215 * \param[in] gif opened gif stream | |
| 216 * \return pix, or NULL on error | |
| 217 * | |
| 218 * <pre> | |
| 219 * Notes: | |
| 220 * (1) This decodes the pix from the compressed gif stream and | |
| 221 * closes the stream. | |
| 222 * (2) It is static so that the stream is not exposed to clients. | |
| 223 * (3) Leptonica does not support gifanim (more than 1 image in the file). | |
| 224 * </pre> | |
| 225 */ | |
| 226 static PIX * | |
| 227 gifToPix(GifFileType *gif) | |
| 228 { | |
| 229 l_int32 wpl, i, j, w, h, d, cindex, ncolors, valid, nimages; | |
| 230 l_int32 rval, gval, bval; | |
| 231 l_uint32 *data, *line; | |
| 232 PIX *pixd; | |
| 233 PIXCMAP *cmap; | |
| 234 ColorMapObject *gif_cmap; | |
| 235 SavedImage si; | |
| 236 int giferr; | |
| 237 | |
| 238 /* Read all the data, but use only the first image found */ | |
| 239 if (DGifSlurp(gif) != GIF_OK) { | |
| 240 DGifCloseFile(gif, &giferr); | |
| 241 return (PIX *)ERROR_PTR("failed to read GIF data", __func__, NULL); | |
| 242 } | |
| 243 | |
| 244 if (gif->SavedImages == NULL) { | |
| 245 DGifCloseFile(gif, &giferr); | |
| 246 return (PIX *)ERROR_PTR("no images found in GIF", __func__, NULL); | |
| 247 } | |
| 248 | |
| 249 nimages = gif->ImageCount; | |
| 250 if (nimages > 1) { | |
| 251 DGifCloseFile(gif, &giferr); | |
| 252 L_ERROR("There are %d images in the file; gifanim is not supported\n", | |
| 253 __func__, nimages); | |
| 254 return NULL; | |
| 255 } | |
| 256 | |
| 257 si = gif->SavedImages[0]; | |
| 258 w = si.ImageDesc.Width; | |
| 259 h = si.ImageDesc.Height; | |
| 260 if (w <= 0 || h <= 0) { | |
| 261 DGifCloseFile(gif, &giferr); | |
| 262 return (PIX *)ERROR_PTR("invalid image dimensions", __func__, NULL); | |
| 263 } | |
| 264 | |
| 265 if (si.RasterBits == NULL) { | |
| 266 DGifCloseFile(gif, &giferr); | |
| 267 return (PIX *)ERROR_PTR("no raster data in GIF", __func__, NULL); | |
| 268 } | |
| 269 | |
| 270 if (si.ImageDesc.ColorMap) { | |
| 271 /* private cmap for this image */ | |
| 272 gif_cmap = si.ImageDesc.ColorMap; | |
| 273 } else if (gif->SColorMap) { | |
| 274 /* global cmap for whole picture */ | |
| 275 gif_cmap = gif->SColorMap; | |
| 276 } else { | |
| 277 /* don't know where to take cmap from */ | |
| 278 DGifCloseFile(gif, &giferr); | |
| 279 return (PIX *)ERROR_PTR("color map is missing", __func__, NULL); | |
| 280 } | |
| 281 | |
| 282 ncolors = gif_cmap->ColorCount; | |
| 283 if (ncolors <= 0 || ncolors > 256) { | |
| 284 DGifCloseFile(gif, &giferr); | |
| 285 return (PIX *)ERROR_PTR("ncolors is invalid", __func__, NULL); | |
| 286 } | |
| 287 if (ncolors <= 2) | |
| 288 d = 1; | |
| 289 else if (ncolors <= 4) | |
| 290 d = 2; | |
| 291 else if (ncolors <= 16) | |
| 292 d = 4; | |
| 293 else /* [17 ... 256] */ | |
| 294 d = 8; | |
| 295 cmap = pixcmapCreate(d); | |
| 296 for (cindex = 0; cindex < ncolors; cindex++) { | |
| 297 rval = gif_cmap->Colors[cindex].Red; | |
| 298 gval = gif_cmap->Colors[cindex].Green; | |
| 299 bval = gif_cmap->Colors[cindex].Blue; | |
| 300 pixcmapAddColor(cmap, rval, gval, bval); | |
| 301 } | |
| 302 | |
| 303 if ((pixd = pixCreate(w, h, d)) == NULL) { | |
| 304 DGifCloseFile(gif, &giferr); | |
| 305 pixcmapDestroy(&cmap); | |
| 306 return (PIX *)ERROR_PTR("failed to allocate pixd", __func__, NULL); | |
| 307 } | |
| 308 pixSetInputFormat(pixd, IFF_GIF); | |
| 309 pixSetColormap(pixd, cmap); | |
| 310 pixcmapIsValid(cmap, pixd, &valid); | |
| 311 if (!valid) { | |
| 312 DGifCloseFile(gif, &giferr); | |
| 313 pixDestroy(&pixd); | |
| 314 pixcmapDestroy(&cmap); | |
| 315 return (PIX *)ERROR_PTR("colormap is invalid", __func__, NULL); | |
| 316 } | |
| 317 | |
| 318 wpl = pixGetWpl(pixd); | |
| 319 data = pixGetData(pixd); | |
| 320 for (i = 0; i < h; i++) { | |
| 321 line = data + i * wpl; | |
| 322 if (d == 1) { | |
| 323 for (j = 0; j < w; j++) { | |
| 324 if (si.RasterBits[i * w + j]) | |
| 325 SET_DATA_BIT(line, j); | |
| 326 } | |
| 327 } else if (d == 2) { | |
| 328 for (j = 0; j < w; j++) | |
| 329 SET_DATA_DIBIT(line, j, si.RasterBits[i * w + j]); | |
| 330 } else if (d == 4) { | |
| 331 for (j = 0; j < w; j++) | |
| 332 SET_DATA_QBIT(line, j, si.RasterBits[i * w + j]); | |
| 333 } else { /* d == 8 */ | |
| 334 for (j = 0; j < w; j++) | |
| 335 SET_DATA_BYTE(line, j, si.RasterBits[i * w + j]); | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 /* Versions before 5.0 required un-interlacing to restore | |
| 340 * the raster lines to normal order if the image | |
| 341 * had been interlaced (for viewing in a browser): | |
| 342 if (gif->Image.Interlace) { | |
| 343 PIX *pixdi = pixUninterlaceGIF(pixd); | |
| 344 pixTransferAllData(pixd, &pixdi, 0, 0); | |
| 345 } | |
| 346 * This is no longer required. */ | |
| 347 | |
| 348 DGifCloseFile(gif, &giferr); | |
| 349 return pixd; | |
| 350 } | |
| 351 | |
| 352 | |
| 353 /*---------------------------------------------------------------------* | |
| 354 * Writing gif * | |
| 355 *---------------------------------------------------------------------*/ | |
| 356 /*! | |
| 357 * \brief pixWriteStreamGif() | |
| 358 * | |
| 359 * \param[in] fp file stream opened for writing | |
| 360 * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp | |
| 361 * \return 0 if OK, 1 on error | |
| 362 * | |
| 363 * <pre> | |
| 364 * Notes: | |
| 365 * (1) All output gif have colormaps. If the pix is 32 bpp rgb, | |
| 366 * this quantizes the colors and writes out 8 bpp. | |
| 367 * If the pix is 16 bpp grayscale, it converts to 8 bpp first. | |
| 368 * </pre> | |
| 369 */ | |
| 370 l_ok | |
| 371 pixWriteStreamGif(FILE *fp, | |
| 372 PIX *pix) | |
| 373 { | |
| 374 l_uint8 *filedata; | |
| 375 size_t filebytes, nbytes; | |
| 376 | |
| 377 if (!fp) | |
| 378 return ERROR_INT("stream not open", __func__, 1); | |
| 379 if (!pix) | |
| 380 return ERROR_INT("pix not defined", __func__, 1); | |
| 381 | |
| 382 pixSetPadBits(pix, 0); | |
| 383 if (pixWriteMemGif(&filedata, &filebytes, pix) != 0) { | |
| 384 LEPT_FREE(filedata); | |
| 385 return ERROR_INT("failure to gif encode pix", __func__, 1); | |
| 386 } | |
| 387 | |
| 388 rewind(fp); | |
| 389 nbytes = fwrite(filedata, 1, filebytes, fp); | |
| 390 LEPT_FREE(filedata); | |
| 391 if (nbytes != filebytes) | |
| 392 return ERROR_INT("write error", __func__, 1); | |
| 393 return 0; | |
| 394 } | |
| 395 | |
| 396 | |
| 397 /*! | |
| 398 * \brief pixWriteMemGif() | |
| 399 * | |
| 400 * \param[out] pdata data of gif compressed image | |
| 401 * \param[out] psize size of returned data | |
| 402 * \param[in] pix | |
| 403 * \return 0 if OK, 1 on error | |
| 404 * | |
| 405 * <pre> | |
| 406 * Notes: | |
| 407 * (1) See comments in pixReadMemGif() | |
| 408 * </pre> | |
| 409 */ | |
| 410 l_ok | |
| 411 pixWriteMemGif(l_uint8 **pdata, | |
| 412 size_t *psize, | |
| 413 PIX *pix) | |
| 414 { | |
| 415 int giferr; | |
| 416 l_int32 result; | |
| 417 GifFileType *gif; | |
| 418 L_BBUFFER *buffer; | |
| 419 | |
| 420 /* 5.1+ and not 5.1.2 */ | |
| 421 #if (GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0)) | |
| 422 L_ERROR("Require giflib-5.1 or later\n", __func__); | |
| 423 return 1; | |
| 424 #endif /* < 5.1 */ | |
| 425 #if GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 1 && GIFLIB_RELEASE == 2 /* 5.1.2 */ | |
| 426 L_ERROR("Can't use giflib-5.1.2; suggest 5.1.3 or later\n", __func__); | |
| 427 return 1; | |
| 428 #endif /* 5.1.2 */ | |
| 429 | |
| 430 if (!pdata) | |
| 431 return ERROR_INT("&data not defined", __func__, 1 ); | |
| 432 *pdata = NULL; | |
| 433 if (!psize) | |
| 434 return ERROR_INT("&size not defined", __func__, 1 ); | |
| 435 *psize = 0; | |
| 436 if (!pix) | |
| 437 return ERROR_INT("&pix not defined", __func__, 1 ); | |
| 438 | |
| 439 if ((buffer = bbufferCreate(NULL, 0)) == NULL) | |
| 440 return ERROR_INT("failed to create buffer", __func__, 1); | |
| 441 | |
| 442 if ((gif = EGifOpen((void*)buffer, gifWriteFunc, NULL)) == NULL) { | |
| 443 bbufferDestroy(&buffer); | |
| 444 return ERROR_INT("failed to create GIF image handle", __func__, 1); | |
| 445 } | |
| 446 | |
| 447 result = pixToGif(pix, gif); | |
| 448 EGifCloseFile(gif, &giferr); | |
| 449 | |
| 450 if (result == 0) { | |
| 451 *pdata = bbufferDestroyAndSaveData(&buffer, psize); | |
| 452 } else { | |
| 453 bbufferDestroy(&buffer); | |
| 454 } | |
| 455 return result; | |
| 456 } | |
| 457 | |
| 458 | |
| 459 static l_int32 | |
| 460 gifWriteFunc(GifFileType *gif, | |
| 461 const GifByteType *src, | |
| 462 l_int32 bytesToWrite) | |
| 463 { | |
| 464 L_BBUFFER *buffer; | |
| 465 | |
| 466 if ((buffer = (L_BBUFFER*)gif->UserData) == NULL) | |
| 467 return ERROR_INT("UserData not set", __func__, -1); | |
| 468 | |
| 469 if(bbufferRead(buffer, (l_uint8*)src, bytesToWrite) == 0) | |
| 470 return bytesToWrite; | |
| 471 return 0; | |
| 472 } | |
| 473 | |
| 474 | |
| 475 /*! | |
| 476 * \brief pixToGif() | |
| 477 * | |
| 478 * \param[in] pix 1, 2, 4, 8, 16 or 32 bpp | |
| 479 * \param[in] gif opened gif stream | |
| 480 * \return 0 if OK, 1 on error | |
| 481 * | |
| 482 * <pre> | |
| 483 * Notes: | |
| 484 * (1) This encodes the pix to the gif stream. The stream is not | |
| 485 * closed by this function. | |
| 486 * (2) It is static to make this function private. | |
| 487 * </pre> | |
| 488 */ | |
| 489 static l_int32 | |
| 490 pixToGif(PIX *pix, | |
| 491 GifFileType *gif) | |
| 492 { | |
| 493 char *text; | |
| 494 l_int32 wpl, i, j, w, h, d, ncolor, rval, gval, bval, valid; | |
| 495 l_int32 gif_ncolor = 0; | |
| 496 l_uint32 *data, *line; | |
| 497 PIX *pixd; | |
| 498 PIXCMAP *cmap; | |
| 499 ColorMapObject *gif_cmap; | |
| 500 GifByteType *gif_line; | |
| 501 | |
| 502 if (!pix) | |
| 503 return ERROR_INT("pix not defined", __func__, 1); | |
| 504 if (!gif) | |
| 505 return ERROR_INT("gif not defined", __func__, 1); | |
| 506 | |
| 507 d = pixGetDepth(pix); | |
| 508 if (d == 32) { | |
| 509 pixd = pixConvertRGBToColormap(pix, 1); | |
| 510 } else if (d > 1) { | |
| 511 pixd = pixConvertTo8(pix, TRUE); | |
| 512 } else { /* d == 1; make sure there's a colormap */ | |
| 513 pixd = pixClone(pix); | |
| 514 if (!pixGetColormap(pixd)) { | |
| 515 cmap = pixcmapCreate(1); | |
| 516 pixcmapAddColor(cmap, 255, 255, 255); | |
| 517 pixcmapAddColor(cmap, 0, 0, 0); | |
| 518 pixSetColormap(pixd, cmap); | |
| 519 } | |
| 520 } | |
| 521 | |
| 522 if (!pixd) | |
| 523 return ERROR_INT("failed to convert to colormapped pix", __func__, 1); | |
| 524 d = pixGetDepth(pixd); | |
| 525 cmap = pixGetColormap(pixd); | |
| 526 if (!cmap) { | |
| 527 pixDestroy(&pixd); | |
| 528 return ERROR_INT("cmap is missing", __func__, 1); | |
| 529 } | |
| 530 pixcmapIsValid(cmap, pixd, &valid); | |
| 531 if (!valid) { | |
| 532 pixDestroy(&pixd); | |
| 533 return ERROR_INT("colormap is not valid", __func__, 1); | |
| 534 } | |
| 535 | |
| 536 /* 'Round' the number of gif colors up to a power of 2 */ | |
| 537 ncolor = pixcmapGetCount(cmap); | |
| 538 for (i = 0; i <= 8; i++) { | |
| 539 if ((1 << i) >= ncolor) { | |
| 540 gif_ncolor = (1 << i); | |
| 541 break; | |
| 542 } | |
| 543 } | |
| 544 if (gif_ncolor < 1) { | |
| 545 pixDestroy(&pixd); | |
| 546 return ERROR_INT("number of colors is invalid", __func__, 1); | |
| 547 } | |
| 548 | |
| 549 /* Save the cmap colors in a gif_cmap */ | |
| 550 if ((gif_cmap = GifMakeMapObject(gif_ncolor, NULL)) == NULL) { | |
| 551 pixDestroy(&pixd); | |
| 552 return ERROR_INT("failed to create GIF color map", __func__, 1); | |
| 553 } | |
| 554 for (i = 0; i < gif_ncolor; i++) { | |
| 555 rval = gval = bval = 0; | |
| 556 if (ncolor > 0) { | |
| 557 if (pixcmapGetColor(cmap, i, &rval, &gval, &bval) != 0) { | |
| 558 pixDestroy(&pixd); | |
| 559 GifFreeMapObject(gif_cmap); | |
| 560 return ERROR_INT("failed to get color from color map", | |
| 561 __func__, 1); | |
| 562 } | |
| 563 ncolor--; | |
| 564 } | |
| 565 gif_cmap->Colors[i].Red = rval; | |
| 566 gif_cmap->Colors[i].Green = gval; | |
| 567 gif_cmap->Colors[i].Blue = bval; | |
| 568 } | |
| 569 | |
| 570 pixGetDimensions(pixd, &w, &h, NULL); | |
| 571 if (EGifPutScreenDesc(gif, w, h, gif_cmap->BitsPerPixel, 0, gif_cmap) | |
| 572 != GIF_OK) { | |
| 573 pixDestroy(&pixd); | |
| 574 GifFreeMapObject(gif_cmap); | |
| 575 return ERROR_INT("failed to write screen description", __func__, 1); | |
| 576 } | |
| 577 GifFreeMapObject(gif_cmap); /* not needed after this point */ | |
| 578 | |
| 579 if (EGifPutImageDesc(gif, 0, 0, w, h, FALSE, NULL) != GIF_OK) { | |
| 580 pixDestroy(&pixd); | |
| 581 return ERROR_INT("failed to image screen description", __func__, 1); | |
| 582 } | |
| 583 | |
| 584 data = pixGetData(pixd); | |
| 585 wpl = pixGetWpl(pixd); | |
| 586 if (d != 1 && d != 2 && d != 4 && d != 8) { | |
| 587 pixDestroy(&pixd); | |
| 588 return ERROR_INT("image depth is not in {1, 2, 4, 8}", __func__, 1); | |
| 589 } | |
| 590 | |
| 591 if ((gif_line = (GifByteType *)LEPT_CALLOC(sizeof(GifByteType), w)) | |
| 592 == NULL) { | |
| 593 pixDestroy(&pixd); | |
| 594 return ERROR_INT("mem alloc fail for data line", __func__, 1); | |
| 595 } | |
| 596 | |
| 597 for (i = 0; i < h; i++) { | |
| 598 line = data + i * wpl; | |
| 599 /* Gif's way of setting the raster line up for compression */ | |
| 600 for (j = 0; j < w; j++) { | |
| 601 switch(d) | |
| 602 { | |
| 603 case 8: | |
| 604 gif_line[j] = GET_DATA_BYTE(line, j); | |
| 605 break; | |
| 606 case 4: | |
| 607 gif_line[j] = GET_DATA_QBIT(line, j); | |
| 608 break; | |
| 609 case 2: | |
| 610 gif_line[j] = GET_DATA_DIBIT(line, j); | |
| 611 break; | |
| 612 case 1: | |
| 613 gif_line[j] = GET_DATA_BIT(line, j); | |
| 614 break; | |
| 615 } | |
| 616 } | |
| 617 | |
| 618 /* Compress and save the line */ | |
| 619 if (EGifPutLine(gif, gif_line, w) != GIF_OK) { | |
| 620 LEPT_FREE(gif_line); | |
| 621 pixDestroy(&pixd); | |
| 622 return ERROR_INT("failed to write data line into GIF", __func__, 1); | |
| 623 } | |
| 624 } | |
| 625 | |
| 626 /* Write a text comment. This must be placed after writing the | |
| 627 * data (!!) Note that because libgif does not provide a function | |
| 628 * for reading comments from file, you will need another way | |
| 629 * to read comments. */ | |
| 630 if ((text = pixGetText(pix)) != NULL) { | |
| 631 if (EGifPutComment(gif, text) != GIF_OK) | |
| 632 L_WARNING("gif comment not written\n", __func__); | |
| 633 } | |
| 634 | |
| 635 LEPT_FREE(gif_line); | |
| 636 pixDestroy(&pixd); | |
| 637 return 0; | |
| 638 } | |
| 639 | |
| 640 | |
| 641 #if 0 | |
| 642 /*---------------------------------------------------------------------* | |
| 643 * Removing interlacing (reference only; not used) * | |
| 644 *---------------------------------------------------------------------*/ | |
| 645 /* GIF supports 4-way interlacing by raster lines. | |
| 646 * Before 5.0, it was necessary for leptonica to restore interlaced | |
| 647 * data to normal raster order when reading to a pix. With 5.0, | |
| 648 * the de-interlacing is done by the library read function. | |
| 649 * It is here only as a reference. */ | |
| 650 static const l_int32 InterlacedOffset[] = {0, 4, 2, 1}; | |
| 651 static const l_int32 InterlacedJumps[] = {8, 8, 4, 2}; | |
| 652 | |
| 653 static PIX * | |
| 654 pixUninterlaceGIF(PIX *pixs) | |
| 655 { | |
| 656 l_int32 w, h, d, wpl, j, k, srow, drow; | |
| 657 l_uint32 *datas, *datad, *lines, *lined; | |
| 658 PIX *pixd; | |
| 659 | |
| 660 if (!pixs) | |
| 661 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 662 | |
| 663 pixGetDimensions(pixs, &w, &h, &d); | |
| 664 wpl = pixGetWpl(pixs); | |
| 665 pixd = pixCreateTemplate(pixs); | |
| 666 datas = pixGetData(pixs); | |
| 667 datad = pixGetData(pixd); | |
| 668 for (k = 0, srow = 0; k < 4; k++) { | |
| 669 for (drow = InterlacedOffset[k]; drow < h; | |
| 670 drow += InterlacedJumps[k], srow++) { | |
| 671 lines = datas + srow * wpl; | |
| 672 lined = datad + drow * wpl; | |
| 673 for (j = 0; j < w; j++) | |
| 674 memcpy(lined, lines, 4 * wpl); | |
| 675 } | |
| 676 } | |
| 677 | |
| 678 return pixd; | |
| 679 } | |
| 680 #endif | |
| 681 | |
| 682 | |
| 683 /* -----------------------------------------------------------------*/ | |
| 684 #endif /* HAVE_LIBGIF || HAVE_LIBUNGIF */ |
