/src/leptonica/src/bmpio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | /*! |
28 | | * \file bmpio.c |
29 | | * <pre> |
30 | | * |
31 | | * Read bmp |
32 | | * PIX *pixReadStreamBmp() |
33 | | * PIX *pixReadMemBmp() |
34 | | * |
35 | | * Write bmp |
36 | | * l_int32 pixWriteStreamBmp() |
37 | | * l_int32 pixWriteMemBmp() |
38 | | * |
39 | | * </pre> |
40 | | */ |
41 | | |
42 | | #ifdef HAVE_CONFIG_H |
43 | | #include <config_auto.h> |
44 | | #endif /* HAVE_CONFIG_H */ |
45 | | |
46 | | #include <string.h> |
47 | | #include "allheaders.h" |
48 | | #include "pix_internal.h" |
49 | | #include "bmp.h" |
50 | | |
51 | | /* --------------------------------------------*/ |
52 | | #if USE_BMPIO /* defined in environ.h */ |
53 | | /* --------------------------------------------*/ |
54 | | |
55 | | /* Here we're setting the pixel value 0 to white (255) and the |
56 | | * value 1 to black (0). This is the convention for grayscale, but |
57 | | * the opposite of the convention for 1 bpp, where 0 is white |
58 | | * and 1 is black. Both colormap entries are opaque (alpha = 255) */ |
59 | | RGBA_QUAD bwmap[2] = { {255,255,255,255}, {0,0,0,255} }; |
60 | | |
61 | | /* Image dimension limits */ |
62 | | static const l_int32 L_MAX_ALLOWED_WIDTH = 1000000; |
63 | | static const l_int32 L_MAX_ALLOWED_HEIGHT = 1000000; |
64 | | static const l_int64 L_MAX_ALLOWED_PIXELS = 400000000LL; |
65 | | static const l_int32 L_MAX_ALLOWED_RES = 10000000; /* pixels/meter */ |
66 | | |
67 | | #ifndef NO_CONSOLE_IO |
68 | | #define DEBUG 0 |
69 | | #endif /* ~NO_CONSOLE_IO */ |
70 | | |
71 | | /*--------------------------------------------------------------* |
72 | | * Read bmp * |
73 | | *--------------------------------------------------------------*/ |
74 | | /*! |
75 | | * \brief pixReadStreamBmp() |
76 | | * |
77 | | * \param[in] fp file stream opened for read |
78 | | * \return pix, or NULL on error |
79 | | * |
80 | | * <pre> |
81 | | * Notes: |
82 | | * (1) Here are references on the bmp file format: |
83 | | * http://en.wikipedia.org/wiki/BMP_file_format |
84 | | * http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html |
85 | | * </pre> |
86 | | */ |
87 | | PIX * |
88 | | pixReadStreamBmp(FILE *fp) |
89 | 0 | { |
90 | 0 | l_uint8 *data; |
91 | 0 | size_t size; |
92 | 0 | PIX *pix; |
93 | |
|
94 | 0 | if (!fp) |
95 | 0 | return (PIX *)ERROR_PTR("fp not defined", __func__, NULL); |
96 | | |
97 | | /* Read data from file and decode into Y,U,V arrays */ |
98 | 0 | rewind(fp); |
99 | 0 | if ((data = l_binaryReadStream(fp, &size)) == NULL) |
100 | 0 | return (PIX *)ERROR_PTR("data not read", __func__, NULL); |
101 | | |
102 | 0 | pix = pixReadMemBmp(data, size); |
103 | 0 | LEPT_FREE(data); |
104 | 0 | return pix; |
105 | 0 | } |
106 | | |
107 | | |
108 | | /*! |
109 | | * \brief pixReadMemBmp() |
110 | | * |
111 | | * \param[in] cdata bmp data |
112 | | * \param[in] size number of bytes of bmp-formatted data |
113 | | * \return pix, or NULL on error |
114 | | * |
115 | | * <pre> |
116 | | * Notes: |
117 | | * (1) The BMP file is organized as follows: |
118 | | * * 14 byte fileheader |
119 | | * * Variable size infoheader: 40, 108 or 124 bytes. |
120 | | * We only use data in he first 40 bytes. |
121 | | * * Optional colormap, with size 4 * ncolors (in bytes) |
122 | | * * Image data |
123 | | * (2) 2 bpp bmp files are not valid in the original spec, but they |
124 | | * are valid in later versions. |
125 | | * (3) We support reading rgb files with bpp = 24 and rgba files |
126 | | * with 32 bpp. For the latter, the transparency component of |
127 | | * the generated pix is saved; however, for rgba images with |
128 | | * non-opaque transparent components, png provides more flexibility. |
129 | | * </pre> |
130 | | */ |
131 | | PIX * |
132 | | pixReadMemBmp(const l_uint8 *cdata, |
133 | | size_t size) |
134 | 43.9k | { |
135 | 43.9k | l_uint8 pel[4]; |
136 | 43.9k | l_uint8 *cmapBuf, *fdata, *data, *bmpih_b; |
137 | 43.9k | l_int16 bftype, depth, d; |
138 | 43.9k | l_int32 offset, width, height, height_neg, xres, yres, spp; |
139 | 43.9k | l_int32 compression, imagebytes, fdatabytes, cmapbytes, ncolors, maxcolors; |
140 | 43.9k | l_int32 fdatabpl, extrabytes, filebpp, pixWpl, pixBpl, i, j, k; |
141 | 43.9k | l_uint32 ihbytes; |
142 | 43.9k | l_uint32 *line, *pixdata, *pword; |
143 | 43.9k | l_int64 npixels; |
144 | 43.9k | BMP_FH *bmpfh; |
145 | 43.9k | BMP_IH bmpih; |
146 | 43.9k | PIX *pix, *pix1; |
147 | 43.9k | PIXCMAP *cmap; |
148 | | |
149 | 43.9k | if (!cdata) |
150 | 0 | return (PIX *)ERROR_PTR("cdata not defined", __func__, NULL); |
151 | 43.9k | if (size < sizeof(BMP_FH) + sizeof(BMP_IH)) |
152 | 0 | return (PIX *)ERROR_PTR("bmf size error", __func__, NULL); |
153 | | |
154 | | /* Verify this is an uncompressed bmp */ |
155 | 43.9k | bmpfh = (BMP_FH *)cdata; |
156 | 43.9k | bftype = bmpfh->bfType[0] + ((l_int32)bmpfh->bfType[1] << 8); |
157 | 43.9k | if (bftype != BMP_ID) |
158 | 0 | return (PIX *)ERROR_PTR("not bmf format", __func__, NULL); |
159 | 43.9k | memcpy(&bmpih, cdata + BMP_FHBYTES, BMP_IHBYTES); |
160 | 43.9k | compression = convertOnBigEnd32(bmpih.biCompression); |
161 | 43.9k | if (compression != 0) |
162 | 0 | return (PIX *)ERROR_PTR("cannot read compressed BMP files", |
163 | 43.9k | __func__, NULL); |
164 | | |
165 | | /* Find the offset from the beginning of the file to the image data */ |
166 | 43.9k | offset = bmpfh->bfOffBits[0]; |
167 | 43.9k | offset += (l_int32)bmpfh->bfOffBits[1] << 8; |
168 | 43.9k | offset += (l_int32)bmpfh->bfOffBits[2] << 16; |
169 | 43.9k | offset += (l_uint32)bmpfh->bfOffBits[3] << 24; |
170 | | |
171 | | /* Read the remaining useful data in the infoheader. |
172 | | * Note that the first 4 bytes give the infoheader size. |
173 | | * The infoheader pointer on sparc64 is not 32-bit aligned. */ |
174 | 43.9k | bmpih_b = (l_uint8 *)&bmpih; |
175 | 43.9k | ihbytes = bmpih_b[0] | ((l_int32)bmpih_b[1] << 8) | |
176 | 43.9k | ((l_int32)bmpih_b[2] << 16) | ((l_uint32)bmpih_b[3] << 24); |
177 | 43.9k | width = convertOnBigEnd32(bmpih.biWidth); |
178 | 43.9k | height = convertOnBigEnd32(bmpih.biHeight); |
179 | 43.9k | depth = convertOnBigEnd16(bmpih.biBitCount); |
180 | 43.9k | imagebytes = convertOnBigEnd32(bmpih.biSizeImage); |
181 | 43.9k | xres = convertOnBigEnd32(bmpih.biXPelsPerMeter); |
182 | 43.9k | yres = convertOnBigEnd32(bmpih.biYPelsPerMeter); |
183 | | |
184 | | /* Some sanity checking. We impose limits on the image |
185 | | * dimensions, resolution and number of pixels. We make sure the |
186 | | * file is the correct size to hold the amount of uncompressed data |
187 | | * that is specified in the header. The number of colormap |
188 | | * entries is checked: it can be either 0 (no cmap) or some |
189 | | * number between 2 and 256. |
190 | | * Note that the imagebytes for uncompressed images is either |
191 | | * 0 or the size of the file data. (The fact that it can |
192 | | * be 0 is perhaps some legacy glitch). */ |
193 | 43.9k | if (width < 1) |
194 | 0 | return (PIX *)ERROR_PTR("width < 1", __func__, NULL); |
195 | 43.9k | if (width > L_MAX_ALLOWED_WIDTH) |
196 | 0 | return (PIX *)ERROR_PTR("width too large", __func__, NULL); |
197 | 43.9k | if (height == 0 || height < -L_MAX_ALLOWED_HEIGHT || |
198 | 43.9k | height > L_MAX_ALLOWED_HEIGHT) |
199 | 0 | return (PIX *)ERROR_PTR("invalid height", __func__, NULL); |
200 | 43.9k | if (xres < 0 || xres > L_MAX_ALLOWED_RES || |
201 | 43.9k | yres < 0 || yres > L_MAX_ALLOWED_RES) |
202 | 0 | return (PIX *)ERROR_PTR("invalid resolution", __func__, NULL); |
203 | 43.9k | height_neg = 0; |
204 | 43.9k | if (height < 0) { |
205 | 0 | height_neg = 1; |
206 | 0 | height = -height; |
207 | 0 | } |
208 | 43.9k | if (ihbytes != 40 && ihbytes != 108 && ihbytes != 124) { |
209 | 0 | L_ERROR("invalid ihbytes = %d; not in {40, 108, 124}\n", |
210 | 0 | __func__, ihbytes); |
211 | 0 | return NULL; |
212 | 0 | } |
213 | 43.9k | npixels = 1LL * width * height; |
214 | 43.9k | if (npixels > L_MAX_ALLOWED_PIXELS) |
215 | 0 | return (PIX *)ERROR_PTR("npixels too large", __func__, NULL); |
216 | 43.9k | if (depth != 1 && depth != 2 && depth != 4 && depth != 8 && |
217 | 43.9k | depth != 16 && depth != 24 && depth != 32) { |
218 | 0 | L_ERROR("invalid depth = %d; not in {1, 2, 4, 8, 16, 24, 32}\n", |
219 | 0 | __func__, depth); |
220 | 0 | return NULL; |
221 | 0 | } |
222 | 43.9k | fdatabpl = 4 * ((1LL * width * depth + 31)/32); |
223 | 43.9k | fdatabytes = fdatabpl * height; |
224 | 43.9k | if (imagebytes != 0 && imagebytes != fdatabytes) { |
225 | 0 | L_ERROR("invalid imagebytes = %d; not equal to fdatabytes = %d\n", |
226 | 0 | __func__, imagebytes, fdatabytes); |
227 | 0 | return NULL; |
228 | 0 | } |
229 | | |
230 | | /* In the original spec, BITMAPINFOHEADER is 40 bytes. |
231 | | * There have been a number of revisions, to capture more information. |
232 | | * For example, the fifth version, BITMAPV5HEADER, adds 84 bytes |
233 | | * of ICC color profiles. We use the size of the infoheader |
234 | | * to accommodate these newer formats. Knowing the size of the |
235 | | * infoheader gives more opportunity to sanity check input params. */ |
236 | 43.9k | cmapbytes = offset - BMP_FHBYTES - ihbytes; |
237 | 43.9k | ncolors = cmapbytes / sizeof(RGBA_QUAD); |
238 | 43.9k | if (ncolors < 0 || ncolors == 1) |
239 | 0 | return (PIX *)ERROR_PTR("invalid: cmap size < 0 or 1", __func__, NULL); |
240 | 43.9k | if (ncolors > 0 && depth > 8) |
241 | 0 | return (PIX *)ERROR_PTR("can't have cmap for d > 8", __func__, NULL); |
242 | 43.9k | maxcolors = (depth <= 8) ? 1 << depth : 0; |
243 | 43.9k | if (ncolors > maxcolors) { |
244 | 0 | L_ERROR("cmap too large for depth %d: ncolors = %d > maxcolors = %d\n", |
245 | 0 | __func__, depth, ncolors, maxcolors); |
246 | 0 | return NULL; |
247 | 0 | } |
248 | 43.9k | if (size != 1LL * offset + 1LL * fdatabytes) |
249 | 0 | return (PIX *)ERROR_PTR("size incommensurate with image data", |
250 | 43.9k | __func__,NULL); |
251 | | |
252 | | /* Handle the colormap */ |
253 | 43.9k | cmapBuf = NULL; |
254 | 43.9k | if (ncolors > 0) { |
255 | 43.9k | if ((cmapBuf = (l_uint8 *)LEPT_CALLOC(ncolors, sizeof(RGBA_QUAD))) |
256 | 43.9k | == NULL) |
257 | 0 | return (PIX *)ERROR_PTR("cmapBuf alloc fail", __func__, NULL ); |
258 | | |
259 | | /* Read the colormap entry data from bmp. The RGBA_QUAD colormap |
260 | | * entries are used for both bmp and leptonica colormaps. */ |
261 | 43.9k | memcpy(cmapBuf, cdata + BMP_FHBYTES + ihbytes, |
262 | 43.9k | ncolors * sizeof(RGBA_QUAD)); |
263 | 43.9k | } |
264 | | |
265 | | /* Make a 32 bpp pix if file depth is 24 bpp */ |
266 | 43.9k | d = (depth == 24) ? 32 : depth; |
267 | 43.9k | if ((pix = pixCreate(width, height, d)) == NULL) { |
268 | 0 | LEPT_FREE(cmapBuf); |
269 | 0 | return (PIX *)ERROR_PTR( "pix not made", __func__, NULL); |
270 | 0 | } |
271 | 43.9k | pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ |
272 | 43.9k | pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ |
273 | 43.9k | pixSetInputFormat(pix, IFF_BMP); |
274 | 43.9k | pixWpl = pixGetWpl(pix); |
275 | 43.9k | pixBpl = 4 * pixWpl; |
276 | 43.9k | if (depth <= 16) |
277 | 43.9k | spp = 1; |
278 | 0 | else if (depth == 24) |
279 | 0 | spp = 3; |
280 | 0 | else /* depth == 32 */ |
281 | 0 | spp = 4; |
282 | 43.9k | pixSetSpp(pix, spp); |
283 | | |
284 | | /* Convert the bmp colormap to a pixcmap */ |
285 | 43.9k | cmap = NULL; |
286 | 43.9k | if (ncolors > 0) { /* import the colormap to the pix cmap */ |
287 | 43.9k | cmap = pixcmapCreate(L_MIN(d, 8)); |
288 | 43.9k | LEPT_FREE(cmap->array); /* remove generated cmap array */ |
289 | 43.9k | cmap->array = (void *)cmapBuf; /* and replace */ |
290 | 43.9k | cmap->n = L_MIN(ncolors, 256); |
291 | 11.2M | for (i = 0; i < cmap->n; i++) /* set all colors opaque */ |
292 | 11.2M | pixcmapSetAlpha (cmap, i, 255); |
293 | 43.9k | } |
294 | 43.9k | if (pixSetColormap(pix, cmap)) { |
295 | 0 | pixDestroy(&pix); |
296 | 0 | return (PIX *)ERROR_PTR("invalid colormap", __func__, NULL); |
297 | 0 | } |
298 | | |
299 | | /* Acquire the image data. Image origin for bmp is at lower right. */ |
300 | 43.9k | fdata = (l_uint8 *)cdata + offset; /* start of the bmp image data */ |
301 | 43.9k | pixdata = pixGetData(pix); |
302 | 43.9k | if (depth != 24 && depth != 32) { /* typ. 1 or 8 bpp */ |
303 | 43.9k | data = (l_uint8 *)pixdata + pixBpl * (height - 1); |
304 | 1.34M | for (i = 0; i < height; i++) { |
305 | 1.30M | memcpy(data, fdata, fdatabpl); |
306 | 1.30M | fdata += fdatabpl; |
307 | 1.30M | data -= pixBpl; |
308 | 1.30M | } |
309 | 43.9k | } else { /* 24 or 32 bpp file; 32 bpp pix |
310 | | * Note: for rgb bmp files, pel[0] is blue, pel[1] is green, |
311 | | * and pel[2] is red. This is opposite to the storage |
312 | | * in the pix, which puts the red pixel in the 0 byte, |
313 | | * the green in the 1 byte and the blue in the 2 byte. |
314 | | * Note also that all words are endian flipped after |
315 | | * assignment on L_LITTLE_ENDIAN platforms. |
316 | | * |
317 | | * We can then make these assignments for little endians: |
318 | | * SET_DATA_BYTE(pword, 1, pel[0]); blue |
319 | | * SET_DATA_BYTE(pword, 2, pel[1]); green |
320 | | * SET_DATA_BYTE(pword, 3, pel[2]); red |
321 | | * This looks like: |
322 | | * 3 (R) 2 (G) 1 (B) 0 |
323 | | * |-----------|------------|-----------|-----------| |
324 | | * and after byte flipping: |
325 | | * 3 2 (B) 1 (G) 0 (R) |
326 | | * |-----------|------------|-----------|-----------| |
327 | | * |
328 | | * For big endians we set: |
329 | | * SET_DATA_BYTE(pword, 2, pel[0]); blue |
330 | | * SET_DATA_BYTE(pword, 1, pel[1]); green |
331 | | * SET_DATA_BYTE(pword, 0, pel[2]); red |
332 | | * This looks like: |
333 | | * 0 (R) 1 (G) 2 (B) 3 |
334 | | * |-----------|------------|-----------|-----------| |
335 | | * so in both cases we get the correct assignment in the PIX. |
336 | | * |
337 | | * Can we do a platform-independent assignment? |
338 | | * Yes, set the bytes without using macros: |
339 | | * *((l_uint8 *)pword) = pel[2]; red |
340 | | * *((l_uint8 *)pword + 1) = pel[1]; green |
341 | | * *((l_uint8 *)pword + 2) = pel[0]; blue |
342 | | * For little endians, before flipping, this looks again like: |
343 | | * 3 (R) 2 (G) 1 (B) 0 |
344 | | * |-----------|------------|-----------|-----------| |
345 | | * |
346 | | * For reading an spp == 4 file with a transparency component, |
347 | | * the code below shows where the alpha component is located |
348 | | * in each pixel. |
349 | | */ |
350 | 0 | filebpp = (depth == 24) ? 3 : 4; |
351 | 0 | extrabytes = fdatabpl - filebpp * width; |
352 | 0 | line = pixdata + pixWpl * (height - 1); |
353 | 0 | for (i = 0; i < height; i++) { |
354 | 0 | for (j = 0; j < width; j++) { |
355 | 0 | pword = line + j; |
356 | 0 | memcpy(&pel, fdata, filebpp); |
357 | 0 | fdata += filebpp; |
358 | 0 | *((l_uint8 *)pword + COLOR_RED) = pel[2]; |
359 | 0 | *((l_uint8 *)pword + COLOR_GREEN) = pel[1]; |
360 | 0 | *((l_uint8 *)pword + COLOR_BLUE) = pel[0]; |
361 | | /* Set the alpha byte to opaque for rgb */ |
362 | 0 | if (depth == 24) |
363 | 0 | *((l_uint8 *)pword + L_ALPHA_CHANNEL) = 255; |
364 | 0 | else |
365 | 0 | *((l_uint8 *)pword + L_ALPHA_CHANNEL) = pel[3]; |
366 | 0 | } |
367 | 0 | if (extrabytes) { |
368 | 0 | for (k = 0; k < extrabytes; k++) { |
369 | 0 | memcpy(&pel, fdata, 1); |
370 | 0 | fdata++; |
371 | 0 | } |
372 | 0 | } |
373 | 0 | line -= pixWpl; |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | 43.9k | pixEndianByteSwap(pix); |
378 | 43.9k | if (height_neg) |
379 | 0 | pixFlipTB(pix, pix); |
380 | | |
381 | | /* ---------------------------------------------- |
382 | | * We do not use 1 bpp pix with colormaps in leptonica. |
383 | | * The colormap must be removed in such a way that the pixel |
384 | | * values are not changed. If the values are only black and |
385 | | * white, return a 1 bpp image; if gray, return an 8 bpp pix; |
386 | | * otherwise, return a 32 bpp rgb pix. |
387 | | * ---------------------------------------------- */ |
388 | 43.9k | if (depth == 1 && cmap) { |
389 | 0 | L_INFO("removing opaque cmap from 1 bpp\n", __func__); |
390 | 0 | pix1 = pixRemoveColormap(pix, REMOVE_CMAP_BASED_ON_SRC); |
391 | 0 | pixDestroy(&pix); |
392 | 0 | pix = pix1; /* rename */ |
393 | 0 | } |
394 | | |
395 | 43.9k | return pix; |
396 | 43.9k | } |
397 | | |
398 | | |
399 | | /*--------------------------------------------------------------* |
400 | | * Write bmp * |
401 | | *--------------------------------------------------------------*/ |
402 | | /*! |
403 | | * \brief pixWriteStreamBmp() |
404 | | * |
405 | | * \param[in] fp file stream |
406 | | * \param[in] pix all depths |
407 | | * \return 0 if OK, 1 on error |
408 | | */ |
409 | | l_ok |
410 | | pixWriteStreamBmp(FILE *fp, |
411 | | PIX *pix) |
412 | 0 | { |
413 | 0 | l_uint8 *data; |
414 | 0 | size_t size, nbytes; |
415 | |
|
416 | 0 | if (!fp) |
417 | 0 | return ERROR_INT("stream not defined", __func__, 1); |
418 | 0 | if (!pix) |
419 | 0 | return ERROR_INT("pix not defined", __func__, 1); |
420 | | |
421 | 0 | pixWriteMemBmp(&data, &size, pix); |
422 | 0 | rewind(fp); |
423 | 0 | nbytes = fwrite(data, 1, size, fp); |
424 | 0 | LEPT_FREE(data); |
425 | 0 | if (nbytes != size) { |
426 | 0 | L_ERROR("Truncation: nbytes = %zu, size = %zu\n", |
427 | 0 | __func__, nbytes, size); |
428 | 0 | return ERROR_INT("Write error", __func__, 1); |
429 | 0 | } |
430 | 0 | return 0; |
431 | 0 | } |
432 | | |
433 | | |
434 | | /*! |
435 | | * \brief pixWriteMemBmp() |
436 | | * |
437 | | * \param[out] pfdata data of bmp formatted image |
438 | | * \param[out] pfsize size of returned data |
439 | | * \param[in] pixs 1, 2, 4, 8, 16, 32 bpp |
440 | | * \return 0 if OK, 1 on error |
441 | | * |
442 | | * <pre> |
443 | | * Notes: |
444 | | * (1) 2 bpp bmp files are not valid in the original spec, and are |
445 | | * written as 8 bpp. |
446 | | * (2) pix with depth <= 8 bpp are written with a colormap. |
447 | | * 16 bpp gray and 32 bpp rgb pix are written without a colormap. |
448 | | * (3) The transparency component in an rgba (spp = 4) pix is written. |
449 | | * (4) The bmp colormap entries, RGBA_QUAD, are the same as |
450 | | * the ones used for colormaps in leptonica. This allows |
451 | | * a simple memcpy for bmp output. |
452 | | * </pre> |
453 | | */ |
454 | | l_ok |
455 | | pixWriteMemBmp(l_uint8 **pfdata, |
456 | | size_t *pfsize, |
457 | | PIX *pixs) |
458 | 43.9k | { |
459 | 43.9k | l_uint8 pel[4]; |
460 | 43.9k | l_uint8 *cta = NULL; /* address of the bmp color table array */ |
461 | 43.9k | l_uint8 *fdata, *data, *fmdata; |
462 | 43.9k | l_int32 cmaplen; /* number of bytes in the bmp colormap */ |
463 | 43.9k | l_int32 ncolors, val, stepsize, w, h, d, fdepth, xres, yres, valid; |
464 | 43.9k | l_int32 pixWpl, pixBpl, extrabytes, spp, fBpl, fWpl, i, j, k; |
465 | 43.9k | l_int32 heapcm; /* extra copy of cta on the heap ? 1 : 0 */ |
466 | 43.9k | l_uint32 offbytes, fimagebytes; |
467 | 43.9k | l_uint32 *line, *pword; |
468 | 43.9k | size_t fsize; |
469 | 43.9k | BMP_FH *bmpfh; |
470 | 43.9k | BMP_IH bmpih; |
471 | 43.9k | PIX *pix; |
472 | 43.9k | PIXCMAP *cmap; |
473 | 43.9k | RGBA_QUAD *pquad; |
474 | | |
475 | 43.9k | if (pfdata) *pfdata = NULL; |
476 | 43.9k | if (pfsize) *pfsize = 0; |
477 | 43.9k | if (!pfdata) |
478 | 0 | return ERROR_INT("&fdata not defined", __func__, 1 ); |
479 | 43.9k | if (!pfsize) |
480 | 0 | return ERROR_INT("&fsize not defined", __func__, 1 ); |
481 | 43.9k | if (!pixs) |
482 | 0 | return ERROR_INT("pixs not defined", __func__, 1); |
483 | | |
484 | | /* Verify validity of colormap */ |
485 | 43.9k | if ((cmap = pixGetColormap(pixs)) != NULL) { |
486 | 0 | pixcmapIsValid(cmap, pixs, &valid); |
487 | 0 | if (!valid) |
488 | 0 | return ERROR_INT("colormap is not valid", __func__, 1); |
489 | 0 | } |
490 | | |
491 | 43.9k | pixGetDimensions(pixs, &w, &h, &d); |
492 | 43.9k | spp = pixGetSpp(pixs); |
493 | 43.9k | if (spp != 1 && spp != 3 && spp != 4) { |
494 | 0 | L_ERROR("unsupported spp = %d\n", __func__, spp); |
495 | 0 | return 1; |
496 | 0 | } |
497 | 43.9k | if (d == 2) { |
498 | 0 | L_WARNING("2 bpp files can't be read; converting to 8 bpp\n", |
499 | 0 | __func__); |
500 | 0 | pix = pixConvert2To8(pixs, 0, 85, 170, 255, 1); |
501 | 0 | d = 8; |
502 | 43.9k | } else if (d == 24) { |
503 | 0 | pix = pixConvert24To32(pixs); |
504 | 0 | d = 32; |
505 | 43.9k | } else { |
506 | 43.9k | pix = pixCopy(NULL, pixs); |
507 | 43.9k | } |
508 | | |
509 | | /* Find the bits/pixel to be written to file */ |
510 | 43.9k | if (spp == 1) |
511 | 43.9k | fdepth = d; |
512 | 0 | else if (spp == 3) |
513 | 0 | fdepth = 24; |
514 | 0 | else /* spp == 4 */ |
515 | 0 | fdepth = 32; |
516 | | |
517 | | /* Resolution is given in pixels/meter */ |
518 | 43.9k | xres = (l_int32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); |
519 | 43.9k | yres = (l_int32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); |
520 | | |
521 | 43.9k | pixWpl = pixGetWpl(pix); |
522 | 43.9k | pixBpl = 4 * pixWpl; |
523 | 43.9k | fWpl = (w * fdepth + 31) / 32; |
524 | 43.9k | fBpl = 4 * fWpl; |
525 | 43.9k | fimagebytes = h * fBpl; |
526 | 43.9k | if (fimagebytes > 4LL * L_MAX_ALLOWED_PIXELS) { |
527 | 0 | pixDestroy(&pix); |
528 | 0 | return ERROR_INT("image data is too large", __func__, 1); |
529 | 0 | } |
530 | | |
531 | | /* If not rgb or 16 bpp, the bmp data is required to have a colormap */ |
532 | 43.9k | heapcm = 0; |
533 | 43.9k | if (d == 32 || d == 16) { /* 24 bpp rgb or 16 bpp: no colormap */ |
534 | 0 | ncolors = 0; |
535 | 0 | cmaplen = 0; |
536 | 43.9k | } else if ((cmap = pixGetColormap(pix))) { /* existing colormap */ |
537 | 0 | ncolors = pixcmapGetCount(cmap); |
538 | 0 | cmaplen = ncolors * sizeof(RGBA_QUAD); |
539 | 0 | cta = (l_uint8 *)cmap->array; |
540 | 43.9k | } else { /* no existing colormap; d <= 8; make a binary or gray one */ |
541 | 43.9k | if (d == 1) { |
542 | 0 | cmaplen = sizeof(bwmap); |
543 | 0 | ncolors = 2; |
544 | 0 | cta = (l_uint8 *)bwmap; |
545 | 43.9k | } else { /* d = 2,4,8; use a grayscale output colormap */ |
546 | 43.9k | ncolors = 1 << d; |
547 | 43.9k | cmaplen = ncolors * sizeof(RGBA_QUAD); |
548 | 43.9k | heapcm = 1; |
549 | 43.9k | cta = (l_uint8 *)LEPT_CALLOC(cmaplen, 1); |
550 | 43.9k | stepsize = 255 / (ncolors - 1); |
551 | 43.9k | for (i = 0, val = 0, pquad = (RGBA_QUAD *)cta; |
552 | 11.2M | i < ncolors; |
553 | 11.2M | i++, val += stepsize, pquad++) { |
554 | 11.2M | pquad->blue = pquad->green = pquad->red = val; |
555 | 11.2M | pquad->alpha = 255; /* opaque */ |
556 | 11.2M | } |
557 | 43.9k | } |
558 | 43.9k | } |
559 | | |
560 | | #if DEBUG |
561 | | if (pixGetColormap(pix) != NULL) { |
562 | | l_uint8 *pcmptr; |
563 | | pcmptr = (l_uint8 *)pixGetColormap(pix)->array; |
564 | | lept_stderr("Pix colormap[0] = %c%c%c%d\n", |
565 | | pcmptr[0], pcmptr[1], pcmptr[2], pcmptr[3]); |
566 | | lept_stderr("Pix colormap[1] = %c%c%c%d\n", |
567 | | pcmptr[4], pcmptr[5], pcmptr[6], pcmptr[7]); |
568 | | } |
569 | | #endif /* DEBUG */ |
570 | | |
571 | 43.9k | offbytes = BMP_FHBYTES + BMP_IHBYTES + cmaplen; |
572 | 43.9k | fsize = offbytes + fimagebytes; |
573 | 43.9k | fdata = (l_uint8 *)LEPT_CALLOC(fsize, 1); |
574 | 43.9k | *pfdata = fdata; |
575 | 43.9k | *pfsize = fsize; |
576 | | |
577 | | /* Write little-endian file header data */ |
578 | 43.9k | bmpfh = (BMP_FH *)fdata; |
579 | 43.9k | bmpfh->bfType[0] = (l_uint8)(BMP_ID >> 0); |
580 | 43.9k | bmpfh->bfType[1] = (l_uint8)(BMP_ID >> 8); |
581 | 43.9k | bmpfh->bfSize[0] = (l_uint8)(fsize >> 0); |
582 | 43.9k | bmpfh->bfSize[1] = (l_uint8)(fsize >> 8); |
583 | 43.9k | bmpfh->bfSize[2] = (l_uint8)(fsize >> 16); |
584 | 43.9k | bmpfh->bfSize[3] = (l_uint8)(fsize >> 24); |
585 | 43.9k | bmpfh->bfOffBits[0] = (l_uint8)(offbytes >> 0); |
586 | 43.9k | bmpfh->bfOffBits[1] = (l_uint8)(offbytes >> 8); |
587 | 43.9k | bmpfh->bfOffBits[2] = (l_uint8)(offbytes >> 16); |
588 | 43.9k | bmpfh->bfOffBits[3] = (l_uint8)(offbytes >> 24); |
589 | | |
590 | | /* Convert to little-endian and write the info header data */ |
591 | 43.9k | bmpih.biSize = convertOnBigEnd32(BMP_IHBYTES); |
592 | 43.9k | bmpih.biWidth = convertOnBigEnd32(w); |
593 | 43.9k | bmpih.biHeight = convertOnBigEnd32(h); |
594 | 43.9k | bmpih.biPlanes = convertOnBigEnd16(1); |
595 | 43.9k | bmpih.biBitCount = convertOnBigEnd16(fdepth); |
596 | 43.9k | bmpih.biCompression = 0; |
597 | 43.9k | bmpih.biSizeImage = convertOnBigEnd32(fimagebytes); |
598 | 43.9k | bmpih.biXPelsPerMeter = convertOnBigEnd32(xres); |
599 | 43.9k | bmpih.biYPelsPerMeter = convertOnBigEnd32(yres); |
600 | 43.9k | bmpih.biClrUsed = convertOnBigEnd32(ncolors); |
601 | 43.9k | bmpih.biClrImportant = convertOnBigEnd32(ncolors); |
602 | 43.9k | memcpy(fdata + BMP_FHBYTES, &bmpih, BMP_IHBYTES); |
603 | | |
604 | | /* Copy the colormap data and free the cta if necessary */ |
605 | 43.9k | if (ncolors > 0) { |
606 | 43.9k | memcpy(fdata + BMP_FHBYTES + BMP_IHBYTES, cta, cmaplen); |
607 | 43.9k | if (heapcm) LEPT_FREE(cta); |
608 | 43.9k | } |
609 | | |
610 | | /* When you write a binary image with a colormap |
611 | | * that sets BLACK to 0, you must invert the data */ |
612 | 43.9k | if (fdepth == 1 && cmap && ((l_uint8 *)(cmap->array))[0] == 0x0) { |
613 | 0 | pixInvert(pix, pix); |
614 | 0 | } |
615 | | |
616 | | /* An endian byte swap is also required */ |
617 | 43.9k | pixEndianByteSwap(pix); |
618 | | |
619 | | /* Transfer the image data. Image origin for bmp is at lower right. */ |
620 | 43.9k | fmdata = fdata + offbytes; |
621 | 43.9k | if (fdepth != 24 && fdepth != 32) { /* typ 1 or 8 bpp */ |
622 | 43.9k | data = (l_uint8 *)pixGetData(pix) + pixBpl * (h - 1); |
623 | 1.34M | for (i = 0; i < h; i++) { |
624 | 1.30M | memcpy(fmdata, data, fBpl); |
625 | 1.30M | data -= pixBpl; |
626 | 1.30M | fmdata += fBpl; |
627 | 1.30M | } |
628 | 43.9k | } else { /* 32 bpp pix; 24 bpp or 32 bpp file |
629 | | * See the comments in pixReadStreamBmp() to |
630 | | * understand the logic behind the pixel ordering below. |
631 | | * Note that we have again done an endian swap on |
632 | | * little endian machines before arriving here, so that |
633 | | * the bytes are ordered on both platforms as: |
634 | | * Red Green Blue -- |
635 | | * |-----------|------------|-----------|-----------| |
636 | | * |
637 | | * For writing an spp == 4 file, the code below shows where |
638 | | * the alpha component is written to file in each pixel. |
639 | | */ |
640 | 0 | extrabytes = fBpl - spp * w; |
641 | 0 | line = pixGetData(pix) + pixWpl * (h - 1); |
642 | 0 | for (i = 0; i < h; i++) { |
643 | 0 | for (j = 0; j < w; j++) { |
644 | 0 | pword = line + j; |
645 | 0 | pel[2] = *((l_uint8 *)pword + COLOR_RED); |
646 | 0 | pel[1] = *((l_uint8 *)pword + COLOR_GREEN); |
647 | 0 | pel[0] = *((l_uint8 *)pword + COLOR_BLUE); |
648 | 0 | if (spp == 4) |
649 | 0 | pel[3] = *((l_uint8 *)pword + L_ALPHA_CHANNEL); |
650 | 0 | memcpy(fmdata, &pel, spp); |
651 | 0 | fmdata += spp; |
652 | 0 | } |
653 | 0 | if (extrabytes) { |
654 | 0 | for (k = 0; k < extrabytes; k++) { |
655 | 0 | memcpy(fmdata, &pel, 1); |
656 | 0 | fmdata++; |
657 | 0 | } |
658 | 0 | } |
659 | 0 | line -= pixWpl; |
660 | 0 | } |
661 | 0 | } |
662 | | |
663 | 43.9k | pixDestroy(&pix); |
664 | 43.9k | return 0; |
665 | 43.9k | } |
666 | | |
667 | | /* --------------------------------------------*/ |
668 | | #endif /* USE_BMPIO */ |