/* bmpwrite.c Writes Bitmap files. Even RLE encoded ones. */ /* (Windows (TM) doesn't read all of those, but who */ /* cares? ;-) */ /* I changed a few things over the time, so perhaps */ /* it dos now, but now there's no Windows left on */ /* my computer... */ /* Alexander.Schulz@stud.uni-karlsruhe.de */ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include "bmp.h" #include "libgimp/stdplugins-intl.h" guchar *pixels; int cur_progress; int max_progress; typedef struct { gint run; } BMPSaveInterface; static BMPSaveInterface gsint = { FALSE /* run */ }; int encoded = 0; static gint save_dialog (); static void save_close_callback (GtkWidget *widget, gpointer data); static void save_ok_callback (GtkWidget *widget, gpointer data); static void save_toggle_update (GtkWidget *widget, gpointer data); gint WriteBMP (filename,image,drawable_ID) char *filename; gint32 image,drawable_ID; { FILE *outfile; int Red[MAXCOLORS]; int Green[MAXCOLORS]; int Blue[MAXCOLORS]; unsigned char *cmap; int rows, cols, Spcols, channels, MapSize, SpZeile; long BitsPerPixel; int colors; char *temp_buf; guchar *pixels; GPixelRgn pixel_rgn; GDrawable *drawable; GDrawableType drawable_type; guchar puffer[50]; int i; /* first: can we save this image? */ drawable = gimp_drawable_get(drawable_ID); drawable_type = gimp_drawable_type(drawable_ID); gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width, drawable->height, FALSE, FALSE); switch (drawable_type) { case RGB_IMAGE: case GRAY_IMAGE: case INDEXED_IMAGE: break; default: g_message(_("bmp: cannot operate on unknown image types or alpha images")); gimp_quit (); break; } /* We can save it. So what colors do we use? */ switch (drawable_type) { case RGB_IMAGE: colors = 0; BitsPerPixel = 24; MapSize = 0; channels = 3; break; case GRAY_IMAGE: colors = 256; BitsPerPixel=8; MapSize=1024; channels = 1; for (i = 0; i < colors; i++) { Red[i] = i; Green[i] = i; Blue[i] = i; } break; case INDEXED_IMAGE: cmap = gimp_image_get_cmap (image, &colors); MapSize = 4*colors; channels = 1; if (colors>16) BitsPerPixel=8; else if (colors>2) BitsPerPixel=4; else BitsPerPixel=1; for (i = 0; i < colors; i++) { Red[i] = *cmap++; Green[i] = *cmap++; Blue[i] = *cmap++; } break; default: fprintf (stderr, "%s: This should not happen\n", prog_name); return FALSE; } /* Perhaps someone wants RLE encoded Bitmaps */ encoded = 0; if (((BitsPerPixel==8) || (BitsPerPixel==4)) && interactive_bmp) {if (! save_dialog ()) {return -1;}} /* Let's take some file */ outfile = fopen (filename, "wb"); if (!outfile) { g_message (_("can't open %s\n"), filename); return -1; } /* fetch the image */ pixels = (guchar *) g_malloc(drawable->width*drawable->height*channels); gimp_pixel_rgn_get_rect(&pixel_rgn, pixels, 0, 0, drawable->width, drawable->height); /* And let's begin the progress */ if (interactive_bmp) { temp_buf = g_malloc (strlen (filename) + 11); sprintf (temp_buf, _("Saving %s:"), filename); gimp_progress_init (temp_buf); g_free (temp_buf); } cur_progress = 0; max_progress = drawable->height; /* Now, we need some further information ... */ cols = drawable->width; rows = drawable->height; /* ... that we write to our headers. */ if ((BitsPerPixel != 24) && (cols % (8/BitsPerPixel))) Spcols=(((cols / (8/BitsPerPixel))+1)*(8/BitsPerPixel)); else Spcols=cols; if ((((Spcols*BitsPerPixel)/8) % 4) == 0) SpZeile=((Spcols*BitsPerPixel)/8); else SpZeile=((int)(((Spcols*BitsPerPixel)/8)/4)+1)*4; Bitmap_File_Head.bfSize=0x36+MapSize+(rows*SpZeile); Bitmap_File_Head.reserverd=0; Bitmap_File_Head.bfOffs=0x36+MapSize; Bitmap_File_Head.biSize=40; Bitmap_Head.biWidth=cols; Bitmap_Head.biHeight=rows; Bitmap_Head.biPlanes=1; Bitmap_Head.biBitCnt=BitsPerPixel; if (encoded==0) Bitmap_Head.biCompr=0; else if (BitsPerPixel==8) Bitmap_Head.biCompr=1; else if (BitsPerPixel==4) Bitmap_Head.biCompr=2; else Bitmap_Head.biCompr=0; Bitmap_Head.biSizeIm=SpZeile*rows; Bitmap_Head.biXPels=1; Bitmap_Head.biYPels=1; if (BitsPerPixel<24) Bitmap_Head.biClrUsed=colors; else Bitmap_Head.biClrUsed=0; Bitmap_Head.biClrImp=Bitmap_Head.biClrUsed; #ifdef DEBUG printf("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n", (int)Bitmap_File_Head.bfSize,(int)Bitmap_Head.biClrUsed,Bitmap_Head.biBitCnt,(int)Bitmap_Head.biWidth, (int)Bitmap_Head.biHeight, (int)Bitmap_Head.biCompr,SpZeile); #endif /* And now write the header and the colormap (if any) to disk */ Write(outfile,"BM",2); FromL(Bitmap_File_Head.bfSize,&puffer[0x00]); FromL(Bitmap_File_Head.reserverd,&puffer[0x04]); FromL(Bitmap_File_Head.bfOffs,&puffer[0x08]); FromL(Bitmap_File_Head.biSize,&puffer[0x0C]); Write(outfile,puffer,16); FromL(Bitmap_Head.biWidth,&puffer[0x00]); FromL(Bitmap_Head.biHeight,&puffer[0x04]); FromS(Bitmap_Head.biPlanes,&puffer[0x08]); FromS(Bitmap_Head.biBitCnt,&puffer[0x0A]); FromL(Bitmap_Head.biCompr,&puffer[0x0C]); FromL(Bitmap_Head.biSizeIm,&puffer[0x10]); FromL(Bitmap_Head.biXPels,&puffer[0x14]); FromL(Bitmap_Head.biYPels,&puffer[0x18]); FromL(Bitmap_Head.biClrUsed,&puffer[0x1C]); FromL(Bitmap_Head.biClrImp,&puffer[0x20]); Write(outfile,puffer,36); WriteColorMap(outfile,Red,Green,Blue,MapSize); /* After that is done, we write the image ... */ WriteImage(outfile, pixels, cols, rows, encoded, channels, BitsPerPixel, SpZeile, MapSize); /* ... and exit normally */ fclose(outfile); gimp_drawable_detach(drawable); g_free(pixels); return TRUE; } void WriteColorMap(FILE *f, int red[MAXCOLORS], int green[MAXCOLORS], int blue[MAXCOLORS], int size) { char trgb[4]; int i; size=size/4; trgb[3]=0; for (i=0;i=0;ypos--) /* for each row */ { for (i=0;i=0;ypos--) /* for each row */ { for (xpos=0;xpos= 0; ypos--) { /* each row separately */ /*printf("Line: %i\n",ypos); */ j = 0; /* first copy the pixels to a buffer, making one byte from two 4bit pixels */ for (xpos = 0; xpos < width;) { v = 0; for (i = 1; (i <= (8 / bpp)) && (xpos < width); i++, xpos++) { /* for each pixel */ temp = src + (ypos * rowstride) + (xpos * channels); v = v | ((guchar) * temp << (8 - (i * bpp))); } Zeile[j++] = v; } breite = width / (8 / bpp); if (width % (8 / bpp)) breite++; /* then check for strings of equal bytes */ for (i = 0; i < breite;) { j = 0; while ((i + j < breite) && (j < (255 / (8 / bpp))) && (Zeile[i + j] == Zeile[i])) j++; ketten[i] = j; /*printf("%i:",ketten[i]); */ i += j; } /*printf("\n"); */ /* then write the strings and the other pixels to the file */ for (i = 0; i < breite;) { if (ketten[i] < 3) /* strings of different pixels ... */ { j = 0; while ((i + j < breite) && (j < (255 / (8 / bpp))) && (ketten[i + j] < 3)) j += ketten[i + j]; /* this can only happen if j jumps over the end with a 2 in ketten[i+j] */ if (j > (255 / (8 / bpp))) j -= 2; /* 00 01 and 00 02 are reserved */ if (j > 2) { Write (f, &buf[12], 1); n = j * (8 / bpp); if (n + i * (8 / bpp) > width) n--; Write (f, &n, 1); laenge += 2; Write (f, &Zeile[i], j); /*printf("0.%i.",n); */ /*for (k=j;k;k--) printf("#"); */ laenge += j; if ((j) % 2) { Write (f, &buf[12], 1); laenge++; /*printf("0"); */ } /*printf("|"); */ } else { for (k = i; k < i + j; k++) { n = (8 / bpp); if (n + i * (8 / bpp) > width) n--; Write (f, &n, 1); Write (f, &Zeile[k], 1); /*printf("%i.#|",n); */ laenge += 2; } } i += j; } else /* strings of equal pixels */ { n = ketten[i] * (8 / bpp); if (n + i * (8 / bpp) > width) n--; Write (f, &n, 1); Write (f, &Zeile[i], 1); /*printf("%i.#|",n); */ i += ketten[i]; laenge += 2; } } /*printf("\n"); */ Write (f, &buf[14], 2); /* End of row */ laenge += 2; cur_progress++; if ((interactive_bmp) && ((cur_progress % 5) == 0)) gimp_progress_update ((double)cur_progress / max_progress); } fseek (f, -2, SEEK_CUR); /* Overwrite last End of row ... */ Write (f, &buf[12], 2); /* ... with End of file */ fseek (f, 0x22, SEEK_SET); /* Write length of image */ FromL (laenge, puffer); Write (f, puffer, 4); fseek (f, 0x02, SEEK_SET); /* Write length of file */ laenge += (0x36 + MapSize); FromL (laenge, puffer); Write (f, puffer, 4); g_free (ketten); g_free (Zeile); break; } } } if (interactive_bmp) gimp_progress_update(1); } /* These ones are just copied from the GIF-plugin */ static gint save_dialog () { GtkWidget *dlg; GtkWidget *button; GtkWidget *toggle; GtkWidget *frame; GtkWidget *vbox; gchar **argv; gint argc; dlg = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dlg), _("Save as BMP")); gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE); gtk_signal_connect (GTK_OBJECT (dlg), "destroy", (GtkSignalFunc) save_close_callback, NULL); /* Action area */ button = gtk_button_new_with_label (_("OK")); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect (GTK_OBJECT (button), "clicked", (GtkSignalFunc) save_ok_callback, dlg); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_grab_default (button); gtk_widget_show (button); button = gtk_button_new_with_label (_("Cancel")); GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); gtk_signal_connect_object (GTK_OBJECT (button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (dlg)); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0); gtk_widget_show (button); /* parameter settings */ frame = gtk_frame_new (_("Save Options")); gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); gtk_container_border_width (GTK_CONTAINER (frame), 10); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0); vbox = gtk_vbox_new (FALSE, 5); gtk_container_border_width (GTK_CONTAINER (vbox), 5); gtk_container_add (GTK_CONTAINER (frame), vbox); toggle = gtk_check_button_new_with_label (_("RLE encoded")); gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0); gtk_signal_connect (GTK_OBJECT (toggle), "toggled", (GtkSignalFunc) save_toggle_update, &encoded); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), encoded); gtk_widget_show (toggle); gtk_widget_show (vbox); gtk_widget_show (frame); gtk_widget_show (dlg); gtk_main (); gdk_flush (); return gsint.run; } static void save_close_callback (GtkWidget *widget, gpointer data) { gtk_main_quit (); } static void save_ok_callback (GtkWidget *widget, gpointer data) { gsint.run = TRUE; gtk_widget_destroy (GTK_WIDGET (data)); } static void save_toggle_update (GtkWidget *widget, gpointer data) { int *toggle_val; toggle_val = (int *) data; if (GTK_TOGGLE_BUTTON (widget)->active) *toggle_val = TRUE; else *toggle_val = FALSE; }