1 /* bmpwrite.c Writes Bitmap files. Even RLE encoded ones. */
2 /* (Windows (TM) doesn't read all of those, but who */
3 /* cares? ;-) */
4 /* I changed a few things over the time, so perhaps */
5 /* it dos now, but now there's no Windows left on */
6 /* my computer... */
7
8 /* Alexander.Schulz@stud.uni-karlsruhe.de */
9
10 /*
11 * GIMP - The GNU Image Manipulation Program
12 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
13 *
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program. If not, see <https://www.gnu.org/licenses/>.
26 * ----------------------------------------------------------------------------
27 */
28
29 #include "config.h"
30
31 #include <errno.h>
32 #include <string.h>
33
34 #include <glib/gstdio.h>
35
36 #include <libgimp/gimp.h>
37 #include <libgimp/gimpui.h>
38
39 #include "bmp.h"
40 #include "bmp-save.h"
41
42 #include "libgimp/stdplugins-intl.h"
43
44
45 typedef enum
46 {
47 RGB_565,
48 RGBA_5551,
49 RGB_555,
50 RGB_888,
51 RGBA_8888,
52 RGBX_8888
53 } RGBMode;
54
55
56 static void write_image (FILE *f,
57 guchar *src,
58 gint width,
59 gint height,
60 gint use_run_length_encoding,
61 gint channels,
62 gint bpp,
63 gint spzeile,
64 gint MapSize,
65 RGBMode rgb_format,
66 gint mask_info_size,
67 gint color_space_size);
68
69 static gboolean save_dialog (gint channels,
70 gint bpp);
71
72
73 static struct
74 {
75 RGBMode rgb_format;
76 gint use_run_length_encoding;
77
78 /* Whether or not to write BITMAPV5HEADER color space data */
79 gint dont_write_color_space_data;
80 } BMPSaveData;
81
82
83 static void
write_color_map(FILE * f,gint red[MAXCOLORS],gint green[MAXCOLORS],gint blue[MAXCOLORS],gint size)84 write_color_map (FILE *f,
85 gint red[MAXCOLORS],
86 gint green[MAXCOLORS],
87 gint blue[MAXCOLORS],
88 gint size)
89 {
90 gchar trgb[4];
91 gint i;
92
93 size /= 4;
94 trgb[3] = 0;
95 for (i = 0; i < size; i++)
96 {
97 trgb[0] = (guchar) blue[i];
98 trgb[1] = (guchar) green[i];
99 trgb[2] = (guchar) red[i];
100 Write (f, trgb, 4);
101 }
102 }
103
104 static gboolean
warning_dialog(const gchar * primary,const gchar * secondary)105 warning_dialog (const gchar *primary,
106 const gchar *secondary)
107 {
108 GtkWidget *dialog;
109 gboolean ok;
110
111 dialog = gtk_message_dialog_new (NULL, 0,
112 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
113 "%s", primary);
114
115 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
116 "%s", secondary);
117
118 gimp_window_set_transient (GTK_WINDOW (dialog));
119
120 ok = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK);
121
122 gtk_widget_destroy (dialog);
123
124 return ok;
125 }
126
127 GimpPDBStatusType
save_image(const gchar * filename,gint32 image,gint32 drawable_ID,GimpRunMode run_mode,GError ** error)128 save_image (const gchar *filename,
129 gint32 image,
130 gint32 drawable_ID,
131 GimpRunMode run_mode,
132 GError **error)
133 {
134 FILE *outfile;
135 BitmapFileHead bitmap_file_head;
136 BitmapHead bitmap_head;
137 gint Red[MAXCOLORS];
138 gint Green[MAXCOLORS];
139 gint Blue[MAXCOLORS];
140 guchar *cmap;
141 gint rows, cols, Spcols, channels, MapSize, SpZeile;
142 glong BitsPerPixel;
143 gint colors;
144 guchar *pixels;
145 GeglBuffer *buffer;
146 const Babl *format;
147 GimpImageType drawable_type;
148 gint drawable_width;
149 gint drawable_height;
150 gint i;
151 gint mask_info_size;
152 gint color_space_size;
153 guint32 Mask[4];
154
155 buffer = gimp_drawable_get_buffer (drawable_ID);
156
157 drawable_type = gimp_drawable_type (drawable_ID);
158 drawable_width = gimp_drawable_width (drawable_ID);
159 drawable_height = gimp_drawable_height (drawable_ID);
160
161 switch (drawable_type)
162 {
163 case GIMP_RGBA_IMAGE:
164 format = babl_format ("R'G'B'A u8");
165 colors = 0;
166 BitsPerPixel = 32;
167 MapSize = 0;
168 channels = 4;
169 BMPSaveData.rgb_format = RGBA_8888;
170 break;
171
172 case GIMP_RGB_IMAGE:
173 format = babl_format ("R'G'B' u8");
174 colors = 0;
175 BitsPerPixel = 24;
176 MapSize = 0;
177 channels = 3;
178 BMPSaveData.rgb_format = RGB_888;
179 break;
180
181 case GIMP_GRAYA_IMAGE:
182 if (run_mode == GIMP_RUN_INTERACTIVE &&
183 ! warning_dialog (_("Cannot export indexed image with "
184 "transparency in BMP file format."),
185 _("Alpha channel will be ignored.")))
186 return GIMP_PDB_CANCEL;
187
188 /* fallthrough */
189
190 case GIMP_GRAY_IMAGE:
191 colors = 256;
192 BitsPerPixel = 8;
193 MapSize = 1024;
194
195 if (drawable_type == GIMP_GRAYA_IMAGE)
196 {
197 format = babl_format ("Y'A u8");
198 channels = 2;
199 }
200 else
201 {
202 format = babl_format ("Y' u8");
203 channels = 1;
204 }
205
206 for (i = 0; i < colors; i++)
207 {
208 Red[i] = i;
209 Green[i] = i;
210 Blue[i] = i;
211 }
212 break;
213
214 case GIMP_INDEXEDA_IMAGE:
215 if (run_mode == GIMP_RUN_INTERACTIVE &&
216 ! warning_dialog (_("Cannot export indexed image with "
217 "transparency in BMP file format."),
218 _("Alpha channel will be ignored.")))
219 return GIMP_PDB_CANCEL;
220
221 /* fallthrough */
222
223 case GIMP_INDEXED_IMAGE:
224 format = gimp_drawable_get_format (drawable_ID);
225 cmap = gimp_image_get_colormap (image, &colors);
226 MapSize = 4 * colors;
227
228 if (drawable_type == GIMP_INDEXEDA_IMAGE)
229 channels = 2;
230 else
231 channels = 1;
232
233 if (colors > 16)
234 BitsPerPixel = 8;
235 else if (colors > 2)
236 BitsPerPixel = 4;
237 else
238 BitsPerPixel = 1;
239
240 for (i = 0; i < colors; i++)
241 {
242 Red[i] = *cmap++;
243 Green[i] = *cmap++;
244 Blue[i] = *cmap++;
245 }
246 break;
247
248 default:
249 g_assert_not_reached ();
250 }
251
252 BMPSaveData.use_run_length_encoding = 0;
253 BMPSaveData.dont_write_color_space_data = 0;
254 mask_info_size = 0;
255
256 if (run_mode != GIMP_RUN_NONINTERACTIVE)
257 {
258 gimp_get_data (SAVE_PROC, &BMPSaveData);
259 }
260
261 if (run_mode == GIMP_RUN_INTERACTIVE &&
262 (BitsPerPixel == 8 ||
263 BitsPerPixel == 4 ||
264 BitsPerPixel == 1))
265 {
266 if (! save_dialog (1, BitsPerPixel))
267 return GIMP_PDB_CANCEL;
268 }
269 else if (BitsPerPixel == 24 ||
270 BitsPerPixel == 32)
271 {
272 if (run_mode == GIMP_RUN_INTERACTIVE)
273 {
274 if (! save_dialog (channels, BitsPerPixel))
275 return GIMP_PDB_CANCEL;
276 }
277
278 /* mask_info_size is only set to non-zero for 16- and 32-bpp */
279 switch (BMPSaveData.rgb_format)
280 {
281 case RGB_888:
282 BitsPerPixel = 24;
283 break;
284 case RGBA_8888:
285 BitsPerPixel = 32;
286 mask_info_size = 16;
287 break;
288 case RGBX_8888:
289 BitsPerPixel = 32;
290 mask_info_size = 16;
291 break;
292 case RGB_565:
293 BitsPerPixel = 16;
294 mask_info_size = 16;
295 break;
296 case RGBA_5551:
297 BitsPerPixel = 16;
298 mask_info_size = 16;
299 break;
300 case RGB_555:
301 BitsPerPixel = 16;
302 mask_info_size = 16;
303 break;
304 default:
305 g_return_val_if_reached (GIMP_PDB_EXECUTION_ERROR);
306 }
307 }
308
309 gimp_set_data (SAVE_PROC, &BMPSaveData, sizeof (BMPSaveData));
310
311 /* Let's begin the progress */
312 gimp_progress_init_printf (_("Exporting '%s'"),
313 gimp_filename_to_utf8 (filename));
314
315 /* Let's take some file */
316 outfile = g_fopen (filename, "wb");
317 if (!outfile)
318 {
319 g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
320 _("Could not open '%s' for writing: %s"),
321 gimp_filename_to_utf8 (filename), g_strerror (errno));
322 return GIMP_PDB_EXECUTION_ERROR;
323 }
324
325 /* fetch the image */
326 pixels = g_new (guchar, drawable_width * drawable_height * channels);
327
328 gegl_buffer_get (buffer, GEGL_RECTANGLE (0, 0,
329 drawable_width, drawable_height), 1.0,
330 format, pixels,
331 GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
332
333 g_object_unref (buffer);
334
335 /* Now, we need some further information ... */
336 cols = drawable_width;
337 rows = drawable_height;
338
339 /* ... that we write to our headers. */
340 if ((BitsPerPixel <= 8) && (cols % (8 / BitsPerPixel)))
341 Spcols = (((cols / (8 / BitsPerPixel)) + 1) * (8 / BitsPerPixel));
342 else
343 Spcols = cols;
344
345 if ((((Spcols * BitsPerPixel) / 8) % 4) == 0)
346 SpZeile = ((Spcols * BitsPerPixel) / 8);
347 else
348 SpZeile = ((gint) (((Spcols * BitsPerPixel) / 8) / 4) + 1) * 4;
349
350 if (! BMPSaveData.dont_write_color_space_data)
351 {
352 /* Always include color mask for BITMAPV5HEADER, see #4155. */
353 mask_info_size = 16;
354 color_space_size = 68;
355 }
356 else
357 {
358 color_space_size = 0;
359 }
360
361 bitmap_file_head.bfSize = (0x36 + MapSize + (rows * SpZeile) +
362 mask_info_size + color_space_size);
363 bitmap_file_head.zzHotX = 0;
364 bitmap_file_head.zzHotY = 0;
365 bitmap_file_head.bfOffs = (0x36 + MapSize +
366 mask_info_size + color_space_size);
367 bitmap_file_head.biSize = 40 + mask_info_size + color_space_size;
368
369 bitmap_head.biWidth = cols;
370 bitmap_head.biHeight = rows;
371 bitmap_head.biPlanes = 1;
372 bitmap_head.biBitCnt = BitsPerPixel;
373
374 if (BMPSaveData.use_run_length_encoding == 0)
375 {
376 /* The Microsoft specification for BITMAPV5HEADER says that
377 * BI_BITFIELDS is valid for 16 and 32-bits per pixel,
378 * Since it doesn't mention 24 bpp or other numbers
379 * use BI_RGB for that. See issue #6114. */
380 if (mask_info_size > 0 && (BitsPerPixel == 16 || BitsPerPixel == 32))
381 bitmap_head.biCompr = 3; /* BI_BITFIELDS */
382 else
383 bitmap_head.biCompr = 0; /* BI_RGB */
384 }
385 else if (BitsPerPixel == 8)
386 {
387 bitmap_head.biCompr = 1;
388 }
389 else if (BitsPerPixel == 4)
390 {
391 bitmap_head.biCompr = 2;
392 }
393 else
394 {
395 bitmap_head.biCompr = 0;
396 }
397
398 bitmap_head.biSizeIm = SpZeile * rows;
399
400 {
401 gdouble xresolution;
402 gdouble yresolution;
403 gimp_image_get_resolution (image, &xresolution, &yresolution);
404
405 if (xresolution > GIMP_MIN_RESOLUTION &&
406 yresolution > GIMP_MIN_RESOLUTION)
407 {
408 /*
409 * xresolution and yresolution are in dots per inch.
410 * the BMP spec says that biXPels and biYPels are in
411 * pixels per meter as long ints (actually, "DWORDS"),
412 * so...
413 * n dots inch 100 cm m dots
414 * ------ * ------- * ------ = ------
415 * inch 2.54 cm m inch
416 *
417 * We add 0.5 for proper rounding.
418 */
419 bitmap_head.biXPels = (long int) (xresolution * 100.0 / 2.54 + 0.5);
420 bitmap_head.biYPels = (long int) (yresolution * 100.0 / 2.54 + 0.5);
421 }
422 }
423
424 if (BitsPerPixel <= 8)
425 bitmap_head.biClrUsed = colors;
426 else
427 bitmap_head.biClrUsed = 0;
428
429 bitmap_head.biClrImp = bitmap_head.biClrUsed;
430
431 #ifdef DEBUG
432 printf ("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n",
433 (int)bitmap_file_head.bfSize,
434 (int)bitmap_head.biClrUsed,
435 bitmap_head.biBitCnt,
436 (int)bitmap_head.biWidth,
437 (int)bitmap_head.biHeight,
438 (int)bitmap_head.biCompr,SpZeile);
439 #endif
440
441 /* And now write the header and the colormap (if any) to disk */
442
443 Write (outfile, "BM", 2);
444
445 bitmap_file_head.bfSize = GUINT32_TO_LE (bitmap_file_head.bfSize);
446 bitmap_file_head.zzHotX = GUINT16_TO_LE (bitmap_file_head.zzHotX);
447 bitmap_file_head.zzHotY = GUINT16_TO_LE (bitmap_file_head.zzHotY);
448 bitmap_file_head.bfOffs = GUINT32_TO_LE (bitmap_file_head.bfOffs);
449 bitmap_file_head.biSize = GUINT32_TO_LE (bitmap_file_head.biSize);
450
451 Write (outfile, &bitmap_file_head.bfSize, 16);
452
453 bitmap_head.biWidth = GINT32_TO_LE (bitmap_head.biWidth);
454 bitmap_head.biHeight = GINT32_TO_LE (bitmap_head.biHeight);
455 bitmap_head.biPlanes = GUINT16_TO_LE (bitmap_head.biPlanes);
456 bitmap_head.biBitCnt = GUINT16_TO_LE (bitmap_head.biBitCnt);
457 bitmap_head.biCompr = GUINT32_TO_LE (bitmap_head.biCompr);
458 bitmap_head.biSizeIm = GUINT32_TO_LE (bitmap_head.biSizeIm);
459 bitmap_head.biXPels = GUINT32_TO_LE (bitmap_head.biXPels);
460 bitmap_head.biYPels = GUINT32_TO_LE (bitmap_head.biYPels);
461 bitmap_head.biClrUsed = GUINT32_TO_LE (bitmap_head.biClrUsed);
462 bitmap_head.biClrImp = GUINT32_TO_LE (bitmap_head.biClrImp);
463
464 Write (outfile, &bitmap_head, 36);
465
466 if (mask_info_size > 0)
467 {
468 switch (BMPSaveData.rgb_format)
469 {
470 default:
471 case RGB_888:
472 case RGBX_8888:
473 Mask[0] = 0x00ff0000;
474 Mask[1] = 0x0000ff00;
475 Mask[2] = 0x000000ff;
476 Mask[3] = 0x00000000;
477 break;
478
479 case RGBA_8888:
480 Mask[0] = 0x00ff0000;
481 Mask[1] = 0x0000ff00;
482 Mask[2] = 0x000000ff;
483 Mask[3] = 0xff000000;
484 break;
485
486 case RGB_565:
487 Mask[0] = 0xf800;
488 Mask[1] = 0x7e0;
489 Mask[2] = 0x1f;
490 Mask[3] = 0x0;
491 break;
492
493 case RGBA_5551:
494 Mask[0] = 0x7c00;
495 Mask[1] = 0x3e0;
496 Mask[2] = 0x1f;
497 Mask[3] = 0x8000;
498 break;
499
500 case RGB_555:
501 Mask[0] = 0x7c00;
502 Mask[1] = 0x3e0;
503 Mask[2] = 0x1f;
504 Mask[3] = 0x0;
505 break;
506 }
507
508 Mask[0] = GUINT32_TO_LE (Mask[0]);
509 Mask[1] = GUINT32_TO_LE (Mask[1]);
510 Mask[2] = GUINT32_TO_LE (Mask[2]);
511 Mask[3] = GUINT32_TO_LE (Mask[3]);
512
513 Write (outfile, &Mask, mask_info_size);
514 }
515
516 if (! BMPSaveData.dont_write_color_space_data)
517 {
518 guint32 buf[0x11];
519
520 /* Write V5 color space fields */
521
522 /* bV5CSType = LCS_sRGB */
523 buf[0x00] = GUINT32_TO_LE (0x73524742);
524
525 /* bV5Endpoints is set to 0 (ignored) */
526 for (i = 0; i < 0x09; i++)
527 buf[i + 1] = 0x00;
528
529 /* bV5GammaRed is set to 0 (ignored) */
530 buf[0x0a] = GUINT32_TO_LE (0x0);
531
532 /* bV5GammaGreen is set to 0 (ignored) */
533 buf[0x0b] = GUINT32_TO_LE (0x0);
534
535 /* bV5GammaBlue is set to 0 (ignored) */
536 buf[0x0c] = GUINT32_TO_LE (0x0);
537
538 /* bV5Intent = LCS_GM_GRAPHICS */
539 buf[0x0d] = GUINT32_TO_LE (0x00000002);
540
541 /* bV5ProfileData is set to 0 (ignored) */
542 buf[0x0e] = GUINT32_TO_LE (0x0);
543
544 /* bV5ProfileSize is set to 0 (ignored) */
545 buf[0x0f] = GUINT32_TO_LE (0x0);
546
547 /* bV5Reserved = 0 */
548 buf[0x10] = GUINT32_TO_LE (0x0);
549
550 Write (outfile, buf, color_space_size);
551 }
552
553 write_color_map (outfile, Red, Green, Blue, MapSize);
554
555 /* After that is done, we write the image ... */
556
557 write_image (outfile,
558 pixels, cols, rows,
559 BMPSaveData.use_run_length_encoding,
560 channels, BitsPerPixel, SpZeile,
561 MapSize, BMPSaveData.rgb_format,
562 mask_info_size, color_space_size);
563
564 /* ... and exit normally */
565
566 fclose (outfile);
567 g_free (pixels);
568
569 return GIMP_PDB_SUCCESS;
570 }
571
572 static inline void
Make565(guchar r,guchar g,guchar b,guchar * buf)573 Make565 (guchar r,
574 guchar g,
575 guchar b,
576 guchar *buf)
577 {
578 gint p;
579
580 p = ((((gint) (r / 255.0 * 31.0 + 0.5)) << 11) |
581 (((gint) (g / 255.0 * 63.0 + 0.5)) << 5) |
582 (((gint) (b / 255.0 * 31.0 + 0.5))));
583
584 buf[0] = (guchar) (p & 0xff);
585 buf[1] = (guchar) (p >> 8);
586 }
587
588 static inline void
Make5551(guchar r,guchar g,guchar b,guchar a,guchar * buf)589 Make5551 (guchar r,
590 guchar g,
591 guchar b,
592 guchar a,
593 guchar *buf)
594 {
595 gint p;
596
597 p = ((((gint) (r / 255.0 * 31.0 + 0.5)) << 10) |
598 (((gint) (g / 255.0 * 31.0 + 0.5)) << 5) |
599 (((gint) (b / 255.0 * 31.0 + 0.5))) |
600 (((gint) (a / 255.0 + 0.5) << 15)));
601
602 buf[0] = (guchar) (p & 0xff);
603 buf[1] = (guchar) (p >> 8);
604 }
605
606 static void
write_image(FILE * f,guchar * src,gint width,gint height,gint use_run_length_encoding,gint channels,gint bpp,gint spzeile,gint MapSize,RGBMode rgb_format,gint mask_info_size,gint color_space_size)607 write_image (FILE *f,
608 guchar *src,
609 gint width,
610 gint height,
611 gint use_run_length_encoding,
612 gint channels,
613 gint bpp,
614 gint spzeile,
615 gint MapSize,
616 RGBMode rgb_format,
617 gint mask_info_size,
618 gint color_space_size)
619 {
620 guchar buf[16] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0 };
621 guint32 uint32buf;
622 guchar *temp, v;
623 guchar *row, *ketten;
624 gint xpos, ypos, i, j, rowstride, length, thiswidth;
625 gint breite, k;
626 guchar n, r, g, b, a;
627 gint cur_progress;
628 gint max_progress;
629
630 xpos = 0;
631 rowstride = width * channels;
632
633 cur_progress = 0;
634 max_progress = height;
635
636 /* We'll begin with the 16/24/32 bit Bitmaps, they are easy :-) */
637
638 if (bpp > 8)
639 {
640 for (ypos = height - 1; ypos >= 0; ypos--) /* for each row */
641 {
642 for (i = 0; i < width; i++) /* for each pixel */
643 {
644 temp = src + (ypos * rowstride) + (xpos * channels);
645 switch (rgb_format)
646 {
647 default:
648 case RGB_888:
649 buf[2] = *temp++;
650 buf[1] = *temp++;
651 buf[0] = *temp++;
652 xpos++;
653 if (channels > 3 && (guchar) *temp == 0)
654 buf[0] = buf[1] = buf[2] = 0xff;
655 Write (f, buf, 3);
656 break;
657 case RGBX_8888:
658 buf[2] = *temp++;
659 buf[1] = *temp++;
660 buf[0] = *temp++;
661 buf[3] = 0;
662 xpos++;
663 if (channels > 3 && (guchar) *temp == 0)
664 buf[0] = buf[1] = buf[2] = 0xff;
665 Write (f, buf, 4);
666 break;
667 case RGBA_8888:
668 buf[2] = *temp++;
669 buf[1] = *temp++;
670 buf[0] = *temp++;
671 buf[3] = *temp;
672 xpos++;
673 Write (f, buf, 4);
674 break;
675 case RGB_565:
676 r = *temp++;
677 g = *temp++;
678 b = *temp++;
679 if (channels > 3 && (guchar) *temp == 0)
680 r = g = b = 0xff;
681 Make565 (r, g, b, buf);
682 xpos++;
683 Write (f, buf, 2);
684 break;
685 case RGB_555:
686 r = *temp++;
687 g = *temp++;
688 b = *temp++;
689 if (channels > 3 && (guchar) *temp == 0)
690 r = g = b = 0xff;
691 Make5551 (r, g, b, 0x0, buf);
692 xpos++;
693 Write (f, buf, 2);
694 break;
695 case RGBA_5551:
696 r = *temp++;
697 g = *temp++;
698 b = *temp++;
699 a = *temp;
700 Make5551 (r, g, b, a, buf);
701 xpos++;
702 Write (f, buf, 2);
703 break;
704 }
705 }
706
707 Write (f, &buf[4], spzeile - (width * (bpp/8)));
708
709 cur_progress++;
710 if ((cur_progress % 5) == 0)
711 gimp_progress_update ((gdouble) cur_progress /
712 (gdouble) max_progress);
713
714 xpos = 0;
715 }
716 }
717 else
718 {
719 if (bpp == 1)
720 use_run_length_encoding = 0;
721 switch (use_run_length_encoding) /* now it gets more difficult */
722 { /* uncompressed 1,4 and 8 bit */
723 case 0:
724 {
725 thiswidth = (width / (8 / bpp));
726 if (width % (8 / bpp))
727 thiswidth++;
728
729 for (ypos = height - 1; ypos >= 0; ypos--) /* for each row */
730 {
731 for (xpos = 0; xpos < width;) /* for each _byte_ */
732 {
733 v = 0;
734 for (i = 1;
735 (i <= (8 / bpp)) && (xpos < width);
736 i++, xpos++) /* for each pixel */
737 {
738 temp = src + (ypos * rowstride) + (xpos * channels);
739 if (channels > 1 && *(temp+1) == 0) *temp = 0x0;
740 v=v | ((guchar) *temp << (8 - (i * bpp)));
741 }
742 Write (f, &v, 1);
743 }
744 Write (f, &buf[3], spzeile - thiswidth);
745 xpos = 0;
746
747 cur_progress++;
748 if ((cur_progress % 5) == 0)
749 gimp_progress_update ((gdouble) cur_progress /
750 (gdouble) max_progress);
751 }
752 break;
753 }
754 default:
755 { /* Save RLE encoded file, quite difficult */
756 length = 0;
757 buf[12] = 0;
758 buf[13] = 1;
759 buf[14] = 0;
760 buf[15] = 0;
761 row = g_new (guchar, width / (8 / bpp) + 10);
762 ketten = g_new (guchar, width / (8 / bpp) + 10);
763 for (ypos = height - 1; ypos >= 0; ypos--)
764 { /* each row separately */
765 j = 0;
766 /* first copy the pixels to a buffer,
767 * making one byte from two 4bit pixels
768 */
769 for (xpos = 0; xpos < width;)
770 {
771 v = 0;
772 for (i = 1;
773 (i <= (8 / bpp)) && (xpos < width);
774 i++, xpos++)
775 { /* for each pixel */
776 temp = src + (ypos * rowstride) + (xpos * channels);
777 if (channels > 1 && *(temp+1) == 0) *temp = 0x0;
778 v = v | ((guchar) * temp << (8 - (i * bpp)));
779 }
780 row[j++] = v;
781 }
782 breite = width / (8 / bpp);
783 if (width % (8 / bpp))
784 breite++;
785
786 /* then check for strings of equal bytes */
787 for (i = 0; i < breite; i += j)
788 {
789 j = 0;
790 while ((i + j < breite) &&
791 (j < (255 / (8 / bpp))) &&
792 (row[i + j] == row[i]))
793 j++;
794
795 ketten[i] = j;
796 }
797
798 /* then write the strings and the other pixels to the file */
799 for (i = 0; i < breite;)
800 {
801 if (ketten[i] < 3)
802 /* strings of different pixels ... */
803 {
804 j = 0;
805 while ((i + j < breite) &&
806 (j < (255 / (8 / bpp))) &&
807 (ketten[i + j] < 3))
808 j += ketten[i + j];
809
810 /* this can only happen if j jumps over
811 * the end with a 2 in ketten[i+j]
812 */
813 if (j > (255 / (8 / bpp)))
814 j -= 2;
815 /* 00 01 and 00 02 are reserved */
816 if (j > 2)
817 {
818 Write (f, &buf[12], 1);
819 n = j * (8 / bpp);
820 if (n + i * (8 / bpp) > width)
821 n--;
822 Write (f, &n, 1);
823 length += 2;
824 Write (f, &row[i], j);
825 length += j;
826 if ((j) % 2)
827 {
828 Write (f, &buf[12], 1);
829 length++;
830 }
831 }
832 else
833 {
834 for (k = i; k < i + j; k++)
835 {
836 n = (8 / bpp);
837 if (n + i * (8 / bpp) > width)
838 n--;
839 Write (f, &n, 1);
840 Write (f, &row[k], 1);
841 /*printf("%i.#|",n); */
842 length += 2;
843 }
844 }
845 i += j;
846 }
847 else
848 /* strings of equal pixels */
849 {
850 n = ketten[i] * (8 / bpp);
851 if (n + i * (8 / bpp) > width)
852 n--;
853 Write (f, &n, 1);
854 Write (f, &row[i], 1);
855 i += ketten[i];
856 length += 2;
857 }
858 }
859
860 Write (f, &buf[14], 2); /* End of row */
861 length += 2;
862
863 cur_progress++;
864 if ((cur_progress % 5) == 0)
865 gimp_progress_update ((gdouble) cur_progress /
866 (gdouble) max_progress);
867 }
868
869 fseek (f, -2, SEEK_CUR); /* Overwrite last End of row ... */
870 Write (f, &buf[12], 2); /* ... with End of file */
871
872 fseek (f, 0x22, SEEK_SET); /* Write length of image */
873 uint32buf = GUINT32_TO_LE (length);
874 Write (f, &uint32buf, 4);
875
876 fseek (f, 0x02, SEEK_SET); /* Write length of file */
877 length += (0x36 + MapSize + mask_info_size + color_space_size);
878 uint32buf = GUINT32_TO_LE (length);
879 Write (f, &uint32buf, 4);
880
881 g_free (ketten);
882 g_free (row);
883 break;
884 }
885 }
886 }
887
888 gimp_progress_update (1.0);
889 }
890
891 static void
format_callback(GtkToggleButton * toggle,gpointer data)892 format_callback (GtkToggleButton *toggle,
893 gpointer data)
894 {
895 if (gtk_toggle_button_get_active (toggle))
896 BMPSaveData.rgb_format = GPOINTER_TO_INT (data);
897 }
898
899 static gboolean
save_dialog(gint channels,gint bpp)900 save_dialog (gint channels, gint bpp)
901 {
902 GtkWidget *dialog;
903 GtkWidget *toggle;
904 GtkWidget *vbox_main;
905 GtkWidget *vbox;
906 GtkWidget *vbox2;
907 GtkWidget *expander;
908 GtkWidget *frame;
909 GSList *group;
910 gboolean run;
911
912 /* Dialog init */
913 dialog = gimp_export_dialog_new ("BMP", PLUG_IN_BINARY, SAVE_PROC);
914
915 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
916
917 vbox_main = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
918 gtk_container_set_border_width (GTK_CONTAINER (vbox_main), 12);
919 gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
920 vbox_main, TRUE, TRUE, 0);
921 gtk_widget_show (vbox_main);
922
923 /* Run-Length Encoded */
924 toggle = gtk_check_button_new_with_mnemonic (_("_Run-Length Encoded"));
925 gtk_box_pack_start (GTK_BOX (vbox_main), toggle, FALSE, FALSE, 0);
926 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
927 BMPSaveData.use_run_length_encoding);
928 gtk_widget_show (toggle);
929 if (channels > 1 || bpp == 1)
930 gtk_widget_set_sensitive (toggle, FALSE);
931
932 g_signal_connect (toggle, "toggled",
933 G_CALLBACK (gimp_toggle_button_update),
934 &BMPSaveData.use_run_length_encoding);
935
936 /* Compatibility Options */
937 expander = gtk_expander_new_with_mnemonic (_("Co_mpatibility Options"));
938
939 gtk_box_pack_start (GTK_BOX (vbox_main), expander, TRUE, TRUE, 0);
940 gtk_widget_show (expander);
941
942 vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
943 gtk_container_set_border_width (GTK_CONTAINER (vbox2), 12);
944 gtk_container_add (GTK_CONTAINER (expander), vbox2);
945 gtk_widget_show (vbox2);
946
947 toggle = gtk_check_button_new_with_mnemonic (_("_Do not write color space information"));
948 gimp_help_set_help_data (toggle,
949 _("Some applications can not read BMP images that "
950 "include color space information. GIMP writes "
951 "color space information by default. Enabling "
952 "this option will cause GIMP to not write color "
953 "space information to the file."),
954 NULL);
955 gtk_box_pack_start (GTK_BOX (vbox2), toggle, FALSE, FALSE, 0);
956 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle),
957 BMPSaveData.dont_write_color_space_data);
958 gtk_widget_show (toggle);
959
960 g_signal_connect (toggle, "toggled",
961 G_CALLBACK (gimp_toggle_button_update),
962 &BMPSaveData.dont_write_color_space_data);
963
964 /* Advanced Options */
965 expander = gtk_expander_new_with_mnemonic (_("_Advanced Options"));
966
967 gtk_box_pack_start (GTK_BOX (vbox_main), expander, TRUE, TRUE, 0);
968 gtk_widget_show (expander);
969
970 if (channels < 3)
971 gtk_widget_set_sensitive (expander, FALSE);
972
973 vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
974 gtk_container_set_border_width (GTK_CONTAINER (vbox2), 12);
975 gtk_container_add (GTK_CONTAINER (expander), vbox2);
976 gtk_widget_show (vbox2);
977
978 group = NULL;
979
980 frame = gimp_frame_new (_("16 bits"));
981 gtk_box_pack_start (GTK_BOX (vbox2), frame, TRUE, TRUE, 0);
982 gtk_widget_show (frame);
983
984 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
985 gtk_container_add (GTK_CONTAINER (frame), vbox);
986 gtk_widget_show (vbox);
987
988 toggle = gtk_radio_button_new_with_mnemonic (group, "_R5 G6 B5");
989 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
990 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
991 gtk_widget_show (toggle);
992 g_signal_connect (toggle, "toggled",
993 G_CALLBACK (format_callback),
994 GINT_TO_POINTER (RGB_565));
995
996 toggle = gtk_radio_button_new_with_mnemonic (group, "_A1 R5 G5 B5");
997 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
998 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
999
1000 if (channels < 4)
1001 gtk_widget_set_sensitive (toggle, FALSE);
1002
1003 gtk_widget_show (toggle);
1004
1005 g_signal_connect (toggle, "toggled",
1006 G_CALLBACK (format_callback),
1007 GINT_TO_POINTER (RGBA_5551));
1008 toggle = gtk_radio_button_new_with_mnemonic (group, "_X1 R5 G5 B5");
1009 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
1010 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1011 gtk_widget_show (toggle);
1012 g_signal_connect (toggle, "toggled",
1013 G_CALLBACK (format_callback),
1014 GINT_TO_POINTER (RGB_555));
1015
1016 frame = gimp_frame_new (_("24 bits"));
1017 gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
1018 gtk_widget_show (frame);
1019
1020 toggle = gtk_radio_button_new_with_mnemonic (group, "R_8 G8 B8");
1021 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON(toggle));
1022 gtk_container_add (GTK_CONTAINER (frame), toggle);
1023 gtk_widget_show (toggle);
1024 g_signal_connect (toggle, "toggled",
1025 G_CALLBACK (format_callback),
1026 GINT_TO_POINTER (RGB_888));
1027 if (channels < 4)
1028 {
1029 BMPSaveData.rgb_format = RGB_888;
1030 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
1031 }
1032
1033 frame = gimp_frame_new (_("32 bits"));
1034 gtk_box_pack_start (GTK_BOX (vbox2), frame, FALSE, FALSE, 0);
1035 gtk_widget_show (frame);
1036
1037 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1038 gtk_container_add (GTK_CONTAINER (frame), vbox);
1039 gtk_widget_show (vbox);
1040
1041 toggle = gtk_radio_button_new_with_mnemonic (group, "A8 R8 G8 _B8");
1042 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
1043 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1044 gtk_widget_show (toggle);
1045 g_signal_connect (toggle, "toggled",
1046 G_CALLBACK (format_callback),
1047 GINT_TO_POINTER (RGBA_8888));
1048
1049
1050 if (channels < 4)
1051 {
1052 gtk_widget_set_sensitive (toggle, FALSE);
1053 }
1054 else
1055 {
1056 BMPSaveData.rgb_format = RGBA_8888;
1057 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), TRUE);
1058 }
1059
1060 toggle = gtk_radio_button_new_with_mnemonic (group, "X8 R8 G8 _B8");
1061 group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (toggle));
1062 gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
1063 gtk_widget_show (toggle);
1064 g_signal_connect (toggle, "toggled",
1065 G_CALLBACK (format_callback),
1066 GINT_TO_POINTER (RGBX_8888));
1067
1068 /* Dialog show */
1069 gtk_widget_show (dialog);
1070
1071 run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
1072
1073 gtk_widget_destroy (dialog);
1074
1075 return run;
1076 }
1077