1 /* Copyright (C) 2001-2019 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.,  1305 Grant Avenue - Suite 200, Novato,
13    CA 94945, U.S.A., +1(415)492-9861, for further information.
14 */
15 
16 /* .BMP file format output drivers */
17 #include "gdevprn.h"
18 #include "gdevpccm.h"
19 #include "gdevbmp.h"
20 
21 /* ------ The device descriptors ------ */
22 
23 static dev_proc_print_page(bmp_print_page);
24 static dev_proc_print_page(bmp_cmyk_print_page);
25 
26 /* Monochrome. */
27 
28 const gx_device_printer gs_bmpmono_device =
29 prn_device(prn_bg_procs, "bmpmono",	/* The print_page proc is compatible with allowing bg printing */
30            DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
31            X_DPI, Y_DPI,
32            0, 0, 0, 0,		/* margins */
33            1, bmp_print_page);
34 
35 /* 8-bit (SuperVGA-style) grayscale . */
36 /* (Uses a fixed palette of 256 gray levels.) */
37 
38 /* Since the print_page doesn't alter the device, this device can print in the background */
39 static const gx_device_procs bmpgray_procs =
40 prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
41                 gx_default_gray_map_rgb_color, gx_default_gray_map_color_rgb);
42 const gx_device_printer gs_bmpgray_device = {
43   prn_device_body(gx_device_printer, bmpgray_procs, "bmpgray",
44            DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
45            X_DPI, Y_DPI,
46            0, 0, 0, 0,		/* margins */
47            1, 8, 255, 0, 256, 0, bmp_print_page)
48 };
49 
50 /* 1-bit-per-plane separated CMYK color. */
51 
52 /* Since the print_page doesn't alter the device, this device can print in the background */
53 #define bmp_cmyk_procs(p_map_color_rgb, p_map_cmyk_color)\
54     gdev_prn_open, NULL, NULL, gdev_prn_bg_output_page, gdev_prn_close,\
55     NULL, p_map_color_rgb, NULL, NULL, NULL, NULL, NULL, NULL,\
56     gdev_prn_get_params, gdev_prn_put_params,\
57     p_map_cmyk_color, NULL, NULL, NULL, gx_page_device_get_page_device
58 
59 static const gx_device_procs bmpsep1_procs = {
60     bmp_cmyk_procs(cmyk_1bit_map_color_rgb, cmyk_1bit_map_cmyk_color)
61 };
62 const gx_device_printer gs_bmpsep1_device = {
63   prn_device_body(gx_device_printer, bmpsep1_procs, "bmpsep1",
64         DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
65         X_DPI, Y_DPI,
66         0,0,0,0,			/* margins */
67         4, 4, 1, 1, 2, 2, bmp_cmyk_print_page)
68 };
69 
70 /* 8-bit-per-plane separated CMYK color. */
71 
72 static const gx_device_procs bmpsep8_procs = {
73     bmp_cmyk_procs(cmyk_8bit_map_color_rgb, cmyk_8bit_map_cmyk_color)
74 };
75 const gx_device_printer gs_bmpsep8_device = {
76   prn_device_body(gx_device_printer, bmpsep8_procs, "bmpsep8",
77         DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
78         X_DPI, Y_DPI,
79         0,0,0,0,			/* margins */
80         4, 32, 255, 255, 256, 256, bmp_cmyk_print_page)
81 };
82 
83 /* 4-bit planar (EGA/VGA-style) color. */
84 
85 /* Since the print_page doesn't alter the device, this device can print in the background */
86 static const gx_device_procs bmp16_procs =
87 prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
88                 pc_4bit_map_rgb_color, pc_4bit_map_color_rgb);
89 const gx_device_printer gs_bmp16_device = {
90   prn_device_body(gx_device_printer, bmp16_procs, "bmp16",
91            DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
92            X_DPI, Y_DPI,
93            0, 0, 0, 0,		/* margins */
94            3, 4, 1, 1, 2, 2, bmp_print_page)
95 };
96 
97 /* 8-bit (SuperVGA-style) color. */
98 /* (Uses a fixed palette of 3,3,2 bits.) */
99 
100 /* Since the print_page doesn't alter the device, this device can print in the background */
101 static const gx_device_procs bmp256_procs =
102 prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
103                 pc_8bit_map_rgb_color, pc_8bit_map_color_rgb);
104 const gx_device_printer gs_bmp256_device = {
105   prn_device_body(gx_device_printer, bmp256_procs, "bmp256",
106            DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
107            X_DPI, Y_DPI,
108            0, 0, 0, 0,		/* margins */
109            3, 8, 5, 5, 6, 6, bmp_print_page)
110 };
111 
112 /* 24-bit color. */
113 
114 /* Since the print_page doesn't alter the device, this device can print in the background */
115 static const gx_device_procs bmp16m_procs =
116 prn_color_procs(gdev_prn_open, gdev_prn_bg_output_page, gdev_prn_close,
117                 bmp_map_16m_rgb_color, bmp_map_16m_color_rgb);
118 const gx_device_printer gs_bmp16m_device =
119 prn_device(bmp16m_procs, "bmp16m",
120            DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
121            X_DPI, Y_DPI,
122            0, 0, 0, 0,		/* margins */
123            24, bmp_print_page);
124 
125 /* 32-bit CMYK color (outside the BMP specification). */
126 
127 static const gx_device_procs bmp32b_procs = {
128     bmp_cmyk_procs(cmyk_8bit_map_color_rgb, gx_default_cmyk_map_cmyk_color)
129 };
130 const gx_device_printer gs_bmp32b_device =
131 prn_device(bmp32b_procs, "bmp32b",
132            DEFAULT_WIDTH_10THS, DEFAULT_HEIGHT_10THS,
133            X_DPI, Y_DPI,
134            0, 0, 0, 0,		/* margins */
135            32, bmp_print_page);
136 
137 /* ------ Private definitions ------ */
138 
139 /* Write out a page in BMP format. */
140 /* This routine is used for all non-separated formats. */
141 static int
bmp_print_page(gx_device_printer * pdev,gp_file * file)142 bmp_print_page(gx_device_printer * pdev, gp_file * file)
143 {
144     uint raster = gdev_prn_raster(pdev);
145     /* BMP scan lines are padded to 32 bits. */
146     uint bmp_raster = raster + (-(int)raster & 3);
147     byte *row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer");
148     int y;
149     int code;		/* return code */
150 
151     if (row == 0)		/* can't allocate row buffer */
152         return_error(gs_error_VMerror);
153     memset(row+raster, 0, bmp_raster - raster); /* clear the padding bytes */
154 
155     /* Write the file header. */
156 
157     code = write_bmp_header(pdev, file);
158     if (code < 0)
159         goto done;
160 
161     /* Write the contents of the image. */
162     /* BMP files want the image in bottom-to-top order! */
163 
164     for (y = pdev->height - 1; y >= 0; y--) {
165         code = gdev_prn_copy_scan_lines(pdev, y, row, raster);
166         if (code < 0)
167             goto done;
168         gp_fwrite((const char *)row, bmp_raster, 1, file);
169     }
170 
171 done:
172     gs_free_object(pdev->memory, row, "bmp file buffer");
173 
174     return code;
175 }
176 
177 /* Write out a page in separated CMYK format. */
178 /* This routine is used for all formats. */
179 static int
bmp_cmyk_print_page(gx_device_printer * pdev,gp_file * file)180 bmp_cmyk_print_page(gx_device_printer * pdev, gp_file * file)
181 {
182     int plane_depth = pdev->color_info.depth / 4;
183     uint raster = (pdev->width * plane_depth + 7) >> 3;
184     /* BMP scan lines are padded to 32 bits. */
185     uint bmp_raster = raster + (-(int)raster & 3);
186     byte *row = gs_alloc_bytes(pdev->memory, bmp_raster, "bmp file buffer");
187     int y;
188     int code = 0;		/* return code */
189     int plane;
190 
191     if (row == 0)		/* can't allocate row buffer */
192         return_error(gs_error_VMerror);
193     memset(row+raster, 0, bmp_raster - raster); /* clear the padding bytes */
194 
195     for (plane = 0; plane <= 3; ++plane) {
196         gx_render_plane_t render_plane;
197 
198         /* Write the page header. */
199 
200         code = write_bmp_separated_header(pdev, file);
201         if (code < 0)
202             break;
203 
204         /* Write the contents of the image. */
205         /* BMP files want the image in bottom-to-top order! */
206 
207         gx_render_plane_init(&render_plane, (gx_device *)pdev, plane);
208         for (y = pdev->height - 1; y >= 0; y--) {
209             byte *actual_data;
210             uint actual_raster;
211 
212             code = gdev_prn_get_lines(pdev, y, 1, row, bmp_raster,
213                                       &actual_data, &actual_raster,
214                                       &render_plane);
215             if (code < 0)
216                 goto done;
217             gp_fwrite((const char *)actual_data, bmp_raster, 1, file);
218         }
219     }
220 
221 done:
222     gs_free_object(pdev->memory, row, "bmp file buffer");
223 
224     return code;
225 }
226