Blender  V3.3
bmp.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include <math.h>
9 
10 #include "BLI_fileops.h"
11 #include "BLI_utildefines.h"
12 
13 #include "imbuf.h"
14 
15 #include "IMB_filetype.h"
16 #include "IMB_imbuf.h"
17 #include "IMB_imbuf_types.h"
18 
19 #include "IMB_colormanagement.h"
21 
22 /* Some code copied from article on microsoft.com,
23  * copied here for enhanced BMP support in the future:
24  * http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0197/mfcp1/mfcp1.htm&nav=/msj/0197/newnav.htm
25  */
26 
27 typedef struct BMPINFOHEADER {
40 
41 #if 0
42 typedef struct BMPHEADER {
43  ushort biType;
44  uint biSize;
45  ushort biRes1;
46  ushort biRes2;
47  uint biOffBits;
48 } BMPHEADER;
49 #endif
50 
51 #define BMP_FILEHEADER_SIZE 14
52 
53 #define CHECK_HEADER_FIELD(_mem, _field) ((_mem[0] == _field[0]) && (_mem[1] == _field[1]))
54 #define CHECK_HEADER_FIELD_BMP(_mem) \
55  (CHECK_HEADER_FIELD(_mem, "BM") || CHECK_HEADER_FIELD(_mem, "BA") || \
56  CHECK_HEADER_FIELD(_mem, "CI") || CHECK_HEADER_FIELD(_mem, "CP") || \
57  CHECK_HEADER_FIELD(_mem, "IC") || CHECK_HEADER_FIELD(_mem, "PT"))
58 
59 static bool checkbmp(const uchar *mem, const size_t size)
60 {
61  if (size < (BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER))) {
62  return false;
63  }
64 
65  if (!CHECK_HEADER_FIELD_BMP(mem)) {
66  return false;
67  }
68 
69  bool ok = false;
70  BMPINFOHEADER bmi;
71  uint u;
72 
73  /* Skip file-header. */
74  mem += BMP_FILEHEADER_SIZE;
75 
76  /* for systems where an int needs to be 4 bytes aligned */
77  memcpy(&bmi, mem, sizeof(bmi));
78 
79  u = LITTLE_LONG(bmi.biSize);
80  /* we only support uncompressed images for now. */
81  if (u >= sizeof(BMPINFOHEADER)) {
82  if (bmi.biCompression == 0) {
83  u = LITTLE_SHORT(bmi.biBitCount);
84  if (u > 0 && u <= 32) {
85  ok = true;
86  }
87  }
88  }
89 
90  return ok;
91 }
92 
93 bool imb_is_a_bmp(const uchar *buf, size_t size)
94 {
95  return checkbmp(buf, size);
96 }
97 
98 static size_t imb_bmp_calc_row_size_in_bytes(size_t x, size_t depth)
99 {
100  /* https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader#calculating-surface-stride
101  */
102  return (((x * depth) + 31) & ~31) >> 3;
103 }
104 
105 ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
106 {
107  ImBuf *ibuf = NULL;
108  BMPINFOHEADER bmi;
109  int ibuf_depth;
110  const uchar *bmp;
111  uchar *rect;
112  ushort col;
113  bool top_to_bottom = false;
114 
115  if (checkbmp(mem, size) == 0) {
116  return NULL;
117  }
118 
120 
121  /* For systems where an int needs to be 4 bytes aligned. */
122  memcpy(&bmi, mem + BMP_FILEHEADER_SIZE, sizeof(bmi));
123 
124  const size_t palette_offset = (size_t)BMP_FILEHEADER_SIZE + LITTLE_LONG(bmi.biSize);
125  const int depth = LITTLE_SHORT(bmi.biBitCount);
126  const int xppm = LITTLE_LONG(bmi.biXPelsPerMeter);
127  const int yppm = LITTLE_LONG(bmi.biYPelsPerMeter);
128  const int x = LITTLE_LONG(bmi.biWidth);
129  int y = LITTLE_LONG(bmi.biHeight);
130 
131  /* Negative height means bitmap is stored top-to-bottom. */
132  if (y < 0) {
133  y = -y;
134  top_to_bottom = true;
135  }
136 
137  /* Validate and cross-check offsets and sizes. */
138  if (x < 1 || !(ELEM(depth, 1, 4, 8, 16, 24, 32))) {
139  return NULL;
140  }
141 
142  const size_t pixel_data_offset = (size_t)LITTLE_LONG(*(int *)(mem + 10));
143  const size_t header_bytes = BMP_FILEHEADER_SIZE + sizeof(BMPINFOHEADER);
144  const size_t num_actual_data_bytes = size - pixel_data_offset;
145  const size_t row_size_in_bytes = imb_bmp_calc_row_size_in_bytes(x, depth);
146  const size_t num_expected_data_bytes = row_size_in_bytes * y;
147  if (num_actual_data_bytes < num_expected_data_bytes || num_actual_data_bytes > size ||
148  pixel_data_offset < header_bytes || pixel_data_offset > (size - num_expected_data_bytes) ||
149  palette_offset < header_bytes || palette_offset > pixel_data_offset) {
150  return NULL;
151  }
152 
153  if (depth <= 8) {
154  ibuf_depth = 24;
155  }
156  else {
157  ibuf_depth = depth;
158  }
159 
160  bmp = mem + pixel_data_offset;
161 
162 #if 0
163  printf("palette_offset: %d, x: %d y: %d, depth: %d\n", palette_offset, x, y, depth);
164 #endif
165 
166  if (flags & IB_test) {
167  ibuf = IMB_allocImBuf(x, y, ibuf_depth, 0);
168  }
169  else {
170  ibuf = IMB_allocImBuf(x, y, ibuf_depth, IB_rect);
171  if (!ibuf) {
172  return NULL;
173  }
174 
175  rect = (uchar *)ibuf->rect;
176 
177  if (depth <= 8) {
178  const char(*palette)[4] = (const char(*)[4])(mem + palette_offset);
179  const int startmask = ((1 << depth) - 1) << 8;
180  for (size_t i = y; i > 0; i--) {
181  int index;
182  int bitoffs = 8;
183  int bitmask = startmask;
184  int nbytes = 0;
185  const char *pcol;
186  if (top_to_bottom) {
187  rect = (uchar *)&ibuf->rect[(i - 1) * x];
188  }
189  for (size_t j = x; j > 0; j--) {
190  bitoffs -= depth;
191  bitmask >>= depth;
192  index = (bmp[0] & bitmask) >> bitoffs;
193  pcol = palette[index];
194  /* intentionally BGR -> RGB */
195  rect[0] = pcol[2];
196  rect[1] = pcol[1];
197  rect[2] = pcol[0];
198 
199  rect[3] = 255;
200  rect += 4;
201  if (bitoffs == 0) {
202  /* Advance to the next byte */
203  bitoffs = 8;
204  bitmask = startmask;
205  nbytes += 1;
206  bmp += 1;
207  }
208  }
209  /* Advance to the next row */
210  bmp += (row_size_in_bytes - nbytes);
211  }
212  }
213  else if (depth == 16) {
214  for (size_t i = y; i > 0; i--) {
215  if (top_to_bottom) {
216  rect = (uchar *)&ibuf->rect[(i - 1) * x];
217  }
218  for (size_t j = x; j > 0; j--) {
219  col = bmp[0] + (bmp[1] << 8);
220  rect[0] = ((col >> 10) & 0x1f) << 3;
221  rect[1] = ((col >> 5) & 0x1f) << 3;
222  rect[2] = ((col >> 0) & 0x1f) << 3;
223 
224  rect[3] = 255;
225  rect += 4;
226  bmp += 2;
227  }
228  }
229  }
230  else if (depth == 24) {
231  const int x_pad = x % 4;
232  for (size_t i = y; i > 0; i--) {
233  if (top_to_bottom) {
234  rect = (uchar *)&ibuf->rect[(i - 1) * x];
235  }
236  for (size_t j = x; j > 0; j--) {
237  rect[0] = bmp[2];
238  rect[1] = bmp[1];
239  rect[2] = bmp[0];
240 
241  rect[3] = 255;
242  rect += 4;
243  bmp += 3;
244  }
245  /* for 24-bit images, rows are padded to multiples of 4 */
246  bmp += x_pad;
247  }
248  }
249  else if (depth == 32) {
250  for (size_t i = y; i > 0; i--) {
251  if (top_to_bottom) {
252  rect = (uchar *)&ibuf->rect[(i - 1) * x];
253  }
254  for (size_t j = x; j > 0; j--) {
255  rect[0] = bmp[2];
256  rect[1] = bmp[1];
257  rect[2] = bmp[0];
258  rect[3] = bmp[3];
259  rect += 4;
260  bmp += 4;
261  }
262  }
263  }
264  }
265 
266  if (ibuf) {
267  ibuf->ppm[0] = xppm;
268  ibuf->ppm[1] = yppm;
269  ibuf->ftype = IMB_FTYPE_BMP;
270  }
271 
272  return ibuf;
273 }
274 
275 #undef CHECK_HEADER_FIELD_BMP
276 #undef CHECK_HEADER_FIELD
277 
278 /* Couple of helper functions for writing our data */
279 static int putIntLSB(uint ui, FILE *ofile)
280 {
281  putc((ui >> 0) & 0xFF, ofile);
282  putc((ui >> 8) & 0xFF, ofile);
283  putc((ui >> 16) & 0xFF, ofile);
284  return putc((ui >> 24) & 0xFF, ofile);
285 }
286 
287 static int putShortLSB(ushort us, FILE *ofile)
288 {
289  putc((us >> 0) & 0xFF, ofile);
290  return putc((us >> 8) & 0xFF, ofile);
291 }
292 
293 bool imb_savebmp(ImBuf *ibuf, const char *filepath, int UNUSED(flags))
294 {
295  BMPINFOHEADER infoheader;
296 
297  const size_t bytes_per_pixel = (ibuf->planes + 7) >> 3;
298  BLI_assert(ELEM(bytes_per_pixel, 1, 3));
299 
300  const size_t pad_bytes_per_scanline = (4 - ibuf->x * bytes_per_pixel % 4) % 4;
301  const size_t bytesize = (ibuf->x * bytes_per_pixel + pad_bytes_per_scanline) * ibuf->y;
302 
303  const uchar *data = (const uchar *)ibuf->rect;
304  FILE *ofile = BLI_fopen(filepath, "wb");
305  if (ofile == NULL) {
306  return 0;
307  }
308 
309  const bool is_grayscale = bytes_per_pixel == 1;
310  const size_t palette_size = is_grayscale ? 255 * 4 : 0; /* RGBA32 */
311  const size_t pixel_array_start = BMP_FILEHEADER_SIZE + sizeof(infoheader) + palette_size;
312 
313  putShortLSB(19778, ofile); /* "BM" */
314  putIntLSB(bytesize + pixel_array_start, ofile); /* Total file size */
315  putShortLSB(0, ofile); /* Res1 */
316  putShortLSB(0, ofile); /* Res2 */
317  putIntLSB(pixel_array_start, ofile); /* offset to start of pixel array */
318 
319  putIntLSB(sizeof(infoheader), ofile);
320  putIntLSB(ibuf->x, ofile);
321  putIntLSB(ibuf->y, ofile);
322  putShortLSB(1, ofile);
323  putShortLSB(is_grayscale ? 8 : 24, ofile);
324  putIntLSB(0, ofile);
325  putIntLSB(bytesize, ofile);
326  putIntLSB(round(ibuf->ppm[0]), ofile);
327  putIntLSB(round(ibuf->ppm[1]), ofile);
328  putIntLSB(0, ofile);
329  putIntLSB(0, ofile);
330 
331  /* color palette table, which is just every grayscale color, full alpha */
332  if (is_grayscale) {
333  for (char i = 0; i < 255; i++) {
334  putc(i, ofile);
335  putc(i, ofile);
336  putc(i, ofile);
337  putc(0xFF, ofile);
338  }
339  }
340 
341  if (is_grayscale) {
342  for (size_t y = 0; y < ibuf->y; y++) {
343  for (size_t x = 0; x < ibuf->x; x++) {
344  const size_t ptr = (x + y * ibuf->x) * 4;
345  if (putc(data[ptr], ofile) == EOF) {
346  return 0;
347  }
348  }
349  /* Add padding here. */
350  for (size_t t = 0; t < pad_bytes_per_scanline; t++) {
351  if (putc(0, ofile) == EOF) {
352  return 0;
353  }
354  }
355  }
356  }
357  else {
358  /* Need to write out padded image data in BGR format. */
359  for (size_t y = 0; y < ibuf->y; y++) {
360  for (size_t x = 0; x < ibuf->x; x++) {
361  const size_t ptr = (x + y * ibuf->x) * 4;
362  if (putc(data[ptr + 2], ofile) == EOF) {
363  return 0;
364  }
365  if (putc(data[ptr + 1], ofile) == EOF) {
366  return 0;
367  }
368  if (putc(data[ptr], ofile) == EOF) {
369  return 0;
370  }
371  }
372  /* Add padding here. */
373  for (size_t t = 0; t < pad_bytes_per_scanline; t++) {
374  if (putc(0, ofile) == EOF) {
375  return 0;
376  }
377  }
378  }
379  }
380 
381  fflush(ofile);
382  fclose(ofile);
383  return 1;
384 }
#define BLI_assert(a)
Definition: BLI_assert.h:46
File and directory operations.
FILE * BLI_fopen(const char *filepath, const char *mode) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: fileops.c:906
unsigned char uchar
Definition: BLI_sys_types.h:70
unsigned int uint
Definition: BLI_sys_types.h:67
unsigned short ushort
Definition: BLI_sys_types.h:68
#define UNUSED(x)
#define ELEM(...)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble t
#define LITTLE_LONG
Definition: IMB_anim.h:55
@ COLOR_ROLE_DEFAULT_BYTE
struct ImBuf * IMB_allocImBuf(unsigned int x, unsigned int y, unsigned char planes, unsigned int flags)
Definition: allocimbuf.c:500
#define IM_MAX_SPACE
Definition: IMB_imbuf.h:49
Contains defines and structs used throughout the imbuf module.
@ IB_test
@ IB_rect
#define CHECK_HEADER_FIELD_BMP(_mem)
Definition: bmp.c:54
bool imb_savebmp(ImBuf *ibuf, const char *filepath, int UNUSED(flags))
Definition: bmp.c:293
struct BMPINFOHEADER BMPINFOHEADER
ImBuf * imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
Definition: bmp.c:105
static bool checkbmp(const uchar *mem, const size_t size)
Definition: bmp.c:59
bool imb_is_a_bmp(const uchar *buf, size_t size)
Definition: bmp.c:93
#define BMP_FILEHEADER_SIZE
Definition: bmp.c:51
static int putIntLSB(uint ui, FILE *ofile)
Definition: bmp.c:279
static size_t imb_bmp_calc_row_size_in_bytes(size_t x, size_t depth)
Definition: bmp.c:98
static int putShortLSB(ushort us, FILE *ofile)
Definition: bmp.c:287
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
void colorspace_set_default_role(char *colorspace, int size, int role)
uint col
@ IMB_FTYPE_BMP
#define LITTLE_SHORT
Definition: imbuf.h:40
uint biYPelsPerMeter
Definition: bmp.c:36
uint biSize
Definition: bmp.c:28
ushort biPlanes
Definition: bmp.c:31
uint biCompression
Definition: bmp.c:33
uint biSizeImage
Definition: bmp.c:34
uint biHeight
Definition: bmp.c:30
uint biXPelsPerMeter
Definition: bmp.c:35
uint biClrUsed
Definition: bmp.c:37
uint biWidth
Definition: bmp.c:29
ushort biBitCount
Definition: bmp.c:32
uint biClrImportant
Definition: bmp.c:38
unsigned char planes
enum eImbFileType ftype
unsigned int * rect
double ppm[2]
PointerRNA * ptr
Definition: wm_files.c:3480