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 : */