1 /* Copyright (C)2004 Landmark Graphics Corporation
2 * Copyright (C)2005 Sun Microsystems, Inc.
3 * Copyright (C)2014, 2017-2019 D. R. Commander
4 *
5 * This library is free software and may be redistributed and/or modified under
6 * the terms of the wxWindows Library License, Version 3.1 or (at your option)
7 * any later version. The full license is in the LICENSE.txt file included
8 * with this distribution.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * wxWindows Library License for more details.
14 */
15
16 #include <fcntl.h>
17 #include <sys/types.h>
18 #include <sys/stat.h>
19 #include <errno.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #ifdef _WIN32
24 #include <io.h>
25 #else
26 #include <unistd.h>
27 #endif
28 #include "vglutil.h"
29 #include "bmp.h"
30
31
32 #ifndef BI_RGB
33 #define BI_RGB 0L
34 #endif
35
36
37 #define BMPHDRSIZE 54
38 typedef struct
39 {
40 unsigned short bfType;
41 unsigned int bfSize;
42 unsigned short bfReserved1, bfReserved2;
43 unsigned int bfOffBits;
44
45 unsigned int biSize;
46 int biWidth, biHeight;
47 unsigned short biPlanes, biBitCount;
48 unsigned int biCompression, biSizeImage;
49 int biXPelsPerMeter, biYPelsPerMeter;
50 unsigned int biClrUsed, biClrImportant;
51 } BitmapHeader;
52
53
54 static const char *errorStr = "No error";
55
56
57 #define THROW(m) \
58 { \
59 errorStr = m; ret = -1; goto finally; \
60 }
61 #define TRY_UNIX(f) \
62 { \
63 if((f) == -1) THROW(strerror(errno)); \
64 }
65 #define CATCH(f) \
66 { \
67 if((f) == -1) \
68 { \
69 ret = -1; goto finally; \
70 } \
71 }
72
73
74 #define READ(fd, addr, size) \
75 if((bytesRead = read(fd, addr, (size))) == -1) THROW(strerror(errno)); \
76 if(bytesRead != (size)) THROW("Read error");
77
78 #define WRITE(fd, addr, size) \
79 if((bytesWritten = write(fd, addr, (size))) == -1) THROW(strerror(errno)); \
80 if(bytesWritten != (size)) THROW("Write error");
81
82
pixelConvert(unsigned char * srcBuf,int width,int srcPitch,int height,PF * srcpf,unsigned char * dstBuf,int dstPitch,PF * dstpf,int flip)83 static void pixelConvert(unsigned char *srcBuf, int width, int srcPitch,
84 int height, PF *srcpf, unsigned char *dstBuf, int dstPitch, PF *dstpf,
85 int flip)
86 {
87 if(flip)
88 {
89 srcBuf = &srcBuf[srcPitch * (height - 1)];
90 srcPitch = -srcPitch;
91 }
92 srcpf->convert(srcBuf, width, srcPitch, height, dstBuf, dstPitch, dstpf);
93 while(height--)
94 {
95 if(width * dstpf->size != dstPitch)
96 memset(&dstBuf[width * dstpf->size], 0, dstPitch - width * dstpf->size);
97 dstBuf += dstPitch;
98 }
99 }
100
101
ppm_load(int * fd,unsigned char ** buf,int * width,int align,int * height,PF * pf,enum BMPORN orientation,int ascii)102 static int ppm_load(int *fd, unsigned char **buf, int *width, int align,
103 int *height, PF *pf, enum BMPORN orientation, int ascii)
104 {
105 FILE *file = NULL; int ret = 0, scaleFactor, dstPitch;
106 unsigned char *tempbuf = NULL; char temps[255], temps2[255];
107 int numRead = 0, totalRead = 0, pixel[3], i, j;
108
109 if((file = fdopen(*fd, "r")) == NULL) THROW(strerror(errno));
110
111 do
112 {
113 if(!fgets(temps, 255, file)) THROW("Read error");
114 if(strlen(temps) == 0 || temps[0] == '\n') continue;
115 if(sscanf(temps, "%s", temps2) == 1 && temps2[1] == '#') continue;
116 switch(totalRead)
117 {
118 case 0:
119 if((numRead = sscanf(temps, "%d %d %d", width, height,
120 &scaleFactor)) == EOF)
121 THROW("Read error");
122 break;
123 case 1:
124 if((numRead = sscanf(temps, "%d %d", height, &scaleFactor)) == EOF)
125 THROW("Read error");
126 break;
127 case 2:
128 if((numRead = sscanf(temps, "%d", &scaleFactor)) == EOF)
129 THROW("Read error");
130 break;
131 }
132 totalRead += numRead;
133 } while(totalRead < 3);
134 if((*width) < 1 || (*height) < 1 || scaleFactor < 1)
135 THROW("Corrupt PPM header");
136
137 dstPitch = (((*width) * pf->size) + (align - 1)) & (~(align - 1));
138 if((*buf = (unsigned char *)malloc(dstPitch * (*height))) == NULL)
139 THROW("Memory allocation error");
140 if(ascii)
141 {
142 for(j = 0; j < *height; j++)
143 {
144 for(i = 0; i < *width; i++)
145 {
146 if(fscanf(file, "%d%d%d", &pixel[0], &pixel[1], &pixel[2]) != 3)
147 THROW("Read error");
148 pf->setRGB(&(*buf)[j * dstPitch + i * pf->size],
149 pixel[0] * 255 / scaleFactor, pixel[1] * 255 / scaleFactor,
150 pixel[2] * 255 / scaleFactor);
151 }
152 }
153 }
154 else
155 {
156 if(scaleFactor != 255)
157 THROW("Binary PPMs must have 8-bit components");
158 if((tempbuf = (unsigned char *)malloc((*width) * (*height) * 3)) == NULL)
159 THROW("Memory allocation error");
160 if(fread(tempbuf, (*width) * (*height) * 3, 1, file) != 1)
161 THROW("Read error");
162 pixelConvert(tempbuf, *width, (*width) * 3, *height, pf_get(PF_RGB), *buf,
163 dstPitch, pf, orientation == BMPORN_BOTTOMUP);
164 }
165
166 finally:
167 if(file) { fclose(file); *fd = -1; }
168 free(tempbuf);
169 return ret;
170 }
171
172
bmp_load(char * filename,unsigned char ** buf,int * width,int align,int * height,int format,enum BMPORN orientation)173 int bmp_load(char *filename, unsigned char **buf, int *width, int align,
174 int *height, int format, enum BMPORN orientation)
175 {
176 int fd = -1, bytesRead, srcPitch, srcPixelSize, dstPitch, ret = 0;
177 enum BMPORN srcOrientation = BMPORN_BOTTOMUP;
178 unsigned char *tempbuf = NULL;
179 BitmapHeader bh; int flags = O_RDONLY;
180 PF *pf = pf_get(format);
181
182 #ifdef _WIN32
183 flags |= O_BINARY;
184 #endif
185 if(!filename || !buf || !width || !height || pf->bpc < 8 || align < 1)
186 THROW("Invalid argument to bmp_load()");
187 if((align & (align - 1)) != 0)
188 THROW("Alignment must be a power of 2");
189 TRY_UNIX(fd = open(filename, flags));
190
191 READ(fd, &bh.bfType, sizeof(unsigned short));
192 if(!LittleEndian()) bh.bfType = BYTESWAP16(bh.bfType);
193
194 if(bh.bfType == 0x3650)
195 {
196 CATCH(ppm_load(&fd, buf, width, align, height, pf, orientation, 0));
197 goto finally;
198 }
199 if(bh.bfType == 0x3350)
200 {
201 CATCH(ppm_load(&fd, buf, width, align, height, pf, orientation, 1));
202 goto finally;
203 }
204
205 READ(fd, &bh.bfSize, sizeof(unsigned int));
206 READ(fd, &bh.bfReserved1, sizeof(unsigned short));
207 READ(fd, &bh.bfReserved2, sizeof(unsigned short));
208 READ(fd, &bh.bfOffBits, sizeof(unsigned int));
209 READ(fd, &bh.biSize, sizeof(unsigned int));
210 READ(fd, &bh.biWidth, sizeof(int));
211 READ(fd, &bh.biHeight, sizeof(int));
212 READ(fd, &bh.biPlanes, sizeof(unsigned short));
213 READ(fd, &bh.biBitCount, sizeof(unsigned short));
214 READ(fd, &bh.biCompression, sizeof(unsigned int));
215 READ(fd, &bh.biSizeImage, sizeof(unsigned int));
216 READ(fd, &bh.biXPelsPerMeter, sizeof(int));
217 READ(fd, &bh.biYPelsPerMeter, sizeof(int));
218 READ(fd, &bh.biClrUsed, sizeof(unsigned int));
219 READ(fd, &bh.biClrImportant, sizeof(unsigned int));
220
221 if(!LittleEndian())
222 {
223 bh.bfSize = BYTESWAP(bh.bfSize);
224 bh.bfOffBits = BYTESWAP(bh.bfOffBits);
225 bh.biSize = BYTESWAP(bh.biSize);
226 bh.biWidth = BYTESWAP(bh.biWidth);
227 bh.biHeight = BYTESWAP(bh.biHeight);
228 bh.biPlanes = BYTESWAP16(bh.biPlanes);
229 bh.biBitCount = BYTESWAP16(bh.biBitCount);
230 bh.biCompression = BYTESWAP(bh.biCompression);
231 bh.biSizeImage = BYTESWAP(bh.biSizeImage);
232 bh.biXPelsPerMeter = BYTESWAP(bh.biXPelsPerMeter);
233 bh.biYPelsPerMeter = BYTESWAP(bh.biYPelsPerMeter);
234 bh.biClrUsed = BYTESWAP(bh.biClrUsed);
235 bh.biClrImportant = BYTESWAP(bh.biClrImportant);
236 }
237
238 if(bh.bfType != 0x4d42 || bh.bfOffBits < BMPHDRSIZE || bh.biWidth < 1
239 || bh.biHeight == 0)
240 THROW("Corrupt bitmap header");
241 if((bh.biBitCount != 24 && bh.biBitCount != 32)
242 || bh.biCompression != BI_RGB)
243 THROW("Only uncompessed RGB bitmaps are supported");
244
245 *width = bh.biWidth; *height = bh.biHeight;
246 srcPixelSize = bh.biBitCount / 8;
247 if(*height < 0) { *height = -(*height); srcOrientation = BMPORN_TOPDOWN; }
248 srcPitch = (((*width) * srcPixelSize) + 3) & (~3);
249 dstPitch = (((*width) * pf->size) + (align - 1)) & (~(align - 1));
250
251 if(srcPitch * (*height) + bh.bfOffBits != bh.bfSize)
252 THROW("Corrupt bitmap header");
253 if((tempbuf = (unsigned char *)malloc(srcPitch * (*height))) == NULL
254 || (*buf = (unsigned char *)malloc(dstPitch * (*height))) == NULL)
255 THROW("Memory allocation error");
256 if(lseek(fd, (long)bh.bfOffBits, SEEK_SET) != (long)bh.bfOffBits)
257 THROW(strerror(errno));
258 TRY_UNIX(bytesRead = read(fd, tempbuf, srcPitch * (*height)));
259 if(bytesRead != srcPitch * (*height)) THROW("Read error");
260
261 pixelConvert(tempbuf, *width, srcPitch, *height, pf_get(PF_BGR), *buf,
262 dstPitch, pf, srcOrientation != orientation);
263
264 finally:
265 free(tempbuf);
266 if(fd != -1) close(fd);
267 return ret;
268 }
269
270
ppm_save(char * filename,unsigned char * buf,int width,int pitch,int height,PF * pf,enum BMPORN orientation)271 static int ppm_save(char *filename, unsigned char *buf, int width, int pitch,
272 int height, PF *pf, enum BMPORN orientation)
273 {
274 FILE *file = NULL; int ret = 0;
275 unsigned char *tempbuf = NULL;
276
277 if((file = fopen(filename, "wb")) == NULL) THROW(strerror(errno));
278 if(fprintf(file, "P6\n") < 1) THROW("Write error");
279 if(fprintf(file, "%d %d\n", width, height) < 1) THROW("Write error");
280 if(fprintf(file, "255\n") < 1) THROW("Write error");
281
282 if((tempbuf = (unsigned char *)malloc(width * height * 3)) == NULL)
283 THROW("Memory allocation error");
284
285 pixelConvert(buf, width, pitch, height, pf, tempbuf, width * 3,
286 pf_get(PF_RGB), orientation == BMPORN_BOTTOMUP);
287
288 if((fwrite(tempbuf, width * height * 3, 1, file)) != 1)
289 THROW("Write error");
290
291 finally:
292 free(tempbuf);
293 if(file) fclose(file);
294 return ret;
295 }
296
297
bmp_save(char * filename,unsigned char * buf,int width,int pitch,int height,int format,enum BMPORN orientation)298 int bmp_save(char *filename, unsigned char *buf, int width, int pitch,
299 int height, int format, enum BMPORN orientation)
300 {
301 int fd = -1, bytesWritten, dstPitch, ret = 0;
302 int flags = O_RDWR | O_CREAT | O_TRUNC;
303 unsigned char *tempbuf = NULL; char *temp;
304 BitmapHeader bh; int mode;
305 PF *pf = pf_get(format);
306
307 #ifdef _WIN32
308 flags |= O_BINARY; mode = _S_IREAD | _S_IWRITE;
309 #else
310 mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
311 #endif
312 if(!filename || !buf || width < 1 || height < 1 || pf->bpc < 8 || pitch < 0)
313 THROW("Invalid argument to bmp_save()");
314
315 if(pitch == 0) pitch = width * pf->size;
316
317 if((temp = strrchr(filename, '.')) != NULL)
318 {
319 if(!stricmp(temp, ".ppm"))
320 return ppm_save(filename, buf, width, pitch, height, pf, orientation);
321 }
322
323 TRY_UNIX(fd = open(filename, flags, mode));
324 dstPitch = ((width * 3) + 3) & (~3);
325
326 bh.bfType = 0x4d42;
327 bh.bfSize = BMPHDRSIZE + dstPitch * height;
328 bh.bfReserved1 = 0; bh.bfReserved2 = 0;
329 bh.bfOffBits = BMPHDRSIZE;
330 bh.biSize = 40;
331 bh.biWidth = width; bh.biHeight = height;
332 bh.biPlanes = 0; bh.biBitCount = 24;
333 bh.biCompression = BI_RGB; bh.biSizeImage = 0;
334 bh.biXPelsPerMeter = 0; bh.biYPelsPerMeter = 0;
335 bh.biClrUsed = 0; bh.biClrImportant = 0;
336
337 if(!LittleEndian())
338 {
339 bh.bfType = BYTESWAP16(bh.bfType);
340 bh.bfSize = BYTESWAP(bh.bfSize);
341 bh.bfOffBits = BYTESWAP(bh.bfOffBits);
342 bh.biSize = BYTESWAP(bh.biSize);
343 bh.biWidth = BYTESWAP(bh.biWidth);
344 bh.biHeight = BYTESWAP(bh.biHeight);
345 bh.biPlanes = BYTESWAP16(bh.biPlanes);
346 bh.biBitCount = BYTESWAP16(bh.biBitCount);
347 bh.biCompression = BYTESWAP(bh.biCompression);
348 bh.biSizeImage = BYTESWAP(bh.biSizeImage);
349 bh.biXPelsPerMeter = BYTESWAP(bh.biXPelsPerMeter);
350 bh.biYPelsPerMeter = BYTESWAP(bh.biYPelsPerMeter);
351 bh.biClrUsed = BYTESWAP(bh.biClrUsed);
352 bh.biClrImportant = BYTESWAP(bh.biClrImportant);
353 }
354
355 WRITE(fd, &bh.bfType, sizeof(unsigned short));
356 WRITE(fd, &bh.bfSize, sizeof(unsigned int));
357 WRITE(fd, &bh.bfReserved1, sizeof(unsigned short));
358 WRITE(fd, &bh.bfReserved2, sizeof(unsigned short));
359 WRITE(fd, &bh.bfOffBits, sizeof(unsigned int));
360 WRITE(fd, &bh.biSize, sizeof(unsigned int));
361 WRITE(fd, &bh.biWidth, sizeof(int));
362 WRITE(fd, &bh.biHeight, sizeof(int));
363 WRITE(fd, &bh.biPlanes, sizeof(unsigned short));
364 WRITE(fd, &bh.biBitCount, sizeof(unsigned short));
365 WRITE(fd, &bh.biCompression, sizeof(unsigned int));
366 WRITE(fd, &bh.biSizeImage, sizeof(unsigned int));
367 WRITE(fd, &bh.biXPelsPerMeter, sizeof(int));
368 WRITE(fd, &bh.biYPelsPerMeter, sizeof(int));
369 WRITE(fd, &bh.biClrUsed, sizeof(unsigned int));
370 WRITE(fd, &bh.biClrImportant, sizeof(unsigned int));
371
372 if((tempbuf = (unsigned char *)malloc(dstPitch * height)) == NULL)
373 THROW("Memory allocation error");
374
375 pixelConvert(buf, width, pitch, height, pf, tempbuf, dstPitch,
376 pf_get(PF_BGR), orientation != BMPORN_BOTTOMUP);
377
378 if((bytesWritten =
379 write(fd, tempbuf, dstPitch * height)) != dstPitch * height)
380 THROW(strerror(errno));
381
382 finally:
383 free(tempbuf);
384 if(fd != -1) close(fd);
385 return ret;
386 }
387
388
bmp_geterr(void)389 const char *bmp_geterr(void)
390 {
391 return errorStr;
392 }
393