1 /* pcx.c - Handles output to ZSoft PCX file */
2 /* ZSoft PCX File Format Technical Reference Manual http://bespin.org/~qz/pc-gpe/pcx.txt */
3 
4 /*
5     libzint - the open source barcode library
6     Copyright (C) 2009 - 2021 Robin Stuart <rstuart114@gmail.com>
7 
8     Redistribution and use in source and binary forms, with or without
9     modification, are permitted provided that the following conditions
10     are met:
11 
12     1. Redistributions of source code must retain the above copyright
13        notice, this list of conditions and the following disclaimer.
14     2. Redistributions in binary form must reproduce the above copyright
15        notice, this list of conditions and the following disclaimer in the
16        documentation and/or other materials provided with the distribution.
17     3. Neither the name of the project nor the names of its contributors
18        may be used to endorse or promote products derived from this software
19        without specific prior written permission.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
25     FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26     DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27     OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30     OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31     SUCH DAMAGE.
32  */
33 /* vim: set ts=4 sw=4 et : */
34 
35 #include <errno.h>
36 #include <stdio.h>
37 #include "common.h"
38 #include "pcx.h"        /* PCX header structure */
39 #include <math.h>
40 #ifdef _MSC_VER
41 #include <io.h>
42 #include <fcntl.h>
43 #include <malloc.h>
44 #endif
45 
pcx_pixel_plot(struct zint_symbol * symbol,unsigned char * pixelbuf)46 INTERNAL int pcx_pixel_plot(struct zint_symbol *symbol, unsigned char *pixelbuf) {
47     int fgred, fggrn, fgblu, bgred, bggrn, bgblu;
48     int row, column, i, colour;
49     int run_count;
50     FILE *pcx_file;
51     pcx_header_t header;
52     int bytes_per_line = symbol->bitmap_width + (symbol->bitmap_width & 1); // Must be even
53     unsigned char previous;
54     const int output_to_stdout = symbol->output_options & BARCODE_STDOUT; /* Suppress gcc -fanalyzer warning */
55 #ifdef _MSC_VER
56     unsigned char *rle_row;
57 #endif
58 
59 #ifndef _MSC_VER
60     unsigned char rle_row[bytes_per_line];
61 #else
62     rle_row = (unsigned char *) _alloca(bytes_per_line);
63 #endif /* _MSC_VER */
64 
65     rle_row[bytes_per_line - 1] = 0; // Will remain zero if bitmap_width odd
66 
67     fgred = (16 * ctoi(symbol->fgcolour[0])) + ctoi(symbol->fgcolour[1]);
68     fggrn = (16 * ctoi(symbol->fgcolour[2])) + ctoi(symbol->fgcolour[3]);
69     fgblu = (16 * ctoi(symbol->fgcolour[4])) + ctoi(symbol->fgcolour[5]);
70     bgred = (16 * ctoi(symbol->bgcolour[0])) + ctoi(symbol->bgcolour[1]);
71     bggrn = (16 * ctoi(symbol->bgcolour[2])) + ctoi(symbol->bgcolour[3]);
72     bgblu = (16 * ctoi(symbol->bgcolour[4])) + ctoi(symbol->bgcolour[5]);
73 
74     header.manufacturer = 10; // ZSoft
75     header.version = 5; // Version 3.0
76     header.encoding = 1; // Run length encoding
77     header.bits_per_pixel = 8;
78     header.window_xmin = 0;
79     header.window_ymin = 0;
80     header.window_xmax = symbol->bitmap_width - 1;
81     header.window_ymax = symbol->bitmap_height - 1;
82     header.horiz_dpi = 300;
83     header.vert_dpi = 300;
84 
85     for (i = 0; i < 48; i++) {
86         header.colourmap[i] = 0x00;
87     }
88 
89     header.reserved = 0;
90     header.number_of_planes = 3;
91 
92     header.bytes_per_line = bytes_per_line;
93 
94     header.palette_info = 1; // Colour
95     header.horiz_screen_size = 0;
96     header.vert_screen_size = 0;
97 
98     for (i = 0; i < 54; i++) {
99         header.filler[i] = 0x00;
100     }
101 
102     /* Open output file in binary mode */
103     if (output_to_stdout) {
104 #ifdef _MSC_VER
105         if (-1 == _setmode(_fileno(stdout), _O_BINARY)) {
106             sprintf(symbol->errtxt, "620: Could not set stdout to binary (%d: %.30s)", errno, strerror(errno));
107             return ZINT_ERROR_FILE_ACCESS;
108         }
109 #endif
110         pcx_file = stdout;
111     } else {
112         if (!(pcx_file = fopen(symbol->outfile, "wb"))) {
113             sprintf(symbol->errtxt, "621: Could not open output file (%d: %.30s)", errno, strerror(errno));
114             return ZINT_ERROR_FILE_ACCESS;
115         }
116     }
117 
118     fwrite(&header, sizeof(pcx_header_t), 1, pcx_file);
119 
120     for (row = 0; row < symbol->bitmap_height; row++) {
121         for (colour = 0; colour < 3; colour++) {
122             for (column = 0; column < symbol->bitmap_width; column++) {
123                 switch (colour) {
124                     case 0:
125                         switch (pixelbuf[(row * symbol->bitmap_width) + column]) {
126                             case 'W': // White
127                             case 'M': // Magenta
128                             case 'R': // Red
129                             case 'Y': // Yellow
130                                 rle_row[column] = 255;
131                                 break;
132                             case 'C': // Cyan
133                             case 'B': // Blue
134                             case 'G': // Green
135                             case 'K': // Black
136                                 rle_row[column] = 0;
137                                 break;
138                             case '1':
139                                 rle_row[column] = fgred;
140                                 break;
141                             default:
142                                 rle_row[column] = bgred;
143                                 break;
144                         }
145                         break;
146                     case 1:
147                         switch (pixelbuf[(row * symbol->bitmap_width) + column]) {
148                             case 'W': // White
149                             case 'C': // Cyan
150                             case 'Y': // Yellow
151                             case 'G': // Green
152                                 rle_row[column] = 255;
153                                 break;
154                             case 'B': // Blue
155                             case 'M': // Magenta
156                             case 'R': // Red
157                             case 'K': // Black
158                                 rle_row[column] = 0;
159                                 break;
160                             case '1':
161                                 rle_row[column] = fggrn;
162                                 break;
163                             default:
164                                 rle_row[column] = bggrn;
165                                 break;
166                         }
167                         break;
168                     case 2:
169                         switch (pixelbuf[(row * symbol->bitmap_width) + column]) {
170                             case 'W': // White
171                             case 'C': // Cyan
172                             case 'B': // Blue
173                             case 'M': // Magenta
174                                 rle_row[column] = 255;
175                                 break;
176                             case 'R': // Red
177                             case 'Y': // Yellow
178                             case 'G': // Green
179                             case 'K': // Black
180                                 rle_row[column] = 0;
181                                 break;
182                             case '1':
183                                 rle_row[column] = fgblu;
184                                 break;
185                             default:
186                                 rle_row[column] = bgblu;
187                                 break;
188                         }
189                         break;
190                 }
191             }
192 
193             /* Based on ImageMagick/coders/pcx.c PCXWritePixels()
194              * Copyright 1999-2020 ImageMagick Studio LLC */
195             previous = rle_row[0];
196             run_count = 1;
197             for (column = 1; column < bytes_per_line; column++) { // Note going up to bytes_per_line
198                 if ((previous == rle_row[column]) && (run_count < 63)) {
199                     run_count++;
200                 } else {
201                     if (run_count > 1 || (previous & 0xc0) == 0xc0) {
202                         run_count += 0xc0;
203                         fputc(run_count, pcx_file);
204                     }
205                     fputc(previous, pcx_file);
206                     previous = rle_row[column];
207                     run_count = 1;
208                 }
209             }
210 
211             if (run_count > 1 || (previous & 0xc0) == 0xc0) {
212                 run_count += 0xc0;
213                 fputc(run_count, pcx_file);
214             }
215             fputc(previous, pcx_file);
216         }
217     }
218 
219     if (output_to_stdout) {
220         fflush(pcx_file);
221     } else {
222         fclose(pcx_file);
223     }
224 
225     return 0;
226 }
227