1
0

bmpload.c 24 KB

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