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