Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/zint/backend/gif.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 /* gif.c - Handles output to gif file */ | |
| 2 /* | |
| 3 libzint - the open source barcode library | |
| 4 Copyright (C) 2009-2024 Robin Stuart <rstuart114@gmail.com> | |
| 5 | |
| 6 Redistribution and use in source and binary forms, with or without | |
| 7 modification, are permitted provided that the following conditions | |
| 8 are met: | |
| 9 | |
| 10 1. Redistributions of source code must retain the above copyright | |
| 11 notice, this list of conditions and the following disclaimer. | |
| 12 2. Redistributions in binary form must reproduce the above copyright | |
| 13 notice, this list of conditions and the following disclaimer in the | |
| 14 documentation and/or other materials provided with the distribution. | |
| 15 3. Neither the name of the project nor the names of its contributors | |
| 16 may be used to endorse or promote products derived from this software | |
| 17 without specific prior written permission. | |
| 18 | |
| 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | |
| 20 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE | |
| 23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 25 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 26 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 28 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 29 SUCH DAMAGE. | |
| 30 */ | |
| 31 /* SPDX-License-Identifier: BSD-3-Clause */ | |
| 32 | |
| 33 #include <errno.h> | |
| 34 #include <stdio.h> | |
| 35 #include "common.h" | |
| 36 #include "filemem.h" | |
| 37 #include "output.h" | |
| 38 | |
| 39 /* Set LZW buffer paging size to this in expectation that compressed data will fit for typical scalings */ | |
| 40 #define GIF_LZW_PAGE_SIZE 0x100000 /* Megabyte */ | |
| 41 | |
| 42 struct gif_state { | |
| 43 struct filemem *fmp; | |
| 44 unsigned char *pOut; | |
| 45 const unsigned char *pIn; | |
| 46 const unsigned char *pInEnd; | |
| 47 size_t OutLength; | |
| 48 size_t OutPosCur; | |
| 49 size_t OutByteCountPos; | |
| 50 unsigned short ClearCode; | |
| 51 unsigned short FreeCode; | |
| 52 char fByteCountByteSet; | |
| 53 char fOutPaged; | |
| 54 unsigned char OutBitsFree; | |
| 55 unsigned short NodeAxon[4096]; | |
| 56 unsigned short NodeNext[4096]; | |
| 57 unsigned char NodePix[4096]; | |
| 58 unsigned char map[256]; | |
| 59 }; | |
| 60 | |
| 61 static void gif_BufferNextByte(struct gif_state *pState) { | |
| 62 (pState->OutPosCur)++; | |
| 63 if (pState->fOutPaged && pState->OutPosCur + 2 >= pState->OutLength) { | |
| 64 /* Keep last 256 bytes so `OutByteCountPos` within range */ | |
| 65 fm_write(pState->pOut, 1, pState->OutPosCur - 256, pState->fmp); | |
| 66 memmove(pState->pOut, pState->pOut + pState->OutPosCur - 256, 256); | |
| 67 pState->OutByteCountPos -= pState->OutPosCur - 256; | |
| 68 pState->OutPosCur = 256; | |
| 69 } | |
| 70 /* Check if this position is a byte count position | |
| 71 * `fByteCountByteSet` indicates, if byte count position bytes should be | |
| 72 * inserted in general. | |
| 73 * If this is true, and the distance to the last byte count position is 256 | |
| 74 * (e.g. 255 bytes in between), a byte count byte is inserted, and the value | |
| 75 * of the last one is set to 255. | |
| 76 * */ | |
| 77 if (pState->fByteCountByteSet && (pState->OutByteCountPos + 256 == pState->OutPosCur)) { | |
| 78 (pState->pOut)[pState->OutByteCountPos] = 255; | |
| 79 pState->OutByteCountPos = pState->OutPosCur; | |
| 80 (pState->OutPosCur)++; | |
| 81 } | |
| 82 | |
| 83 (pState->pOut)[pState->OutPosCur] = 0x00; | |
| 84 } | |
| 85 | |
| 86 static void gif_AddCodeToBuffer(struct gif_state *pState, unsigned short CodeIn, unsigned char CodeBits) { | |
| 87 /* Check, if we may fill up the current byte completely */ | |
| 88 if (CodeBits >= pState->OutBitsFree) { | |
| 89 (pState->pOut)[pState->OutPosCur] |= (unsigned char) (CodeIn << (8 - pState->OutBitsFree)); | |
| 90 gif_BufferNextByte(pState); | |
| 91 CodeIn = (unsigned short) (CodeIn >> pState->OutBitsFree); | |
| 92 CodeBits -= pState->OutBitsFree; | |
| 93 pState->OutBitsFree = 8; | |
| 94 /* Write a full byte if there are at least 8 code bits left */ | |
| 95 if (CodeBits >= pState->OutBitsFree) { | |
| 96 (pState->pOut)[pState->OutPosCur] = (unsigned char) CodeIn; | |
| 97 gif_BufferNextByte(pState); | |
| 98 CodeIn = (unsigned short) (CodeIn >> 8); | |
| 99 CodeBits -= 8; | |
| 100 } | |
| 101 } | |
| 102 /* The remaining bits of CodeIn fit in the current byte. */ | |
| 103 if (CodeBits > 0) { | |
| 104 (pState->pOut)[pState->OutPosCur] |= (unsigned char) (CodeIn << (8 - pState->OutBitsFree)); | |
| 105 pState->OutBitsFree -= CodeBits; | |
| 106 } | |
| 107 } | |
| 108 | |
| 109 static void gif_FlushStringTable(struct gif_state *pState) { | |
| 110 unsigned short Pos; | |
| 111 for (Pos = 0; Pos < pState->ClearCode; Pos++) { | |
| 112 (pState->NodeAxon)[Pos] = 0; | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 static unsigned short gif_FindPixelOutlet(struct gif_state *pState, unsigned short HeadNode, unsigned char Byte) { | |
| 117 unsigned short Outlet; | |
| 118 | |
| 119 Outlet = (pState->NodeAxon)[HeadNode]; | |
| 120 while (Outlet) { | |
| 121 if ((pState->NodePix)[Outlet] == Byte) | |
| 122 return Outlet; | |
| 123 Outlet = (pState->NodeNext)[Outlet]; | |
| 124 } | |
| 125 return 0; | |
| 126 } | |
| 127 | |
| 128 static int gif_NextCode(struct gif_state *pState, unsigned char *pPixelValueCur, unsigned char CodeBits) { | |
| 129 unsigned short UpNode; | |
| 130 unsigned short DownNode; | |
| 131 /* start with the root node for last pixel chain */ | |
| 132 UpNode = *pPixelValueCur; | |
| 133 if (pState->pIn == pState->pInEnd) { | |
| 134 gif_AddCodeToBuffer(pState, UpNode, CodeBits); | |
| 135 return 0; | |
| 136 } | |
| 137 *pPixelValueCur = pState->map[*pState->pIn++]; | |
| 138 /* Follow the string table and the data stream to the end of the longest string that has a code */ | |
| 139 while (0 != (DownNode = gif_FindPixelOutlet(pState, UpNode, *pPixelValueCur))) { | |
| 140 UpNode = DownNode; | |
| 141 if (pState->pIn == pState->pInEnd) { | |
| 142 gif_AddCodeToBuffer(pState, UpNode, CodeBits); | |
| 143 return 0; | |
| 144 } | |
| 145 *pPixelValueCur = pState->map[*pState->pIn++]; | |
| 146 } | |
| 147 /* Submit 'UpNode' which is the code of the longest string */ | |
| 148 gif_AddCodeToBuffer(pState, UpNode, CodeBits); | |
| 149 /* ... and extend the string by appending 'PixelValueCur' */ | |
| 150 /* Create a successor node for 'PixelValueCur' whose code is 'freecode' */ | |
| 151 (pState->NodePix)[pState->FreeCode] = *pPixelValueCur; | |
| 152 (pState->NodeAxon)[pState->FreeCode] = (pState->NodeNext)[pState->FreeCode] = 0; | |
| 153 /* ...and link it to the end of the chain emanating from fg_axon[UpNode]. */ | |
| 154 DownNode = (pState->NodeAxon)[UpNode]; | |
| 155 if (!DownNode) { | |
| 156 (pState->NodeAxon)[UpNode] = pState->FreeCode; | |
| 157 } else { | |
| 158 while ((pState->NodeNext)[DownNode]) { | |
| 159 DownNode = (pState->NodeNext)[DownNode]; | |
| 160 } | |
| 161 (pState->NodeNext)[DownNode] = pState->FreeCode; | |
| 162 } | |
| 163 return 1; | |
| 164 } | |
| 165 | |
| 166 static int gif_lzw(struct gif_state *pState, int paletteBitSize) { | |
| 167 unsigned char PixelValueCur; | |
| 168 unsigned char CodeBits; | |
| 169 unsigned short Pos; | |
| 170 | |
| 171 /* > Get first data byte */ | |
| 172 if (pState->pIn == pState->pInEnd) | |
| 173 return 0; | |
| 174 PixelValueCur = pState->map[*pState->pIn++]; | |
| 175 /* Number of bits per data item (=pixel) | |
| 176 * We need at least a value of 2, otherwise the cc and eoi code consumes | |
| 177 * the whole string table | |
| 178 */ | |
| 179 if (paletteBitSize == 1) | |
| 180 paletteBitSize = 2; | |
| 181 | |
| 182 /* initial size of compression codes */ | |
| 183 CodeBits = paletteBitSize + 1; | |
| 184 pState->ClearCode = (1 << paletteBitSize); | |
| 185 pState->FreeCode = pState->ClearCode + 2; | |
| 186 pState->OutBitsFree = 8; | |
| 187 pState->OutPosCur = 0; | |
| 188 pState->fByteCountByteSet = 0; | |
| 189 | |
| 190 for (Pos = 0; Pos < pState->ClearCode; Pos++) | |
| 191 (pState->NodePix)[Pos] = (unsigned char) Pos; | |
| 192 | |
| 193 gif_FlushStringTable(pState); | |
| 194 | |
| 195 /* Write what the GIF specification calls the "code size". */ | |
| 196 (pState->pOut)[pState->OutPosCur] = paletteBitSize; | |
| 197 /* Reserve first bytecount byte */ | |
| 198 gif_BufferNextByte(pState); | |
| 199 pState->OutByteCountPos = pState->OutPosCur; | |
| 200 gif_BufferNextByte(pState); | |
| 201 pState->fByteCountByteSet = 1; | |
| 202 /* Submit one 'ClearCode' as the first code */ | |
| 203 gif_AddCodeToBuffer(pState, pState->ClearCode, CodeBits); | |
| 204 | |
| 205 for (;;) { | |
| 206 /* generate and save the next code, which may consist of multiple input pixels. */ | |
| 207 if (!gif_NextCode(pState, &PixelValueCur, CodeBits)) { /* Check for end of data stream */ | |
| 208 /* submit 'eoi' as the last item of the code stream */ | |
| 209 gif_AddCodeToBuffer(pState, (unsigned short) (pState->ClearCode + 1), CodeBits); | |
| 210 pState->fByteCountByteSet = 0; | |
| 211 if (pState->OutBitsFree < 8) { | |
| 212 gif_BufferNextByte(pState); | |
| 213 } | |
| 214 /* > Update last bytecount byte; */ | |
| 215 if (pState->OutByteCountPos < pState->OutPosCur) { | |
| 216 (pState->pOut)[pState->OutByteCountPos] | |
| 217 = (unsigned char) (pState->OutPosCur - pState->OutByteCountPos - 1); | |
| 218 } | |
| 219 pState->OutPosCur++; | |
| 220 return 1; | |
| 221 } | |
| 222 /* Check for currently last code */ | |
| 223 if (pState->FreeCode == (1U << CodeBits)) | |
| 224 CodeBits++; | |
| 225 pState->FreeCode++; | |
| 226 /* Check for full stringtable - for widest compatibility with gif decoders, empty when 0xfff, not 0x1000 */ | |
| 227 if (pState->FreeCode == 0xfff) { | |
| 228 gif_FlushStringTable(pState); | |
| 229 gif_AddCodeToBuffer(pState, pState->ClearCode, CodeBits); | |
| 230 | |
| 231 CodeBits = (unsigned char) (1 + paletteBitSize); | |
| 232 pState->FreeCode = (unsigned short) (pState->ClearCode + 2); | |
| 233 } | |
| 234 } | |
| 235 } | |
| 236 | |
| 237 /* | |
| 238 * Called function to save in gif format | |
| 239 */ | |
| 240 INTERNAL int gif_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) { | |
| 241 struct filemem fm; | |
| 242 unsigned char outbuf[10]; | |
| 243 unsigned char paletteRGB[10][3]; | |
| 244 int paletteCount, i; | |
| 245 int paletteBitSize; | |
| 246 int paletteSize; | |
| 247 struct gif_state State; | |
| 248 int transparent_index; | |
| 249 int bgindex = -1, fgindex = -1; | |
| 250 | |
| 251 static const unsigned char RGBUnused[3] = {0,0,0}; | |
| 252 unsigned char RGBfg[3]; | |
| 253 unsigned char RGBbg[3]; | |
| 254 unsigned char fgalpha; | |
| 255 unsigned char bgalpha; | |
| 256 | |
| 257 const size_t bitmapSize = (size_t) symbol->bitmap_height * symbol->bitmap_width; | |
| 258 | |
| 259 (void) out_colour_get_rgb(symbol->fgcolour, &RGBfg[0], &RGBfg[1], &RGBfg[2], &fgalpha); | |
| 260 (void) out_colour_get_rgb(symbol->bgcolour, &RGBbg[0], &RGBbg[1], &RGBbg[2], &bgalpha); | |
| 261 | |
| 262 /* prepare state array */ | |
| 263 State.pIn = pixelbuf; | |
| 264 State.pInEnd = pixelbuf + bitmapSize; | |
| 265 /* Allow for overhead of 4 == code size + byte count + overflow byte + zero terminator */ | |
| 266 State.OutLength = bitmapSize + 4; | |
| 267 State.fOutPaged = State.OutLength > GIF_LZW_PAGE_SIZE; | |
| 268 if (State.fOutPaged) { | |
| 269 State.OutLength = GIF_LZW_PAGE_SIZE; | |
| 270 } | |
| 271 if (!(State.pOut = (unsigned char *) malloc(State.OutLength))) { | |
| 272 return errtxt(ZINT_ERROR_MEMORY, symbol, 614, "Insufficient memory for GIF LZW buffer"); | |
| 273 } | |
| 274 | |
| 275 State.fmp = &fm; | |
| 276 | |
| 277 /* Open output file in binary mode */ | |
| 278 if (!fm_open(State.fmp, symbol, "wb")) { | |
| 279 errtxtf(0, symbol, 611, "Could not open GIF output file (%1$d: %2$s)", State.fmp->err, | |
| 280 strerror(State.fmp->err)); | |
| 281 free(State.pOut); | |
| 282 return ZINT_ERROR_FILE_ACCESS; | |
| 283 } | |
| 284 | |
| 285 /* | |
| 286 * Build a table of the used palette items. | |
| 287 * Currently, there are the following 10 colour codes: | |
| 288 * '0': standard background | |
| 289 * '1': standard foreground | |
| 290 * 'W': white | |
| 291 * 'C': cyan | |
| 292 * 'B': blue | |
| 293 * 'M': magenta | |
| 294 * 'R': red | |
| 295 * 'Y': yellow | |
| 296 * 'G': green | |
| 297 * 'K': black | |
| 298 * '0' and '1' may be identical to one of the other values | |
| 299 */ | |
| 300 memset(State.map, 0, sizeof(State.map)); | |
| 301 if (symbol->symbology == BARCODE_ULTRA) { | |
| 302 static const unsigned char ultra_chars[8] = { 'W', 'C', 'B', 'M', 'R', 'Y', 'G', 'K' }; | |
| 303 for (i = 0; i < 8; i++) { | |
| 304 State.map[ultra_chars[i]] = i; | |
| 305 out_colour_char_to_rgb(ultra_chars[i], &paletteRGB[i][0], &paletteRGB[i][1], &paletteRGB[i][2]); | |
| 306 } | |
| 307 paletteCount = 8; | |
| 308 paletteBitSize = 3; | |
| 309 | |
| 310 /* For Ultracode, have foreground only if have bind/box */ | |
| 311 if (symbol->border_width > 0 && (symbol->output_options & (BARCODE_BIND | BARCODE_BOX | BARCODE_BIND_TOP))) { | |
| 312 /* Check whether can re-use black */ | |
| 313 if (RGBfg[0] == 0 && RGBfg[1] == 0 && RGBfg[2] == 0) { | |
| 314 State.map['1'] = fgindex = 7; /* Re-use black */ | |
| 315 } else { | |
| 316 State.map['1'] = fgindex = paletteCount; | |
| 317 memcpy(paletteRGB[paletteCount++], RGBfg, 3); | |
| 318 paletteBitSize = 4; | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 /* For Ultracode, have background only if have whitespace/quiet zones */ | |
| 323 if (symbol->whitespace_width > 0 || symbol->whitespace_height > 0 | |
| 324 || ((symbol->output_options & BARCODE_QUIET_ZONES) | |
| 325 && !(symbol->output_options & BARCODE_NO_QUIET_ZONES))) { | |
| 326 /* Check whether can re-use white */ | |
| 327 if (RGBbg[0] == 0xff && RGBbg[1] == 0xff && RGBbg[2] == 0xff && bgalpha == fgalpha) { | |
| 328 State.map['0'] = bgindex = 0; /* Re-use white */ | |
| 329 } else { | |
| 330 State.map['0'] = bgindex = paletteCount; | |
| 331 memcpy(paletteRGB[paletteCount++], RGBbg, 3); | |
| 332 paletteBitSize = 4; | |
| 333 } | |
| 334 } | |
| 335 } else { | |
| 336 State.map['0'] = bgindex = 0; | |
| 337 memcpy(paletteRGB[bgindex], RGBbg, 3); | |
| 338 State.map['1'] = fgindex = 1; | |
| 339 memcpy(paletteRGB[fgindex], RGBfg, 3); | |
| 340 paletteCount = 2; | |
| 341 paletteBitSize = 1; | |
| 342 } | |
| 343 | |
| 344 /* Set transparency */ | |
| 345 /* Note: does not allow both transparent foreground and background - | |
| 346 * background takes priority */ | |
| 347 transparent_index = -1; | |
| 348 if (bgalpha == 0 && bgindex != -1) { | |
| 349 /* Transparent background */ | |
| 350 transparent_index = bgindex; | |
| 351 } else if (fgalpha == 0 && fgindex != -1) { | |
| 352 /* Transparent foreground */ | |
| 353 transparent_index = fgindex; | |
| 354 } | |
| 355 | |
| 356 /* palette size 2 ^ bit size */ | |
| 357 paletteSize = 1 << paletteBitSize; | |
| 358 | |
| 359 /* GIF signature (6) */ | |
| 360 fm_write(transparent_index == -1 ? "GIF87a" : "GIF89a", 1, 6, State.fmp); | |
| 361 /* Screen Descriptor (7) */ | |
| 362 /* Screen Width */ | |
| 363 outbuf[0] = (unsigned char) (0xff & symbol->bitmap_width); | |
| 364 outbuf[1] = (unsigned char) (0xff & (symbol->bitmap_width >> 8)); | |
| 365 /* Screen Height */ | |
| 366 outbuf[2] = (unsigned char) (0xff & symbol->bitmap_height); | |
| 367 outbuf[3] = (unsigned char) (0xff & (symbol->bitmap_height >> 8)); | |
| 368 /* write ImageBits-1 to the three least significant bits of byte 5 of | |
| 369 * the Screen Descriptor | |
| 370 * Bits 76543210 | |
| 371 * 1 : Global colour map | |
| 372 * 111 : 8 bit colour depth of the palette | |
| 373 * 0 : Not ordered in decreasing importance | |
| 374 * xxx : palette bit size - 1 | |
| 375 */ | |
| 376 outbuf[4] = (unsigned char) (0xf0 | (0x7 & (paletteBitSize - 1))); | |
| 377 | |
| 378 /* | |
| 379 * Background colour index | |
| 380 * Default to 0. If colour code 0 or K is present, it is used as index | |
| 381 */ | |
| 382 outbuf[5] = bgindex == -1 ? 0 : bgindex; | |
| 383 /* Byte 7 must be 0x00 */ | |
| 384 outbuf[6] = 0x00; | |
| 385 fm_write(outbuf, 1, 7, State.fmp); | |
| 386 /* Global Color Table (paletteSize*3) */ | |
| 387 fm_write(paletteRGB, 1, 3*paletteCount, State.fmp); | |
| 388 /* add unused palette items to fill palette size */ | |
| 389 for (i = paletteCount; i < paletteSize; i++) { | |
| 390 fm_write(RGBUnused, 1, 3, State.fmp); | |
| 391 } | |
| 392 | |
| 393 /* Graphic control extension (8) */ | |
| 394 /* A graphic control extension block is used for overlay gifs. | |
| 395 * This is necessary to define a transparent color. | |
| 396 */ | |
| 397 if (transparent_index != -1) { | |
| 398 /* Extension Introducer = '!' */ | |
| 399 outbuf[0] = '!'; | |
| 400 /* Graphic Control Label */ | |
| 401 outbuf[1] = 0xf9; | |
| 402 /* Block Size */ | |
| 403 outbuf[2] = 4; | |
| 404 /* Packet fields: | |
| 405 * 3 Reserved | |
| 406 * 3 Disposal Method: 0 No Action, 1 No Dispose, 2: Background, 3: Prev. | |
| 407 * 1 User Input Flag: 0: no user input, 1: user input | |
| 408 * 1 Transparent Color Flag: 0: No Transparency, 1: Transparency index | |
| 409 */ | |
| 410 outbuf[3] = 1; | |
| 411 /* Delay Time */ | |
| 412 outbuf[4] = 0; | |
| 413 outbuf[5] = 0; | |
| 414 /* Transparent Color Index */ | |
| 415 outbuf[6] = (unsigned char) transparent_index; | |
| 416 /* Block Terminator */ | |
| 417 outbuf[7] = 0; | |
| 418 fm_write(outbuf, 1, 8, State.fmp); | |
| 419 } | |
| 420 /* Image Descriptor */ | |
| 421 /* Image separator character = ',' */ | |
| 422 outbuf[0] = ','; | |
| 423 /* "Image Left" */ | |
| 424 outbuf[1] = 0x00; | |
| 425 outbuf[2] = 0x00; | |
| 426 /* "Image Top" */ | |
| 427 outbuf[3] = 0x00; | |
| 428 outbuf[4] = 0x00; | |
| 429 /* Image Width (low byte first) */ | |
| 430 outbuf[5] = (unsigned char) (0xff & symbol->bitmap_width); | |
| 431 outbuf[6] = (unsigned char) (0xff & (symbol->bitmap_width >> 8)); | |
| 432 /* Image Height */ | |
| 433 outbuf[7] = (unsigned char) (0xff & symbol->bitmap_height); | |
| 434 outbuf[8] = (unsigned char) (0xff & (symbol->bitmap_height >> 8)); | |
| 435 | |
| 436 /* Byte 10 contains the interlaced flag and | |
| 437 * information on the local color table. | |
| 438 * There is no local color table if its most significant bit is reset. | |
| 439 */ | |
| 440 outbuf[9] = 0x00; | |
| 441 fm_write(outbuf, 1, 10, State.fmp); | |
| 442 | |
| 443 /* call lzw encoding */ | |
| 444 if (!gif_lzw(&State, paletteBitSize)) { | |
| 445 free(State.pOut); | |
| 446 (void) fm_close(State.fmp, symbol); | |
| 447 return errtxt(ZINT_ERROR_MEMORY, symbol, 613, "Insufficient memory for GIF LZW buffer"); | |
| 448 } | |
| 449 fm_write(State.pOut, 1, State.OutPosCur, State.fmp); | |
| 450 free(State.pOut); | |
| 451 | |
| 452 /* GIF terminator */ | |
| 453 fm_putc(';', State.fmp); | |
| 454 | |
| 455 if (fm_error(State.fmp)) { | |
| 456 errtxtf(0, symbol, 615, "Incomplete write of GIF output (%1$d: %2$s)", State.fmp->err, | |
| 457 strerror(State.fmp->err)); | |
| 458 (void) fm_close(State.fmp, symbol); | |
| 459 return ZINT_ERROR_FILE_WRITE; | |
| 460 } | |
| 461 | |
| 462 if (!fm_close(State.fmp, symbol)) { | |
| 463 return errtxtf(ZINT_ERROR_FILE_WRITE, symbol, 617, "Failure on closing GIF output file (%1$d: %2$s)", | |
| 464 State.fmp->err, strerror(State.fmp->err)); | |
| 465 } | |
| 466 | |
| 467 return 0; | |
| 468 } | |
| 469 | |
| 470 /* vim: set ts=4 sw=4 et : */ |
