bmpload.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. // BMP loader
  2. //
  3. // See: https://en.wikipedia.org/wiki/BMP_file_format
  4. #include "vips.h"
  5. #include <stdlib.h>
  6. #include <stdint.h>
  7. #include <stdbool.h>
  8. #define BMP_ROW_ADDR(BMP, OR, R, Y) \
  9. VIPS_REGION_ADDR(OR, 0, R->top + (BMP->top_down ? Y : R->height - 1 - Y))
  10. /**
  11. * BMP ForeignLoad VIPS class implementation (generic)
  12. */
  13. typedef struct _VipsForeignLoadBmp {
  14. VipsForeignLoad parent_object;
  15. VipsSource *source;
  16. int32_t width;
  17. int32_t height;
  18. uint16_t planes;
  19. uint16_t bpp;
  20. uint16_t compression;
  21. uint16_t offset;
  22. uint32_t rmask;
  23. uint32_t gmask;
  24. uint32_t bmask;
  25. uint32_t amask;
  26. uint32_t num_colors;
  27. int bands;
  28. bool top_down; // true if image is vertically reversed
  29. bool rle; // true if image is compressed with RLE
  30. bool bmp565; // 16-bit
  31. uint32_t *palette; // palette for 1, 2, 4 or 8 bits per pixel BMP palette
  32. int y_pos; // current position in the image, used when sequential access is possible
  33. int dy; // in RLE mode this indicates how many lines to skip
  34. int dx; // in RLE mode this indicates start pixel X
  35. VipsPel *row_buffer; // buffer for the current row, long enough to hold the whole 32-bit row+padding
  36. } VipsForeignLoadBmp;
  37. typedef VipsForeignLoadClass VipsForeignLoadBmpClass;
  38. G_DEFINE_ABSTRACT_TYPE(VipsForeignLoadBmp, vips_foreign_load_bmp,
  39. VIPS_TYPE_FOREIGN_LOAD);
  40. static void
  41. vips_foreign_load_bmp_dispose(GObject *gobject)
  42. {
  43. VipsForeignLoadBmp *bmp = (VipsForeignLoadBmp *) gobject;
  44. VIPS_UNREF(bmp->source);
  45. G_OBJECT_CLASS(vips_foreign_load_bmp_parent_class)->dispose(gobject);
  46. }
  47. static int
  48. vips_foreign_load_bmp_build(VipsObject *object)
  49. {
  50. VipsForeignLoadBmp *bmp = (VipsForeignLoadBmp *) object;
  51. return VIPS_OBJECT_CLASS(vips_foreign_load_bmp_parent_class)
  52. ->build(object);
  53. }
  54. static VipsForeignFlags
  55. vips_foreign_load_bmp_get_flags(VipsForeignLoad *load)
  56. {
  57. return VIPS_FOREIGN_SEQUENTIAL;
  58. }
  59. /**
  60. * Sets the image header for the output image
  61. */
  62. static int
  63. vips_foreign_load_bmp_set_image_header(VipsForeignLoadBmp *bmp, VipsImage *out)
  64. {
  65. vips_image_init_fields(
  66. out,
  67. bmp->width,
  68. bmp->height,
  69. bmp->bands,
  70. VIPS_FORMAT_UCHAR,
  71. VIPS_CODING_NONE,
  72. VIPS_INTERPRETATION_sRGB,
  73. 1.0,
  74. 1.0);
  75. if (bmp->palette != NULL) {
  76. int bd;
  77. if (bmp->num_colors > 16) {
  78. bd = 8; // 8-bit palette
  79. }
  80. else if (bmp->num_colors > 4) {
  81. bd = 4; // 4-bit palette
  82. }
  83. else if (bmp->num_colors > 2) {
  84. bd = 2; // 2-bit palette
  85. }
  86. else {
  87. bd = 1; // 1-bit palette
  88. }
  89. vips_image_set_int(out, "palette-bit-depth", bd);
  90. #ifdef VIPS_META_BITS_PER_SAMPLE
  91. vips_image_set_int(out, VIPS_META_BITS_PER_SAMPLE, bd);
  92. #endif
  93. #ifdef VIPS_META_PALETTE
  94. vips_image_set_int(out, VIPS_META_PALETTE, TRUE);
  95. #endif
  96. }
  97. if (vips_image_pipelinev(out, VIPS_DEMAND_STYLE_THINSTRIP, NULL))
  98. return -1;
  99. return 0;
  100. }
  101. /**
  102. * Checks if the source is a BMP image
  103. */
  104. static gboolean
  105. vips_foreign_load_bmp_source_is_a_source(VipsSource *source)
  106. {
  107. unsigned char *bbuf = vips_source_sniff(source, 2);
  108. if (!bbuf) {
  109. vips_error("vips_foreign_load_bmp_source_is_a_source", "unable to sniff source");
  110. return 0;
  111. }
  112. return bbuf[0] == 'B' &&
  113. bbuf[1] == 'M';
  114. }
  115. /**
  116. * Loads the header of the BMP image from the source.
  117. */
  118. static int
  119. vips_foreign_load_bmp_header(VipsForeignLoad *load)
  120. {
  121. VipsForeignLoadBmp *bmp = (VipsForeignLoadBmp *) load;
  122. // Rewind the source to the beginning
  123. if (vips_source_rewind(bmp->source))
  124. return -1;
  125. VipsPel file_header_buf[BMP_FILE_HEADER_LEN + 4];
  126. // Read the header + the next uint32 after
  127. if (vips_source_read(bmp->source, &file_header_buf, BMP_FILE_HEADER_LEN + 4) <= 0) {
  128. vips_error("vips_foreign_load_bmp_header", "unable to read file header from the source");
  129. return -1;
  130. }
  131. // Check if the info header length is valid
  132. uint32_t offset = GUINT32_FROM_LE(*(uint32_t *) (file_header_buf + 10));
  133. uint32_t info_header_len = GUINT32_FROM_LE(*(uint32_t *) (file_header_buf + 14));
  134. if (
  135. (info_header_len != BMP_BITMAP_INFO_HEADER_LEN) &&
  136. (info_header_len != BMP_V4_INFO_HEADER_LEN) &&
  137. (info_header_len != BMP_V5_INFO_HEADER_LEN)) {
  138. vips_error("vips_foreign_load_bmp_header", "incorrect BMP header length");
  139. return -1;
  140. }
  141. // Now, read the info header. -4 bytes is because we've already read the first 4 bytes of
  142. // the header (info_header_len) at the previous step. Constants include those 4 bytes.
  143. VipsPel *info_header = VIPS_ARRAY(load, info_header_len - 4, VipsPel);
  144. if (vips_source_read(bmp->source, info_header, info_header_len - 4) <= 0) {
  145. vips_error("vips_foreign_load_bmp_header", "unable to read BMP info header");
  146. return -1;
  147. }
  148. int32_t width = GINT32_FROM_LE(*(int32_t *) (info_header));
  149. int32_t height = GINT32_FROM_LE(*(int32_t *) (info_header + 4));
  150. uint16_t planes = GUINT16_FROM_LE(*(uint16_t *) (info_header + 8));
  151. uint16_t bpp = GUINT16_FROM_LE(*(uint16_t *) (info_header + 10));
  152. uint32_t compression = GUINT32_FROM_LE(*(uint32_t *) (info_header + 12));
  153. uint32_t num_colors = GUINT32_FROM_LE(*(uint32_t *) (info_header + 28));
  154. bool top_down = FALSE;
  155. bool rle = FALSE;
  156. bool bmp565 = FALSE;
  157. uint32_t rmask = 0;
  158. uint32_t gmask = 0;
  159. uint32_t bmask = 0;
  160. uint32_t amask = 0;
  161. int bands = 3; // 3 bands by default (RGB)
  162. // Let's determine if the image has an alpha channel
  163. bool has_alpha = FALSE;
  164. // If the info header is V4 or V5, check for alpha channel explicitly
  165. if (info_header_len > BMP_BITMAP_INFO_HEADER_LEN) {
  166. has_alpha = GUINT32_FROM_LE(*(uint32_t *) (info_header + 48)) != 0;
  167. }
  168. else {
  169. has_alpha = (bpp == 32); // otherwise, rely on the bpp value
  170. }
  171. if (has_alpha) {
  172. bands = 4;
  173. }
  174. if (height < 0) {
  175. height = -height;
  176. top_down = TRUE;
  177. }
  178. if ((width <= 0) || (height <= 0)) {
  179. vips_error("vips_foreign_load_bmp_header", "unsupported BMP image dimensions");
  180. return -1;
  181. }
  182. // we only support 1 plane and 8, 24 or 32 bits per pixel
  183. if (planes != 1) {
  184. vips_error("vips_foreign_load_bmp_header", "unsupported BMP image: planes != 1");
  185. return -1;
  186. }
  187. if (compression == COMPRESSION_BI_RGB) {
  188. // go ahead: no compression
  189. }
  190. else if (
  191. ((compression == COMPRESSION_BI_RLE8) && (bpp == 8)) ||
  192. ((compression == COMPRESSION_BI_RLE4) && (bpp == 4))) {
  193. // rle compression is used for 8-bit or 4-bit images
  194. rle = TRUE;
  195. }
  196. else if (
  197. (compression == COMPRESSION_BI_BITFIELDS) ||
  198. (compression == COMPRESSION_BI_BITFIELDS_ALPHA)) {
  199. int color_mask_len = 3;
  200. if (bpp > 24) {
  201. color_mask_len = 4;
  202. }
  203. uint32_t color_mask_buf[4];
  204. uint32_t *color_mask;
  205. // for the non-v4/v5 bmp image we need to load color mask separately since
  206. // it is not included in the header
  207. if (info_header_len == BMP_BITMAP_INFO_HEADER_LEN) {
  208. // let's attach it to load itself so we won't care about conditionally freeing it
  209. if (vips_source_read(bmp->source, color_mask_buf, color_mask_len * sizeof(uint32_t)) <= 0) {
  210. vips_error("vips_foreign_load_bmp_header", "unable to read BMP color mask");
  211. return -1;
  212. }
  213. color_mask = color_mask_buf;
  214. }
  215. else {
  216. // In case of v4/v5 info header, the color mask is already included in the info header,
  217. // we just need to read it
  218. color_mask = (uint32_t *) ((VipsPel *) info_header + 36);
  219. }
  220. // Standard says that color masks are in BE order. However, we do all the
  221. // checks and calculations as like as masks were in LE order.
  222. rmask = GUINT32_FROM_LE(color_mask[0]);
  223. gmask = GUINT32_FROM_LE(color_mask[1]);
  224. bmask = GUINT32_FROM_LE(color_mask[2]);
  225. amask = 0; // default alpha mask is 0
  226. if (color_mask_len > 3) {
  227. amask = GUINT32_FROM_LE(color_mask[3]);
  228. }
  229. if ((bpp == 16) && (rmask = 0xF800) && (gmask == 0x7E0) && (bmask == 0x1F)) {
  230. bmp565 = TRUE;
  231. }
  232. else if ((bpp == 16) && (rmask == 0x7C00) && (gmask == 0x3E0) && (bmask == 0x1F)) {
  233. // Go ahead, it's a regular 16 bit image
  234. }
  235. else if ((bpp == 32) && (rmask == 0xff0000) && (gmask == 0xff00) && (bmask == 0xff) && (amask == 0xff000000)) {
  236. // Go ahead, it's a regular 32-bit image
  237. }
  238. else {
  239. vips_error("vips_foreign_load_bmp_header", "unsupported BMP image: unsupported color masks");
  240. return -1;
  241. }
  242. }
  243. else {
  244. vips_error("vips_foreign_load_bmp_header", "unsupported BMP image: compression not supported");
  245. return -1;
  246. }
  247. // BMP images with 1, 2, 4 or 8 bits per pixel
  248. if (bpp <= 8) {
  249. // They could not have num_colors == 0, this is a bug in the BMP file.
  250. if (num_colors == 0) {
  251. num_colors = 1 << bpp;
  252. }
  253. // Please note, that BMP images are stored in BGR order rather than RGB order.
  254. // Every 4th byte is padding.
  255. bmp->palette = VIPS_MALLOC(load, num_colors * BMP_PALETTE_ITEM_SIZE);
  256. if (vips_source_read(bmp->source, bmp->palette, num_colors * BMP_PALETTE_ITEM_SIZE) <= 0) {
  257. vips_error("vips_foreign_load_bmp_header", "unable to read BMP palette");
  258. return -1;
  259. }
  260. }
  261. bmp->width = width;
  262. bmp->height = height;
  263. bmp->planes = planes;
  264. bmp->bpp = bpp;
  265. bmp->compression = compression;
  266. bmp->offset = offset;
  267. bmp->top_down = top_down;
  268. bmp->rle = rle;
  269. bmp->bmp565 = bmp565;
  270. bmp->num_colors = num_colors;
  271. bmp->bands = bands;
  272. bmp->rmask = rmask;
  273. bmp->gmask = gmask;
  274. bmp->bmask = bmask;
  275. bmp->amask = amask;
  276. bmp->y_pos = 0;
  277. bmp->dx = 0; // In sequential access this indicates that we need to skip n lines
  278. bmp->dy = 0; // n pixels
  279. // set the image header of the out image
  280. if (vips_foreign_load_bmp_set_image_header(bmp, load->out)) {
  281. return -1;
  282. }
  283. // seek to the beginning of image data
  284. if (vips_source_seek(bmp->source, offset, SEEK_SET) < 0) {
  285. vips_error("vips_foreign_load_bmp_header", "unable to seek to BMP image data");
  286. return -1;
  287. }
  288. vips_source_minimise(bmp->source);
  289. return 0;
  290. }
  291. /**
  292. * Generates a strip for 24/32 bpp BMP image.
  293. */
  294. static int
  295. vips_foreign_load_bmp_24_32_generate_strip(VipsRect *r, VipsRegion *out_region, VipsForeignLoadBmp *bmp)
  296. {
  297. // Align the row size to 4 bytes, as BMP rows are 4-byte aligned.
  298. int row_size = (bmp->bands * r->width + 3) & (~3);
  299. VipsPel *src;
  300. VipsPel *dest;
  301. for (int y = 0; y < r->height; y++) {
  302. src = bmp->row_buffer;
  303. dest = BMP_ROW_ADDR(bmp, out_region, r, y);
  304. if (vips_foreign_load_read_full(bmp->source, src, row_size) <= 0) {
  305. vips_error("vips_foreign_load_bmp_24_32_generate_strip", "failed to read raw data");
  306. return -1;
  307. }
  308. for (int x = 0; x < r->width; x++) {
  309. dest[0] = src[2]; // B
  310. dest[1] = src[1]; // G
  311. dest[2] = src[0]; // R
  312. // if the image has alpha channel, copy it too
  313. if (bmp->bands == 4) {
  314. dest[3] = src[3]; // A
  315. }
  316. dest += bmp->bands;
  317. src += bmp->bands;
  318. }
  319. bmp->y_pos += 1;
  320. }
  321. return 0;
  322. }
  323. /**
  324. * Generates a strip for 16 bpp BMP image.
  325. */
  326. static int
  327. vips_foreign_load_bmp_16_generate_strip(VipsRect *r, VipsRegion *out_region, VipsForeignLoadBmp *bmp)
  328. {
  329. // Align the row size to 4 bytes, as BMP rows are 4-byte aligned, 16 bpp = 2 bytes per pixel
  330. int row_size = (2 * r->width + 3) & (~3);
  331. VipsPel *src;
  332. VipsPel *dest;
  333. for (int y = 0; y < r->height; y++) {
  334. src = bmp->row_buffer;
  335. dest = BMP_ROW_ADDR(bmp, out_region, r, y);
  336. if (vips_foreign_load_read_full(bmp->source, src, row_size) <= 0) {
  337. vips_error("vips_foreign_load_bmp_16_generate_strip", "failed to read raw data");
  338. return -1;
  339. }
  340. for (int x = 0; x < r->width; x++) {
  341. uint16_t pixel = GUINT16_FROM_LE(*(uint16_t *) src);
  342. // 565 and non-565 formats both are handled here: they differ by the masks
  343. dest[0] = (uint8_t) ((pixel & bmp->rmask) >> 11) << 3;
  344. dest[1] = (uint8_t) ((pixel & bmp->gmask) >> 5) << 2;
  345. dest[2] = (uint8_t) (pixel & bmp->bmask) << 3;
  346. dest += bmp->bands;
  347. src += 2; // 2 bytes per pixel for 16 bpp
  348. }
  349. bmp->y_pos += 1;
  350. }
  351. return 0;
  352. }
  353. /**
  354. * Writes pixels for 1/2/4/8 bpp BMP image using palette. Pixels are taken from the src (if present), or src_byte (RLE case).
  355. */
  356. void
  357. vips_foreign_load_bpp_1_8_write_pixels_palette(VipsForeignLoadBmp *bmp, VipsPel *dest, VipsPel *src, int width, VipsPel src_byte)
  358. {
  359. int bit = 8 - bmp->bpp;
  360. int src_offset = 0;
  361. for (int x = 0; x < width; x++) {
  362. // Read the palette index from the source
  363. int pixel;
  364. if (src != NULL) {
  365. pixel = (int) src[src_offset] >> bit;
  366. }
  367. else {
  368. pixel = src_byte >> bit;
  369. }
  370. int mask = (1 << bmp->bpp) - 1;
  371. int palette_index = pixel & mask;
  372. int dest_offset = x * bmp->bands;
  373. if (bit == 0) {
  374. bit = 8 - bmp->bpp;
  375. src_offset++;
  376. }
  377. else {
  378. bit -= bmp->bpp;
  379. }
  380. VipsPel *color = (VipsPel *) &bmp->palette[palette_index];
  381. dest[dest_offset + 0] = color[2]; // BGR, reversed
  382. dest[dest_offset + 1] = color[1];
  383. dest[dest_offset + 2] = color[0];
  384. }
  385. }
  386. /**
  387. * Generates a strip for 1/2/4/8 bpp BMP image.
  388. */
  389. static int
  390. vips_foreign_load_bmp_1_8_generate_strip(VipsRect *r, VipsRegion *out_region, VipsForeignLoadBmp *bmp)
  391. {
  392. // Align the row size to 4 bytes, as BMP rows are 4-byte aligned
  393. char cap = 8 / bmp->bpp;
  394. int row_size = ((r->width + cap - 1) / cap + 3) & (~3);
  395. VipsPel *src = bmp->row_buffer; // just a shortcut
  396. VipsPel *dest;
  397. for (int y = 0; y < r->height; y++) {
  398. if (vips_foreign_load_read_full(bmp->source, src, row_size) <= 0) {
  399. vips_error("vips_foreign_load_bmp_16_generate_strip", "failed to read raw data");
  400. return -1;
  401. }
  402. dest = BMP_ROW_ADDR(bmp, out_region, r, y);
  403. vips_foreign_load_bpp_1_8_write_pixels_palette(bmp, dest, src, r->width, -1);
  404. bmp->y_pos += 1;
  405. }
  406. return 0;
  407. }
  408. /**
  409. * Generates a strip for 1/2/4/8 bpp BMP image.
  410. *
  411. * BMP RLE is encoded per-line (so, each line has 0x00 00 - LE control byte at the end).
  412. */
  413. static int
  414. vips_foreign_load_bmp_rle_generate_strip(VipsRect *r, VipsRegion *out_region, VipsForeignLoadBmp *bmp)
  415. {
  416. // Align the row size to 4 bytes, as BMP rows are 4-byte aligned
  417. char cap = 8 / bmp->bpp;
  418. VipsPel *src = bmp->row_buffer; // just a shortcut
  419. VipsPel *dest;
  420. VipsPel cmd[2];
  421. VipsPel dxdy[2];
  422. for (int y = 0; y < r->height; y++) {
  423. dest = BMP_ROW_ADDR(bmp, out_region, r, y);
  424. // fill the line with zeros (move to skips)
  425. memset(dest, 0, r->width * bmp->bands);
  426. // Skip lines if needed, this might be the whole region
  427. if (bmp->dy > 0) {
  428. bmp->dy--;
  429. bmp->y_pos += 1;
  430. continue;
  431. }
  432. int x = 0;
  433. if (bmp->dx > 0) {
  434. x = bmp->dx;
  435. bmp->dx = 0;
  436. }
  437. do {
  438. // Read next command
  439. //
  440. // NOTE: This might not be very efficient, unless underlying vips buffer is memory-buffered
  441. if (vips_foreign_load_read_full(bmp->source, &cmd, 2) <= 0) {
  442. vips_error("vips_foreign_load_bmp_rle_generate_strip", "failed to read next RLE command");
  443. return -1;
  444. }
  445. // Check control byte
  446. if (cmd[0] == 0) {
  447. if (cmd[1] == BMP_RLE_EOL) {
  448. break;
  449. }
  450. else if (cmd[1] == BMP_RLE_EOF) {
  451. bmp->dy = G_MAXUINT32; // set dy to max, so all the leftover lines will be skipped
  452. bmp->dx = 0;
  453. break; // exit the loop, we reached EOF
  454. }
  455. else if (cmd[1] == BMP_RLE_MOVE_TO) {
  456. if (vips_foreign_load_read_full(bmp->source, &dxdy, 2) <= 0) {
  457. vips_error("vips_foreign_load_bmp_rle_generate_strip", "failed to read RLE move command");
  458. return -1;
  459. }
  460. int dx = dxdy[0]; // relative X offset
  461. int dy = dxdy[1]; // relative Y offset
  462. // We treat movement by Y as EOL
  463. if (dy > 0) {
  464. bmp->dx = MIN(x + dx, r->width); // New X position must not exceed the width of the image
  465. bmp->dy = dy; // We do not care if Y pos is outside of the impage, it's a separate check
  466. break; // we need to skip lines, so we exit the loop
  467. } // Movement by X might not lead to EOL, so we continue
  468. else {
  469. bmp->dy = dy; // 0
  470. x = MIN(x + dx, r->width); // Move to the desired pixel
  471. }
  472. }
  473. else { // Directly read next n bytes
  474. int pixels_count = cmd[1];
  475. int bytes_count = ((pixels_count + cap - 1) / cap + 1) & ~1;
  476. pixels_count = MIN(pixels_count, r->width - x);
  477. if (vips_foreign_load_read_full(bmp->source, src, bytes_count) <= 0) {
  478. vips_error("vips_foreign_load_bmp_rle_generate_strip", "failed to read RLE data");
  479. return -1;
  480. }
  481. vips_foreign_load_bpp_1_8_write_pixels_palette(bmp, dest + (x * bmp->bands), src, pixels_count, -1);
  482. x += pixels_count;
  483. }
  484. }
  485. else { // read RLE-encoded pixels
  486. VipsPel pixels_count = cmd[0];
  487. VipsPel pixel = cmd[1];
  488. pixels_count = MIN(pixels_count, r->width - x);
  489. vips_foreign_load_bpp_1_8_write_pixels_palette(bmp, dest + (x * bmp->bands), NULL, pixels_count, pixel);
  490. x += pixels_count;
  491. }
  492. } while (1);
  493. bmp->y_pos += 1; // Move to the next line
  494. }
  495. return 0;
  496. }
  497. /**
  498. * Loads a strip of non-rle bmp image, access must be sequential, demand
  499. * style must be thinstrip + strip height set to max.
  500. */
  501. static int
  502. vips_foreign_load_bmp_rgb_generate(VipsRegion *out_region,
  503. void *seq, void *a, void *b, gboolean *stop)
  504. {
  505. VipsForeignLoadBmp *bmp = (VipsForeignLoadBmp *) a;
  506. VipsRect *r = &out_region->valid;
  507. /**
  508. * Sanity checks which assure that the requested region has the correct shape.
  509. *
  510. * We use sequential access + thinstrip demand style which means that image would
  511. * be read in strips, where each strip represents image-wide set of rows.
  512. */
  513. g_assert(r->left == 0); // Strip starts at the left edge of the image
  514. g_assert(r->width == out_region->im->Xsize); // Has width of the image
  515. g_assert(VIPS_RECT_BOTTOM(r) <= out_region->im->Ysize); // Equals or less of image height
  516. // Equals to the maximum height of the strip or less (last strip)
  517. if (bmp->top_down)
  518. g_assert(r->height ==
  519. VIPS_MIN(VIPS__FATSTRIP_HEIGHT, out_region->im->Ysize - r->top));
  520. else
  521. g_assert(r->height == out_region->im->Ysize);
  522. // Check if the requested strip is in order
  523. if (r->top != bmp->y_pos) {
  524. vips_error("vips_foreign_load_bmp_generate", "out of order read at line %d", bmp->y_pos);
  525. return -1;
  526. }
  527. if (bmp->rle) {
  528. return vips_foreign_load_bmp_rle_generate_strip(r, out_region, bmp);
  529. }
  530. else if (bmp->bpp >= 24) {
  531. return vips_foreign_load_bmp_24_32_generate_strip(r, out_region, bmp);
  532. }
  533. else if (bmp->bpp == 16) {
  534. return vips_foreign_load_bmp_16_generate_strip(r, out_region, bmp);
  535. }
  536. return vips_foreign_load_bmp_1_8_generate_strip(r, out_region, bmp);
  537. }
  538. /**
  539. * Loads a BMP image from the source.
  540. */
  541. static int
  542. vips_foreign_load_bmp_load(VipsForeignLoad *load)
  543. {
  544. VipsForeignLoadBmp *bmp = (VipsForeignLoadBmp *) load;
  545. // For a case when we encounter buggy BMP image which has RLE command to read next
  546. // 255 bytes, and our buffer is smaller than that, we need it to be at least 255 bytes.
  547. int row_buffer_length = (bmp->width * 4) + 4;
  548. if (row_buffer_length < 255) {
  549. row_buffer_length = 255;
  550. }
  551. // Allocate a row buffer for the current row in all generate* functions.
  552. // 4 * width + 4 is guaranteed to be enough for the longest (32-bit per pixel) row + padding.
  553. bmp->row_buffer = VIPS_ARRAY(load, row_buffer_length, VipsPel);
  554. VipsImage **t = (VipsImage **)
  555. vips_object_local_array(VIPS_OBJECT(load), 2);
  556. t[0] = vips_image_new();
  557. if (
  558. vips_foreign_load_bmp_set_image_header(bmp, t[0]) ||
  559. vips_image_generate(t[0],
  560. NULL, vips_foreign_load_bmp_rgb_generate, NULL,
  561. bmp, NULL) ||
  562. // For bottom-up BMP images we need to flip the image vertically.
  563. // We do this in vips_image_generate callback, so we need to be sure that
  564. // we generate regions size of the whole image.
  565. vips_sequential(t[0], &t[1],
  566. "tile_height", bmp->top_down ? VIPS__FATSTRIP_HEIGHT : t[0]->Ysize,
  567. NULL) ||
  568. vips_image_write(t[1], load->real) ||
  569. vips_source_decode(bmp->source)) {
  570. return -1;
  571. }
  572. return 0;
  573. }
  574. static void
  575. vips_foreign_load_bmp_class_init(VipsForeignLoadBmpClass *class)
  576. {
  577. GObjectClass *gobject_class = G_OBJECT_CLASS(class);
  578. VipsObjectClass *object_class = (VipsObjectClass *) class;
  579. VipsForeignClass *foreign_class = (VipsForeignClass *) class;
  580. VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
  581. gobject_class->dispose = vips_foreign_load_bmp_dispose;
  582. gobject_class->set_property = vips_object_set_property;
  583. gobject_class->get_property = vips_object_get_property;
  584. object_class->nickname = "bmpload_base";
  585. object_class->description = "load bmp image (internal format for video thumbs)";
  586. object_class->build = vips_foreign_load_bmp_build;
  587. load_class->get_flags = vips_foreign_load_bmp_get_flags;
  588. load_class->header = vips_foreign_load_bmp_header;
  589. load_class->load = vips_foreign_load_bmp_load;
  590. }
  591. static void
  592. vips_foreign_load_bmp_init(VipsForeignLoadBmp *load)
  593. {
  594. load->palette = NULL;
  595. }
  596. typedef struct _VipsForeignLoadBmpSource {
  597. VipsForeignLoadBmp parent_object;
  598. VipsSource *source;
  599. } VipsForeignLoadBmpSource;
  600. typedef VipsForeignLoadBmpClass VipsForeignLoadBmpSourceClass;
  601. G_DEFINE_TYPE(VipsForeignLoadBmpSource, vips_foreign_load_bmp_source,
  602. vips_foreign_load_bmp_get_type());
  603. static int
  604. vips_foreign_load_bmp_source_build(VipsObject *object)
  605. {
  606. VipsForeignLoadBmp *bmp = (VipsForeignLoadBmp *) object;
  607. VipsForeignLoadBmpSource *source =
  608. (VipsForeignLoadBmpSource *) object;
  609. if (source->source) {
  610. bmp->source = source->source;
  611. g_object_ref(bmp->source);
  612. }
  613. return VIPS_OBJECT_CLASS(vips_foreign_load_bmp_source_parent_class)
  614. ->build(object);
  615. }
  616. static void
  617. vips_foreign_load_bmp_source_class_init(
  618. VipsForeignLoadBmpSourceClass *class)
  619. {
  620. GObjectClass *gobject_class = G_OBJECT_CLASS(class);
  621. VipsObjectClass *object_class = (VipsObjectClass *) class;
  622. VipsOperationClass *operation_class = VIPS_OPERATION_CLASS(class);
  623. VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
  624. gobject_class->set_property = vips_object_set_property;
  625. gobject_class->get_property = vips_object_get_property;
  626. load_class->is_a_source = vips_foreign_load_bmp_source_is_a_source;
  627. object_class->nickname = "bmpload_source";
  628. object_class->description = "load image from bmp source";
  629. object_class->build = vips_foreign_load_bmp_source_build;
  630. VIPS_ARG_OBJECT(class, "source", 1,
  631. "Source",
  632. "Source to load from",
  633. VIPS_ARGUMENT_REQUIRED_INPUT,
  634. G_STRUCT_OFFSET(VipsForeignLoadBmpSource, source),
  635. VIPS_TYPE_SOURCE);
  636. }
  637. static void
  638. vips_foreign_load_bmp_source_init(VipsForeignLoadBmpSource *source)
  639. {
  640. }
  641. /**
  642. * vips_bmpload_source:
  643. * @source: source to load
  644. * @out: (out): image to write
  645. * @...: `NULL`-terminated list of optional named arguments
  646. *
  647. * Read a RAWRGB-formatted memory block into a VIPS image.
  648. *
  649. * Returns: 0 on success, -1 on error.
  650. */
  651. int
  652. vips_bmpload_source(VipsSource *source, VipsImage **out, ...)
  653. {
  654. va_list ap;
  655. int result;
  656. va_start(ap, out);
  657. result = vips_call_split("bmpload_source", ap, source, out);
  658. va_end(ap);
  659. return result;
  660. }
  661. // wrapper function which hiders varargs (...) from CGo
  662. int
  663. vips_bmpload_source_go(VipsImgproxySource *source, VipsImage **out)
  664. {
  665. return vips_bmpload_source(
  666. VIPS_SOURCE(source), out,
  667. "access", VIPS_ACCESS_SEQUENTIAL,
  668. NULL);
  669. }