1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup imbuf
22 */
23
24 #include <math.h>
25
26 #include "BLI_fileops.h"
27 #include "BLI_utildefines.h"
28
29 #include "imbuf.h"
30
31 #include "IMB_filetype.h"
32 #include "IMB_imbuf.h"
33 #include "IMB_imbuf_types.h"
34
35 #include "IMB_colormanagement.h"
36 #include "IMB_colormanagement_intern.h"
37
38 /* Some code copied from article on microsoft.com,
39 * copied here for enhanced BMP support in the future:
40 * http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/mfcp1/mfcp1.htm&nav=/msj/0197/newnav.htm
41 */
42
43 typedef struct BMPINFOHEADER {
44 uint biSize;
45 uint biWidth;
46 uint biHeight;
47 ushort biPlanes;
48 ushort biBitCount;
49 uint biCompression;
50 uint biSizeImage;
51 uint biXPelsPerMeter;
52 uint biYPelsPerMeter;
53 uint biClrUsed;
54 uint biClrImportant;
55 } BMPINFOHEADER;
56
57 #if 0
58 typedef struct BMPHEADER {
59 ushort biType;
60 uint biSize;
61 ushort biRes1;
62 ushort biRes2;
63 uint biOffBits;
64 } BMPHEADER;
65 #endif
66
67 #define BMP_FILEHEADER_SIZE 14
68
69 #define CHECK_HEADER_FIELD(_mem, _field) ((_mem[0] == _field[0]) && (_mem[1] == _field[1]))
70 #define CHECK_HEADER_FIELD_BMP(_mem) \
71 (CHECK_HEADER_FIELD(_mem, "BM") || CHECK_HEADER_FIELD(_mem, "BA") || \
72 CHECK_HEADER_FIELD(_mem, "CI") || CHECK_HEADER_FIELD(_mem, "CP") || \
73 CHECK_HEADER_FIELD(_mem, "IC") || CHECK_HEADER_FIELD(_mem, "PT"))
74
checkbmp(const uchar * mem)75 static int checkbmp(const uchar *mem)
76 {
77
78 int ret_val = 0;
79 BMPINFOHEADER bmi;
80 uint u;
81
82 if (mem) {
83 if (CHECK_HEADER_FIELD_BMP(mem)) {
84 /* skip fileheader */
85 mem += BMP_FILEHEADER_SIZE;
86 }
87 else {
88 return 0;
89 }
90
91 /* for systems where an int needs to be 4 bytes aligned */
92 memcpy(&bmi, mem, sizeof(bmi));
93
94 u = LITTLE_LONG(bmi.biSize);
95 /* we only support uncompressed images for now. */
96 if (u >= sizeof(BMPINFOHEADER)) {
97 if (bmi.biCompression == 0) {
98 u = LITTLE_SHORT(bmi.biBitCount);
99 if (u > 0 && u <= 32) {
100 ret_val = 1;
101 }
102 }
103 }
104 }
105
106 return ret_val;
107 }
108
imb_is_a_bmp(const uchar * buf)109 int imb_is_a_bmp(const uchar *buf)
110 {
111 return checkbmp(buf);
112 }
113
imb_bmp_decode(const uchar * mem,size_t size,int flags,char colorspace[IM_MAX_SPACE])114 ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
115 {
116 ImBuf *ibuf = NULL;
117 BMPINFOHEADER bmi;
118 int x, y, depth, ibuf_depth, skip;
119 const uchar *bmp;
120 uchar *rect;
121 ushort col;
122 double xppm, yppm;
123 bool top_to_bottom = false;
124
125 (void)size; /* unused */
126
127 if (checkbmp(mem) == 0) {
128 return NULL;
129 }
130
131 colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
132
133 bmp = mem + LITTLE_LONG(*(int *)(mem + 10));
134
135 if (CHECK_HEADER_FIELD_BMP(mem)) {
136 /* skip fileheader */
137 mem += BMP_FILEHEADER_SIZE;
138 }
139 else {
140 return NULL;
141 }
142
143 /* for systems where an int needs to be 4 bytes aligned */
144 memcpy(&bmi, mem, sizeof(bmi));
145
146 skip = LITTLE_LONG(bmi.biSize);
147 x = LITTLE_LONG(bmi.biWidth);
148 y = LITTLE_LONG(bmi.biHeight);
149 depth = LITTLE_SHORT(bmi.biBitCount);
150 xppm = LITTLE_LONG(bmi.biXPelsPerMeter);
151 yppm = LITTLE_LONG(bmi.biYPelsPerMeter);
152
153 if (depth <= 8) {
154 ibuf_depth = 24;
155 }
156 else {
157 ibuf_depth = depth;
158 }
159
160 if (y < 0) {
161 /* Negative height means bitmap is stored top-to-bottom... */
162 y = -y;
163 top_to_bottom = true;
164 }
165
166 #if 0
167 printf("skip: %d, x: %d y: %d, depth: %d (%x)\n", skip, x, y, depth, bmi.biBitCount);
168 #endif
169
170 if (flags & IB_test) {
171 ibuf = IMB_allocImBuf(x, y, ibuf_depth, 0);
172 }
173 else {
174 ibuf = IMB_allocImBuf(x, y, ibuf_depth, IB_rect);
175 if (!ibuf) {
176 return NULL;
177 }
178
179 rect = (uchar *)ibuf->rect;
180
181 if (depth <= 8) {
182 const int rowsize = (depth * x + 31) / 32 * 4;
183 const char(*palette)[4] = (void *)(mem + skip);
184 const int startmask = ((1 << depth) - 1) << 8;
185 for (size_t i = y; i > 0; i--) {
186 int index;
187 int bitoffs = 8;
188 int bitmask = startmask;
189 int nbytes = 0;
190 const char *pcol;
191 if (top_to_bottom) {
192 rect = (uchar *)&ibuf->rect[(i - 1) * x];
193 }
194 for (size_t j = x; j > 0; j--) {
195 bitoffs -= depth;
196 bitmask >>= depth;
197 index = (bmp[0] & bitmask) >> bitoffs;
198 pcol = palette[index];
199 /* intentionally BGR -> RGB */
200 rect[0] = pcol[2];
201 rect[1] = pcol[1];
202 rect[2] = pcol[0];
203
204 rect[3] = 255;
205 rect += 4;
206 if (bitoffs == 0) {
207 /* Advance to the next byte */
208 bitoffs = 8;
209 bitmask = startmask;
210 nbytes += 1;
211 bmp += 1;
212 }
213 }
214 /* Advance to the next row */
215 bmp += (rowsize - nbytes);
216 }
217 }
218 else if (depth == 16) {
219 for (size_t i = y; i > 0; i--) {
220 if (top_to_bottom) {
221 rect = (uchar *)&ibuf->rect[(i - 1) * x];
222 }
223 for (size_t j = x; j > 0; j--) {
224 col = bmp[0] + (bmp[1] << 8);
225 rect[0] = ((col >> 10) & 0x1f) << 3;
226 rect[1] = ((col >> 5) & 0x1f) << 3;
227 rect[2] = ((col >> 0) & 0x1f) << 3;
228
229 rect[3] = 255;
230 rect += 4;
231 bmp += 2;
232 }
233 }
234 }
235 else if (depth == 24) {
236 const int x_pad = x % 4;
237 for (size_t i = y; i > 0; i--) {
238 if (top_to_bottom) {
239 rect = (uchar *)&ibuf->rect[(i - 1) * x];
240 }
241 for (size_t j = x; j > 0; j--) {
242 rect[0] = bmp[2];
243 rect[1] = bmp[1];
244 rect[2] = bmp[0];
245
246 rect[3] = 255;
247 rect += 4;
248 bmp += 3;
249 }
250 /* for 24-bit images, rows are padded to multiples of 4 */
251 bmp += x_pad;
252 }
253 }
254 else if (depth == 32) {
255 for (size_t i = y; i > 0; i--) {
256 if (top_to_bottom) {
257 rect = (uchar *)&ibuf->rect[(i - 1) * x];
258 }
259 for (size_t j = x; j > 0; j--) {
260 rect[0] = bmp[2];
261 rect[1] = bmp[1];
262 rect[2] = bmp[0];
263 rect[3] = bmp[3];
264 rect += 4;
265 bmp += 4;
266 }
267 }
268 }
269 }
270
271 if (ibuf) {
272 ibuf->ppm[0] = xppm;
273 ibuf->ppm[1] = yppm;
274 ibuf->ftype = IMB_FTYPE_BMP;
275 }
276
277 return ibuf;
278 }
279
280 #undef CHECK_HEADER_FIELD_BMP
281 #undef CHECK_HEADER_FIELD
282
283 /* Couple of helper functions for writing our data */
putIntLSB(uint ui,FILE * ofile)284 static int putIntLSB(uint ui, FILE *ofile)
285 {
286 putc((ui >> 0) & 0xFF, ofile);
287 putc((ui >> 8) & 0xFF, ofile);
288 putc((ui >> 16) & 0xFF, ofile);
289 return putc((ui >> 24) & 0xFF, ofile);
290 }
291
putShortLSB(ushort us,FILE * ofile)292 static int putShortLSB(ushort us, FILE *ofile)
293 {
294 putc((us >> 0) & 0xFF, ofile);
295 return putc((us >> 8) & 0xFF, ofile);
296 }
297
298 /* Found write info at http://users.ece.gatech.edu/~slabaugh/personal/c/bitmapUnix.c */
imb_savebmp(ImBuf * ibuf,const char * filepath,int UNUSED (flags))299 int imb_savebmp(ImBuf *ibuf, const char *filepath, int UNUSED(flags))
300 {
301 BMPINFOHEADER infoheader;
302
303 const size_t bytes_per_pixel = (ibuf->planes + 7) >> 3;
304 BLI_assert(bytes_per_pixel == 1 || bytes_per_pixel == 3);
305
306 const size_t pad_bytes_per_scanline = (4 - ibuf->x * bytes_per_pixel % 4) % 4;
307 const size_t bytesize = (ibuf->x * bytes_per_pixel + pad_bytes_per_scanline) * ibuf->y;
308
309 const uchar *data = (const uchar *)ibuf->rect;
310 FILE *ofile = BLI_fopen(filepath, "wb");
311 if (ofile == NULL) {
312 return 0;
313 }
314
315 const bool is_grayscale = bytes_per_pixel == 1;
316 const size_t palette_size = is_grayscale ? 255 * 4 : 0; /* RGBA32 */
317 const size_t pixel_array_start = BMP_FILEHEADER_SIZE + sizeof(infoheader) + palette_size;
318
319 putShortLSB(19778, ofile); /* "BM" */
320 putIntLSB(bytesize + pixel_array_start, ofile); /* Total file size */
321 putShortLSB(0, ofile); /* Res1 */
322 putShortLSB(0, ofile); /* Res2 */
323 putIntLSB(pixel_array_start, ofile); /* offset to start of pixel array */
324
325 putIntLSB(sizeof(infoheader), ofile);
326 putIntLSB(ibuf->x, ofile);
327 putIntLSB(ibuf->y, ofile);
328 putShortLSB(1, ofile);
329 putShortLSB(is_grayscale ? 8 : 24, ofile);
330 putIntLSB(0, ofile);
331 putIntLSB(bytesize, ofile);
332 putIntLSB(round(ibuf->ppm[0]), ofile);
333 putIntLSB(round(ibuf->ppm[1]), ofile);
334 putIntLSB(0, ofile);
335 putIntLSB(0, ofile);
336
337 /* color palette table, which is just every grayscale color, full alpha */
338 if (is_grayscale) {
339 for (char i = 0; i < 255; i++) {
340 putc(i, ofile);
341 putc(i, ofile);
342 putc(i, ofile);
343 putc(0xFF, ofile);
344 }
345 }
346
347 if (is_grayscale) {
348 for (size_t y = 0; y < ibuf->y; y++) {
349 for (size_t x = 0; x < ibuf->x; x++) {
350 const size_t ptr = (x + y * ibuf->x) * 4;
351 if (putc(data[ptr], ofile) == EOF) {
352 return 0;
353 }
354 }
355 /* Add padding here. */
356 for (size_t t = 0; t < pad_bytes_per_scanline; t++) {
357 if (putc(0, ofile) == EOF) {
358 return 0;
359 }
360 }
361 }
362 }
363 else {
364 /* Need to write out padded image data in BGR format. */
365 for (size_t y = 0; y < ibuf->y; y++) {
366 for (size_t x = 0; x < ibuf->x; x++) {
367 const size_t ptr = (x + y * ibuf->x) * 4;
368 if (putc(data[ptr + 2], ofile) == EOF) {
369 return 0;
370 }
371 if (putc(data[ptr + 1], ofile) == EOF) {
372 return 0;
373 }
374 if (putc(data[ptr], ofile) == EOF) {
375 return 0;
376 }
377 }
378 /* Add padding here. */
379 for (size_t t = 0; t < pad_bytes_per_scanline; t++) {
380 if (putc(0, ofile) == EOF) {
381 return 0;
382 }
383 }
384 }
385 }
386 if (ofile) {
387 fflush(ofile);
388 fclose(ofile);
389 }
390 return 1;
391 }
392