/src/ghostpdl/devices/gdevbmpc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | /* .BMP file format driver utilities */ |
17 | | #include "gdevprn.h" |
18 | | #include "gdevbmp.h" |
19 | | |
20 | | /* |
21 | | * Define BMP file format structures. |
22 | | * All multi-byte quantities are stored LSB-first! |
23 | | */ |
24 | | |
25 | | typedef ushort word; |
26 | | #if ARCH_SIZEOF_INT == 4 |
27 | | typedef uint dword; |
28 | | #else |
29 | | # if ARCH_SIZEOF_LONG == 4 |
30 | | typedef ulong dword; |
31 | | # endif |
32 | | #endif |
33 | | #if ARCH_IS_BIG_ENDIAN |
34 | | # define BMP_ASSIGN_WORD(a,v) a = ((v) >> 8) + ((v) << 8) |
35 | | # define BMP_ASSIGN_DWORD(a,v)\ |
36 | | a = ((v) >> 24) + (((v) >> 8) & 0xff00L) +\ |
37 | | (((dword)(v) << 8) & 0xff0000L) + ((dword)(v) << 24) |
38 | | #else |
39 | 33.4k | # define BMP_ASSIGN_WORD(a,v) a = (v) |
40 | 92.0k | # define BMP_ASSIGN_DWORD(a,v) a = (v) |
41 | | #endif |
42 | | |
43 | | typedef struct bmp_file_header_s { |
44 | | |
45 | | /* BITMAPFILEHEADER */ |
46 | | |
47 | | /* |
48 | | * This structure actually begins with two bytes |
49 | | * containing the characters 'BM', but we must omit them, |
50 | | * because some compilers would insert padding to force |
51 | | * the size member to a 32- or 64-bit boundary. |
52 | | */ |
53 | | |
54 | | /*byte typeB, typeM; *//* always 'BM' */ |
55 | | dword size; /* total size of file */ |
56 | | word reserved1; |
57 | | word reserved2; |
58 | | dword offBits; /* offset of bits from start of file */ |
59 | | |
60 | | } bmp_file_header; |
61 | | |
62 | | #define sizeof_bmp_file_header (2 + sizeof(bmp_file_header)) |
63 | | |
64 | | typedef struct bmp_info_header_s { |
65 | | |
66 | | /* BITMAPINFOHEADER */ |
67 | | |
68 | | dword size; /* size of info header in bytes */ |
69 | | dword width; /* width in pixels */ |
70 | | dword height; /* height in pixels */ |
71 | | word planes; /* # of planes, always 1 */ |
72 | | word bitCount; /* bits per pixel */ |
73 | | dword compression; /* compression scheme, always 0 */ |
74 | | dword sizeImage; /* size of bits */ |
75 | | dword xPelsPerMeter; /* X pixels per meter */ |
76 | | dword yPelsPerMeter; /* Y pixels per meter */ |
77 | | dword clrUsed; /* # of colors used */ |
78 | | dword clrImportant; /* # of important colors */ |
79 | | |
80 | | /* This is followed by (1 << bitCount) bmp_quad structures, */ |
81 | | /* unless bitCount == 24. */ |
82 | | |
83 | | } bmp_info_header; |
84 | | |
85 | | typedef struct bmp_quad_s { |
86 | | |
87 | | /* RGBQUAD */ |
88 | | |
89 | | byte blue, green, red, reserved; |
90 | | |
91 | | } bmp_quad; |
92 | | |
93 | | /* Write the BMP file header. */ |
94 | | static int |
95 | | write_bmp_depth_header(gx_device_printer *pdev, gp_file *file, int depth, |
96 | | const byte *palette /* [4 << depth] */, |
97 | | int raster) |
98 | 8.36k | { |
99 | | /* BMP scan lines are padded to 32 bits. */ |
100 | 8.36k | ulong bmp_raster = raster + (-raster & 3); |
101 | 8.36k | int height = pdev->height; |
102 | 8.36k | int quads = (depth <= 8 ? sizeof(bmp_quad) << depth : 0); |
103 | | |
104 | | /* Write the file header. */ |
105 | | |
106 | 8.36k | gp_fputc('B', file); |
107 | 8.36k | gp_fputc('M', file); |
108 | 8.36k | { |
109 | 8.36k | bmp_file_header fhdr; |
110 | | |
111 | 8.36k | BMP_ASSIGN_DWORD(fhdr.size, |
112 | 8.36k | sizeof_bmp_file_header + |
113 | 8.36k | sizeof(bmp_info_header) + quads + |
114 | 8.36k | bmp_raster * height); |
115 | 8.36k | BMP_ASSIGN_WORD(fhdr.reserved1, 0); |
116 | 8.36k | BMP_ASSIGN_WORD(fhdr.reserved2, 0); |
117 | 8.36k | BMP_ASSIGN_DWORD(fhdr.offBits, |
118 | 8.36k | sizeof_bmp_file_header + |
119 | 8.36k | sizeof(bmp_info_header) + quads); |
120 | 8.36k | if (gp_fwrite((const char *)&fhdr, 1, sizeof(fhdr), file) != sizeof(fhdr)) |
121 | 0 | return_error(gs_error_ioerror); |
122 | 8.36k | } |
123 | | |
124 | | /* Write the info header. */ |
125 | | |
126 | 8.36k | { |
127 | 8.36k | bmp_info_header ihdr; |
128 | | |
129 | 8.36k | BMP_ASSIGN_DWORD(ihdr.size, sizeof(ihdr)); |
130 | 8.36k | BMP_ASSIGN_DWORD(ihdr.width, pdev->width); |
131 | 8.36k | BMP_ASSIGN_DWORD(ihdr.height, height); |
132 | 8.36k | BMP_ASSIGN_WORD(ihdr.planes, 1); |
133 | 8.36k | BMP_ASSIGN_WORD(ihdr.bitCount, depth); |
134 | 8.36k | BMP_ASSIGN_DWORD(ihdr.compression, 0); |
135 | 8.36k | BMP_ASSIGN_DWORD(ihdr.sizeImage, bmp_raster * height); |
136 | | /* |
137 | | * Earlier versions of this driver set the PelsPerMeter values |
138 | | * to zero. At a user's request, we now set them correctly, |
139 | | * but we suspect this will cause problems other places. |
140 | | */ |
141 | 8.36k | #define INCHES_PER_METER (100 /*cm/meter*/ / 2.54 /*cm/inch*/) |
142 | 8.36k | BMP_ASSIGN_DWORD(ihdr.xPelsPerMeter, |
143 | 8.36k | (dword)(pdev->x_pixels_per_inch * INCHES_PER_METER + 0.5)); |
144 | 8.36k | BMP_ASSIGN_DWORD(ihdr.yPelsPerMeter, |
145 | 8.36k | (dword)(pdev->y_pixels_per_inch * INCHES_PER_METER + 0.5)); |
146 | 8.36k | #undef INCHES_PER_METER |
147 | 8.36k | BMP_ASSIGN_DWORD(ihdr.clrUsed, 0); |
148 | 8.36k | BMP_ASSIGN_DWORD(ihdr.clrImportant, 0); |
149 | 8.36k | if (gp_fwrite((const char *)&ihdr, 1, sizeof(ihdr), file) != sizeof(ihdr)) |
150 | 0 | return_error(gs_error_ioerror); |
151 | 8.36k | } |
152 | | |
153 | | /* Write the palette. */ |
154 | | |
155 | 8.36k | if (depth <= 8) |
156 | 8.36k | gp_fwrite(palette, sizeof(bmp_quad), 1 << depth, file); |
157 | | |
158 | 8.36k | return 0; |
159 | 8.36k | } |
160 | | |
161 | | /* Write the BMP file header. */ |
162 | | int |
163 | | write_bmp_header(gx_device_printer *pdev, gp_file *file) |
164 | 8.36k | { |
165 | 8.36k | int depth = pdev->color_info.depth; |
166 | 8.36k | bmp_quad palette[256]; |
167 | | |
168 | 8.36k | if (depth <= 8) { |
169 | 8.36k | int i; |
170 | 8.36k | gx_color_value rgb[3]; |
171 | 8.36k | bmp_quad q; |
172 | | |
173 | 8.36k | q.reserved = 0; |
174 | 25.1k | for (i = 0; i != 1 << depth; i++) { |
175 | | /* Note that the use of map_color_rgb is deprecated in |
176 | | favor of decode_color. This should work, though, because |
177 | | backwards compatibility is preserved. */ |
178 | 16.7k | (*dev_proc(pdev, map_color_rgb))((gx_device *)pdev, |
179 | 16.7k | (gx_color_index)i, rgb); |
180 | 16.7k | q.red = gx_color_value_to_byte(rgb[0]); |
181 | 16.7k | q.green = gx_color_value_to_byte(rgb[1]); |
182 | 16.7k | q.blue = gx_color_value_to_byte(rgb[2]); |
183 | 16.7k | palette[i] = q; |
184 | 16.7k | } |
185 | 8.36k | } |
186 | 8.36k | return write_bmp_depth_header(pdev, file, depth, (const byte *)palette, |
187 | 8.36k | gdev_prn_raster(pdev)); |
188 | 8.36k | } |
189 | | |
190 | | /* Write a BMP header for separated CMYK output. */ |
191 | | int |
192 | | write_bmp_separated_header(gx_device_printer *pdev, gp_file *file) |
193 | 0 | { |
194 | 0 | int depth = pdev->color_info.depth; |
195 | 0 | int plane_depth = depth / 4; |
196 | 0 | bmp_quad palette[256]; |
197 | 0 | bmp_quad q; |
198 | 0 | int i; |
199 | |
|
200 | 0 | q.reserved = 0; |
201 | 0 | for (i = 0; i < 1 << plane_depth; i++) { |
202 | 0 | q.red = q.green = q.blue = |
203 | 0 | 255 - i * 255 / ((1 << plane_depth) - 1); |
204 | 0 | palette[i] = q; |
205 | 0 | } |
206 | 0 | return write_bmp_depth_header(pdev, file, plane_depth, |
207 | 0 | (const byte *)palette, |
208 | 0 | (pdev->width*plane_depth + 7) >> 3); |
209 | 0 | } |
210 | | |
211 | | /* 24-bit color mappers (taken from gdevmem2.c). */ |
212 | | /* Note that Windows expects RGB values in the order B,G,R. */ |
213 | | |
214 | | /* Map a r-g-b color to a color index. */ |
215 | | gx_color_index |
216 | | bmp_map_16m_rgb_color(gx_device * dev, const gx_color_value cv[]) |
217 | 0 | { |
218 | |
|
219 | 0 | gx_color_value r, g, b; |
220 | 0 | r = cv[0]; g = cv[1]; b = cv[2]; |
221 | 0 | return gx_color_value_to_byte(r) + |
222 | 0 | ((uint) gx_color_value_to_byte(g) << 8) + |
223 | 0 | ((ulong) gx_color_value_to_byte(b) << 16); |
224 | 0 | } |
225 | | |
226 | | /* Map a color index to a r-g-b color. */ |
227 | | int |
228 | | bmp_map_16m_color_rgb(gx_device * dev, gx_color_index color, |
229 | | gx_color_value prgb[3]) |
230 | 0 | { |
231 | 0 | prgb[2] = gx_color_value_from_byte(color >> 16); |
232 | 0 | prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff); |
233 | 0 | prgb[0] = gx_color_value_from_byte(color & 0xff); |
234 | 0 | return 0; |
235 | 0 | } |