1 /* gif.c - Handles output to gif file */
2
3 /*
4 libzint - the open source barcode library
5 Copyright (C) 2009-2017 Robin Stuart <rstuart114@gmail.com>
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16 3. Neither the name of the project nor the names of its contributors
17 may be used to endorse or promote products derived from this software
18 without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
24 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include "common.h"
37 #include <math.h>
38 #ifdef _MSC_VER
39 #include <io.h>
40 #include <fcntl.h>
41 #include <malloc.h>
42 #endif
43
44 #define SSET "0123456789ABCDEF"
45
46 /* Index of transparent color, -1 for no transparent color
47 * This might be set into a variable if transparency is activated as an option
48 */
49 #define TRANSPARENT_INDEX (-1)
50
51 /* Used bit depth, may be changed for bigger pallet in future */
52 #define DESTINATION_IMAGE_BITS 1
53 #include <stdlib.h>
54
55 typedef struct s_statestruct {
56 unsigned char * pOut;
57 unsigned char *pIn;
58 unsigned int InLen;
59 unsigned int OutLength;
60 unsigned int OutPosCur;
61 unsigned int OutByteCountPos;
62 unsigned short ClearCode;
63 unsigned short FreeCode;
64 char fByteCountByteSet;
65 unsigned char OutBitsFree;
66 unsigned short NodeAxon[4096];
67 unsigned short NodeNext[4096];
68 unsigned char NodePix[4096];
69 } statestruct;
70
BufferNextByte(statestruct * pState)71 static char BufferNextByte(statestruct *pState) {
72 (pState->OutPosCur)++;
73 /* Check if this position is a byte count position
74 * fg_f_bytecountbyte_set indicates, if byte count position bytes should be
75 * inserted in general.
76 * If this is true, and the distance to the last byte count position is 256
77 * (e.g. 255 bytes in between), a byte count byte is inserted, and the value
78 * of the last one is set to 255.
79 * */
80 if (pState->fByteCountByteSet && (pState->OutByteCountPos + 256 == pState->OutPosCur)) {
81 (pState->pOut)[pState->OutByteCountPos] = 255;
82 pState->OutByteCountPos = pState->OutPosCur;
83 (pState->OutPosCur)++;
84 }
85 if (pState->OutPosCur >= pState->OutLength)
86 return 1;
87 (pState->pOut)[pState->OutPosCur] = 0x00;
88 return 0;
89 }
90
AddCodeToBuffer(statestruct * pState,unsigned short CodeIn,unsigned char CodeBits)91 static char AddCodeToBuffer(statestruct *pState, unsigned short CodeIn, unsigned char CodeBits) {
92 /* Check, if we may fill up the current byte completely */
93 if (CodeBits >= pState->OutBitsFree) {
94 (pState->pOut)[pState->OutPosCur] |= (unsigned char)
95 (CodeIn << (8 - pState->OutBitsFree));
96 if (BufferNextByte(pState))
97 return -1;
98 CodeIn = (unsigned short) (CodeIn >> pState->OutBitsFree);
99 CodeBits -= pState->OutBitsFree;
100 pState->OutBitsFree = 8;
101 /* Write a full byte if there are at least 8 code bits left */
102 if (CodeBits >= pState->OutBitsFree) {
103 (pState->pOut)[pState->OutPosCur] = (unsigned char) CodeIn;
104 if (BufferNextByte(pState))
105 return -1;
106 CodeIn = (unsigned short) (CodeIn >> 8);
107 CodeBits -= 8;
108 }
109 }
110 /* The remaining bits of CodeIn fit in the current byte. */
111 if (CodeBits > 0) {
112 (pState->pOut)[pState->OutPosCur] |= (unsigned char)
113 (CodeIn << (8 - pState->OutBitsFree));
114 pState->OutBitsFree -= CodeBits;
115 }
116 return 0;
117 }
118
FlushStringTable(statestruct * pState)119 static void FlushStringTable(statestruct *pState) {
120 unsigned short Pos;
121 for (Pos = 0; Pos < pState->ClearCode; Pos++) {
122 (pState->NodeAxon)[Pos] = 0;
123 }
124 }
125
FindPixelOutlet(statestruct * pState,unsigned short HeadNode,unsigned char Byte)126 unsigned short FindPixelOutlet(statestruct *pState, unsigned short HeadNode, unsigned char Byte) {
127 unsigned short Outlet;
128
129 Outlet = (pState->NodeAxon)[HeadNode];
130 while (Outlet) {
131 if ((pState->NodePix)[Outlet] == Byte)
132 return Outlet;
133 Outlet = (pState->NodeNext)[Outlet];
134 }
135 return 0;
136 }
137
NextCode(statestruct * pState,unsigned char * pPixelValueCur,unsigned char CodeBits)138 static char NextCode(statestruct *pState, unsigned char * pPixelValueCur, unsigned char CodeBits) {
139 unsigned short UpNode;
140 unsigned short DownNode;
141 /* start with the root node for last pixel chain */
142 UpNode = *pPixelValueCur;
143 if ((pState->InLen) == 0)
144 return AddCodeToBuffer(pState, UpNode, CodeBits);
145
146 *pPixelValueCur = (*(pState->pIn)) - '0';
147 (pState->pIn)++;
148 (pState->InLen)--;
149 /* Follow the string table and the data stream to the end of the longest string that has a code */
150 while (0 != (DownNode = FindPixelOutlet(pState, UpNode, *pPixelValueCur))) {
151 UpNode = DownNode;
152 if ((pState->InLen) == 0)
153 return AddCodeToBuffer(pState, UpNode, CodeBits);
154
155 *pPixelValueCur = (*(pState->pIn)) - '0';
156 (pState->pIn)++;
157 (pState->InLen)--;
158 }
159 /* Submit 'UpNode' which is the code of the longest string */
160 if (AddCodeToBuffer(pState, UpNode, CodeBits))
161 return -1;
162 /* ... and extend the string by appending 'PixelValueCur' */
163 /* Create a successor node for 'PixelValueCur' whose code is 'freecode' */
164 (pState->NodePix)[pState->FreeCode] = *pPixelValueCur;
165 (pState->NodeAxon)[pState->FreeCode] = (pState->NodeNext)[pState->FreeCode] = 0;
166 /* ...and link it to the end of the chain emanating from fg_axon[UpNode]. */
167 DownNode = (pState->NodeAxon)[UpNode];
168 if (!DownNode) {
169 (pState->NodeAxon)[UpNode] = pState->FreeCode;
170 } else {
171 while ((pState->NodeNext)[DownNode]) {
172 DownNode = (pState->NodeNext)[DownNode];
173 }
174 (pState->NodeNext)[DownNode] = pState->FreeCode;
175 }
176 return 1;
177 }
178
gif_lzw(unsigned char * pOut,int OutLength,unsigned char * pIn,int InLen)179 int gif_lzw(unsigned char *pOut, int OutLength, unsigned char *pIn, int InLen) {
180 unsigned char PixelValueCur;
181 unsigned char CodeBits;
182 unsigned short Pos;
183 statestruct State;
184
185 State.pIn = pIn;
186 State.InLen = InLen;
187 State.pOut = pOut;
188 State.OutLength = OutLength;
189 // > Get first data byte
190 if (State.InLen == 0)
191 return 0;
192
193 PixelValueCur = (unsigned char) ((*(State.pIn)) - '0');
194 (State.pIn)++;
195 (State.InLen)--;
196 CodeBits = 3;
197 State.ClearCode = 4;
198 State.FreeCode = 6;
199 State.OutBitsFree = 8;
200 State.OutPosCur = -1;
201 State.fByteCountByteSet = 0;
202
203 if (BufferNextByte(&State))
204 return 0;
205
206 for (Pos = 0; Pos < State.ClearCode; Pos++)
207 State.NodePix[Pos] = (unsigned char) Pos;
208
209 FlushStringTable(&State);
210
211 /* Write what the GIF specification calls the "code size". */
212 (State.pOut)[State.OutPosCur] = 2;
213 /* Reserve first bytecount byte */
214 if (BufferNextByte(&State))
215 return 0;
216 State.OutByteCountPos = State.OutPosCur;
217 if (BufferNextByte(&State))
218 return 0;
219 State.fByteCountByteSet = 1;
220 /* Submit one 'ClearCode' as the first code */
221 if (AddCodeToBuffer(&State, State.ClearCode, CodeBits))
222 return 0;
223
224 for (;;) {
225 char Res;
226 /* generate and save the next code, which may consist of multiple input pixels. */
227 Res = NextCode(&State, &PixelValueCur, CodeBits);
228 if (Res < 0)
229 return 0;
230 //* Check for end of data stream */
231 if (!Res) {
232 /* submit 'eoi' as the last item of the code stream */
233 if (AddCodeToBuffer(&State, (unsigned short) (State.ClearCode + 1), CodeBits))
234 return 0;
235 State.fByteCountByteSet = 0;
236 if (State.OutBitsFree < 8) {
237 if (BufferNextByte(&State))
238 return 0;
239 }
240 // > Update last bytecount byte;
241 if (State.OutByteCountPos < State.OutPosCur) {
242 (State.pOut)[State.OutByteCountPos] = (unsigned char) (State.OutPosCur - State.OutByteCountPos - 1);
243 }
244 State.OutPosCur++;
245 return State.OutPosCur;
246 }
247 /* Check for currently last code */
248 if (State.FreeCode == (1U << CodeBits))
249 CodeBits++;
250 State.FreeCode++;
251 /* Check for full stringtable */
252 if (State.FreeCode == 0xfff) {
253 FlushStringTable(&State);
254 if (AddCodeToBuffer(&State, State.ClearCode, CodeBits))
255 return 0;
256
257 CodeBits = (unsigned char) (1 + 2);
258 State.FreeCode = (unsigned short) (State.ClearCode + 2);
259 }
260 }
261 }
262
gif_pixel_plot(struct zint_symbol * symbol,char * pixelbuf)263 int gif_pixel_plot(struct zint_symbol *symbol, char *pixelbuf) {
264 char outbuf[10];
265 FILE *gif_file;
266 unsigned short usTemp;
267 int byte_out;
268 #ifdef _MSC_VER
269 char * lzwoutbuf;
270 #endif
271
272 #ifndef _MSC_VER
273 char lzwoutbuf[symbol->bitmap_height * symbol->bitmap_width];
274 #else
275 lzwoutbuf = (char *) _alloca((symbol->bitmap_height * symbol->bitmap_width) * sizeof (char));
276 #endif /* _MSC_VER */
277
278 /* Open output file in binary mode */
279 if ((symbol->output_options & BARCODE_STDOUT) != 0) {
280 #ifdef _MSC_VER
281 if (-1 == _setmode(_fileno(stdout), _O_BINARY)) {
282 strcpy(symbol->errtxt, "610: Can't open output file");
283 return ZINT_ERROR_FILE_ACCESS;
284 }
285 #endif
286 gif_file = stdout;
287 } else {
288 if (!(gif_file = fopen(symbol->outfile, "wb"))) {
289 strcpy(symbol->errtxt, "611: Can't open output file");
290 return ZINT_ERROR_FILE_ACCESS;
291 }
292 }
293 /*ImageWidth = 2;
294 ImageHeight = 2;
295 rotated_bitmap[0] = 1;
296 rotated_bitmap[1] = 1;
297 rotated_bitmap[2] = 0;
298 rotated_bitmap[3] = 0;
299 */
300
301 /* GIF signature (6) */
302 memcpy(outbuf, "GIF87a", 6);
303 if (TRANSPARENT_INDEX != -1)
304 outbuf[4] = '9';
305 fwrite(outbuf, 6, 1, gif_file);
306 /* Screen Descriptor (7) */
307 /* Screen Width */
308 usTemp = (unsigned short) symbol->bitmap_width;
309 outbuf[0] = (unsigned char) (0xff & usTemp);
310 outbuf[1] = (unsigned char) ((0xff00 & usTemp) / 0x100);
311 /* Screen Height */
312 usTemp = (unsigned short) symbol->bitmap_height;
313 outbuf[2] = (unsigned char) (0xff & usTemp);
314 outbuf[3] = (unsigned char) ((0xff00 & usTemp) / 0x100);
315 /* write ImageBits-1 to the three least significant bits of byte 5 of
316 * the Screen Descriptor
317 */
318 outbuf[4] = (unsigned char) (0xf0 | (0x7 & (DESTINATION_IMAGE_BITS - 1)));
319 /* Background color = colortable index 0 */
320 outbuf[5] = 0x00;
321 /* Byte 7 must be 0x00 */
322 outbuf[6] = 0x00;
323 fwrite(outbuf, 7, 1, gif_file);
324 /* Global Color Table (6) */
325 /* RGB 0 color */
326 outbuf[0] = (unsigned char) (16 * ctoi(symbol->bgcolour[0])) + ctoi(symbol->bgcolour[1]);
327 outbuf[1] = (unsigned char) (16 * ctoi(symbol->bgcolour[2])) + ctoi(symbol->bgcolour[3]);
328 outbuf[2] = (unsigned char) (16 * ctoi(symbol->bgcolour[4])) + ctoi(symbol->bgcolour[5]);
329 /* RGB 1 color */
330 outbuf[3] = (unsigned char) (16 * ctoi(symbol->fgcolour[0])) + ctoi(symbol->fgcolour[1]);
331 outbuf[4] = (unsigned char) (16 * ctoi(symbol->fgcolour[2])) + ctoi(symbol->fgcolour[3]);
332 outbuf[5] = (unsigned char) (16 * ctoi(symbol->fgcolour[4])) + ctoi(symbol->fgcolour[5]);
333 fwrite(outbuf, 6, 1, gif_file);
334
335 /* Graphic control extension (8) */
336 /* A graphic control extension block is used for overlay gifs.
337 * This is necessary to define a transparent color.
338 */
339 if (TRANSPARENT_INDEX != -1) {
340 /* Extension Introducer = '!' */
341 outbuf[0] = '\x21';
342 /* Graphic Control Label */
343 outbuf[1] = '\xf9';
344 /* Block Size */
345 outbuf[2] = 4;
346 /* Packet fields:
347 * 3 Reserved
348 * 3 Disposal Method: 0 No Action, 1 No Dispose, 2: Background, 3: Prev.
349 * 1 User Input Flag: 0: no user input, 1: user input
350 * 1 Transparent Color Flag: 0: No Transparency, 1: Transparency index
351 */
352 outbuf[3] = 1;
353 /* Delay Time */
354 outbuf[4] = 0;
355 outbuf[5] = 0;
356 /* Transparent Color Index */
357 outbuf[6] = (unsigned char) TRANSPARENT_INDEX;
358 /* Block Terminator */
359 outbuf[7] = 0;
360 fwrite(outbuf, 8, 1, gif_file);
361 }
362 /* Image Descriptor */
363 /* Image separator character = ',' */
364 outbuf[0] = 0x2c;
365 /* "Image Left" */
366 outbuf[1] = 0x00;
367 outbuf[2] = 0x00;
368 /* "Image Top" */
369 outbuf[3] = 0x00;
370 outbuf[4] = 0x00;
371 /* Image Width (low byte first) */
372 outbuf[5] = (unsigned char) (0xff & symbol->bitmap_width);
373 outbuf[6] = (unsigned char) ((0xff00 & symbol->bitmap_width) / 0x100);
374 /* Image Height */
375 outbuf[7] = (unsigned char) (0xff & symbol->bitmap_height);
376 outbuf[8] = (unsigned char) ((0xff00 & symbol->bitmap_height) / 0x100);
377
378 /* Byte 10 contains the interlaced flag and
379 * information on the local color table.
380 * There is no local color table if its most significant bit is reset.
381 */
382 outbuf[9] = (unsigned char) (0 | (0x7 & (DESTINATION_IMAGE_BITS - 1)));
383 fwrite(outbuf, 10, 1, gif_file);
384
385 /* call lzw encoding */
386 byte_out = gif_lzw(
387 (unsigned char *) lzwoutbuf,
388 symbol->bitmap_height * symbol->bitmap_width,
389 (unsigned char *) pixelbuf,
390 symbol->bitmap_height * symbol->bitmap_width);
391 if (byte_out <= 0) {
392 fclose(gif_file);
393 return ZINT_ERROR_MEMORY;
394 }
395 fwrite(lzwoutbuf, byte_out, 1, gif_file);
396
397 /* GIF terminator */
398 fputc('\x3b', gif_file);
399 fclose(gif_file);
400
401 return 0;
402 }