home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / bmp / bmpwrite.c < prev   
Encoding:
C/C++ Source or Header  |  2000-08-28  |  15.8 KB  |  610 lines

  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.  * The GIMP -- an 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 2 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, write to the Free Software
  26.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  27.  * ----------------------------------------------------------------------------
  28.  */
  29.  
  30. #include "config.h"
  31.  
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #ifdef HAVE_UNISTD_H
  36. #include <unistd.h>
  37. #endif
  38.  
  39. #include <gtk/gtk.h>
  40.  
  41. #include <libgimp/gimp.h>
  42. #include <libgimp/gimpui.h>
  43.  
  44. #include "bmp.h"
  45.  
  46. #include "libgimp/stdplugins-intl.h"
  47.  
  48. guchar *pixels;
  49. gint    cur_progress;
  50.  
  51. gint    max_progress;
  52.  
  53. typedef struct
  54. {
  55.   gint run;
  56. } BMPSaveInterface;
  57.  
  58. static BMPSaveInterface gsint =
  59. {
  60.   FALSE   /*  run  */
  61. };
  62.  
  63. gint encoded = 0;
  64.  
  65.  
  66. static gint  save_dialog      (void);
  67. static void  save_ok_callback (GtkWidget *widget,
  68.                    gpointer   data);
  69.  
  70. GimpPDBStatusType
  71. WriteBMP (gchar  *filename,
  72.       gint32  image,
  73.       gint32  drawable_ID)
  74. {
  75.   FILE *outfile;
  76.   gint Red[MAXCOLORS];
  77.   gint Green[MAXCOLORS];
  78.   gint Blue[MAXCOLORS];
  79.   guchar *cmap;
  80.   gint rows, cols, Spcols, channels, MapSize, SpZeile;
  81.   glong BitsPerPixel;
  82.   gint colors;
  83.   gchar *temp_buf;
  84.   guchar *pixels;
  85.   GimpPixelRgn pixel_rgn;
  86.   GimpDrawable *drawable;
  87.   GimpImageType drawable_type;
  88.   guchar puffer[50];
  89.   gint i;
  90.  
  91.   /* first: can we save this image? */
  92.   
  93.   drawable = gimp_drawable_get(drawable_ID);
  94.   drawable_type = gimp_drawable_type(drawable_ID);
  95.   gimp_pixel_rgn_init (&pixel_rgn, drawable,
  96.                0, 0, drawable->width, drawable->height, FALSE, FALSE);
  97.   switch (drawable_type)
  98.     {
  99.     case GIMP_RGB_IMAGE:
  100.     case GIMP_GRAY_IMAGE:
  101.     case GIMP_INDEXED_IMAGE:
  102.       break;
  103.     default:
  104.       g_message(_("BMP: cannot operate on unknown image types or alpha images"));
  105.       return GIMP_PDB_EXECUTION_ERROR;
  106.       break;
  107.     }
  108.  
  109.   /* We can save it. So what colors do we use? */
  110.  
  111.   switch (drawable_type)
  112.     {
  113.     case GIMP_RGB_IMAGE:
  114.       colors       = 0;
  115.       BitsPerPixel = 24;
  116.       MapSize      = 0;
  117.       channels     = 3;
  118.       break;
  119.     case GIMP_GRAY_IMAGE:
  120.       colors       = 256;
  121.       BitsPerPixel = 8;
  122.       MapSize      = 1024;
  123.       channels     = 1;
  124.       for (i = 0; i < colors; i++)
  125.     {
  126.       Red[i]   = i;
  127.       Green[i] = i;
  128.       Blue[i]  = i;
  129.     }
  130.       break;
  131.     case GIMP_INDEXED_IMAGE:
  132.       cmap     = gimp_image_get_cmap (image, &colors);
  133.       MapSize  = 4 * colors;
  134.       channels = 1;
  135.  
  136.       if (colors > 16)
  137.     BitsPerPixel = 8;
  138.       else if (colors > 2)
  139.     BitsPerPixel = 4;
  140.       else
  141.     BitsPerPixel = 1;
  142.       
  143.       for (i = 0; i < colors; i++)
  144.     {
  145.       Red[i]   = *cmap++;
  146.       Green[i] = *cmap++;
  147.       Blue[i]  = *cmap++;
  148.     }
  149.       break;
  150.     default:
  151.       fprintf (stderr, "%s: This should not happen\n", prog_name);
  152.       return GIMP_PDB_EXECUTION_ERROR;
  153.     }
  154.  
  155.   /* Perhaps someone wants RLE encoded Bitmaps */
  156.   encoded = 0;
  157.   if ((BitsPerPixel == 8 || BitsPerPixel == 4) && interactive_bmp)
  158.     {
  159.       if (! save_dialog ())
  160.     return GIMP_PDB_CANCEL;
  161.     }
  162.  
  163.   /* Let's take some file */  
  164.   outfile = fopen (filename, "wb");
  165.   if (!outfile)
  166.     {
  167.       g_message (_("Can't open %s"), filename);
  168.       return GIMP_PDB_EXECUTION_ERROR;
  169.     }
  170.  
  171.   /* fetch the image */
  172.   pixels = g_new (guchar, drawable->width * drawable->height * channels);
  173.   gimp_pixel_rgn_get_rect (&pixel_rgn, pixels,
  174.                0, 0, drawable->width, drawable->height);
  175.  
  176.   /* And let's begin the progress */
  177.   if (interactive_bmp)
  178.     {
  179.       temp_buf = g_strdup_printf (_("Saving %s:"), filename);
  180.       gimp_progress_init (temp_buf);
  181.       g_free (temp_buf);
  182.     }
  183.   cur_progress = 0;
  184.   max_progress = drawable->height;
  185.  
  186.   /* Now, we need some further information ... */
  187.   cols = drawable->width;
  188.   rows = drawable->height;
  189.  
  190.   /* ... that we write to our headers. */
  191.   if ((BitsPerPixel != 24) &&
  192.       (cols % (8/BitsPerPixel)))
  193.     Spcols = (((cols / (8 / BitsPerPixel)) + 1) * (8 / BitsPerPixel));
  194.   else
  195.     Spcols = cols;
  196.  
  197.   if ((((Spcols * BitsPerPixel) / 8) % 4) == 0)
  198.     SpZeile = ((Spcols * BitsPerPixel) / 8);
  199.   else
  200.     SpZeile = ((gint) (((Spcols * BitsPerPixel) / 8) / 4) + 1) * 4;
  201.  
  202.   Bitmap_File_Head.bfSize    = 0x36 + MapSize + (rows * SpZeile);
  203.   Bitmap_File_Head.zzHotX    = 0;
  204.   Bitmap_File_Head.zzHotY    = 0;
  205.   Bitmap_File_Head.bfOffs    = 0x36 + MapSize;
  206.   Bitmap_File_Head.biSize    = 40;        
  207.  
  208.   Bitmap_Head.biWidth  = cols;
  209.   Bitmap_Head.biHeight = rows;
  210.   Bitmap_Head.biPlanes = 1;
  211.   Bitmap_Head.biBitCnt = BitsPerPixel;
  212.  
  213.   if (encoded == 0)
  214.     Bitmap_Head.biCompr = 0;
  215.   else if (BitsPerPixel == 8)
  216.     Bitmap_Head.biCompr = 1;
  217.   else if (BitsPerPixel == 4)
  218.     Bitmap_Head.biCompr = 2;
  219.   else
  220.     Bitmap_Head.biCompr = 0;
  221.  
  222.   Bitmap_Head.biSizeIm = SpZeile * rows;
  223.  
  224. #ifdef GIMP_HAVE_RESOLUTION_INFO
  225.   {
  226.     gdouble xresolution;
  227.     gdouble yresolution;
  228.     gimp_image_get_resolution (image, &xresolution, &yresolution);
  229.  
  230.     if (xresolution > GIMP_MIN_RESOLUTION &&
  231.     yresolution > GIMP_MIN_RESOLUTION)
  232.       {
  233.     /*
  234.      * xresolution and yresolution are in dots per inch.
  235.      * the BMP spec says that biXPels and biYPels are in
  236.      * pixels per meter as long ints (actually, "DWORDS"),
  237.      * so...
  238.      *    n dots    inch     100 cm   m dots
  239.      *    ------ * ------- * ------ = ------
  240.      *     inch    2.54 cm     m       inch
  241.      */
  242.         Bitmap_Head.biXPels = (long int) xresolution * 100.0 / 2.54;
  243.         Bitmap_Head.biYPels = (long int) yresolution * 100.0 / 2.54;
  244.       }
  245.   }
  246. #else /* GIMP_HAVE_RESOLUTION_INFO */
  247.   Bitmap_Head.biXPels = 1;
  248.   Bitmap_Head.biYPels = 1;
  249. #endif /* GIMP_HAVE_RESOLUTION_INFO */
  250.   if (BitsPerPixel < 24) 
  251.     Bitmap_Head.biClrUsed = colors;
  252.   else 
  253.     Bitmap_Head.biClrUsed = 0;
  254.  
  255.   Bitmap_Head.biClrImp = Bitmap_Head.biClrUsed;
  256.   
  257. #ifdef DEBUG
  258.   printf("\nSize: %u, Colors: %u, Bits: %u, Width: %u, Height: %u, Comp: %u, Zeile: %u\n",
  259.          (int)Bitmap_File_Head.bfSize,(int)Bitmap_Head.biClrUsed,Bitmap_Head.biBitCnt,(int)Bitmap_Head.biWidth,
  260.          (int)Bitmap_Head.biHeight, (int)Bitmap_Head.biCompr,SpZeile);
  261. #endif
  262.   
  263.   /* And now write the header and the colormap (if any) to disk */
  264.  
  265.   Write (outfile, "BM", 2);
  266.  
  267.   FromL (Bitmap_File_Head.bfSize, &puffer[0x00]);
  268.   FromS (Bitmap_File_Head.zzHotX, &puffer[0x04]);
  269.   FromS (Bitmap_File_Head.zzHotY, &puffer[0x06]);
  270.   FromL (Bitmap_File_Head.bfOffs, &puffer[0x08]);
  271.   FromL (Bitmap_File_Head.biSize, &puffer[0x0C]);
  272.  
  273.   Write (outfile, puffer, 16);
  274.  
  275.   FromL (Bitmap_Head.biWidth, &puffer[0x00]);
  276.   FromL (Bitmap_Head.biHeight, &puffer[0x04]);
  277.   FromS (Bitmap_Head.biPlanes, &puffer[0x08]);
  278.   FromS (Bitmap_Head.biBitCnt, &puffer[0x0A]);
  279.   FromL (Bitmap_Head.biCompr, &puffer[0x0C]);
  280.   FromL (Bitmap_Head.biSizeIm, &puffer[0x10]);
  281.   FromL (Bitmap_Head.biXPels, &puffer[0x14]);
  282.   FromL (Bitmap_Head.biYPels, &puffer[0x18]);
  283.   FromL (Bitmap_Head.biClrUsed, &puffer[0x1C]);
  284.   FromL (Bitmap_Head.biClrImp, &puffer[0x20]);
  285.  
  286.   Write (outfile, puffer, 36);
  287.   WriteColorMap (outfile, Red, Green, Blue, MapSize);
  288.  
  289.   /* After that is done, we write the image ... */
  290.   
  291.   WriteImage (outfile,
  292.           pixels, cols, rows,
  293.           encoded, channels, BitsPerPixel, SpZeile, MapSize);
  294.  
  295.   /* ... and exit normally */
  296.  
  297.   fclose (outfile);
  298.   gimp_drawable_detach (drawable);
  299.   g_free (pixels);
  300.  
  301.   return GIMP_PDB_SUCCESS;
  302. }
  303.  
  304. void 
  305. WriteColorMap (FILE *f, 
  306.            gint  red[MAXCOLORS], 
  307.            gint  green[MAXCOLORS],
  308.            gint  blue[MAXCOLORS], 
  309.            gint  size)
  310. {
  311.   gchar trgb[4];
  312.   gint i;
  313.  
  314.   size /= 4;
  315.   trgb[3] = 0;
  316.   for (i = 0; i < size; i++)
  317.     {
  318.       trgb[0] = (guchar) blue[i];
  319.       trgb[1] = (guchar) green[i];
  320.       trgb[2] = (guchar) red[i];
  321.       Write (f, trgb, 4);
  322.     }
  323. }
  324.  
  325. void 
  326. WriteImage (FILE   *f, 
  327.         guchar *src, 
  328.         gint    width, 
  329.         gint    height,
  330.         gint    encoded, 
  331.         gint    channels, 
  332.         gint    bpp, 
  333.         gint    spzeile, 
  334.         gint    MapSize)
  335. {
  336.   guchar buf[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0};
  337.   guchar puffer[8];
  338.   guchar *temp, v;
  339.   guchar *Zeile, *ketten;
  340.   gint xpos, ypos, i, j, rowstride, laenge, thiswidth;
  341.   gint breite, n, k;
  342.  
  343.   xpos = 0;
  344.   rowstride = width * channels;
  345.  
  346.   /* We'll begin with the 24 bit Bitmaps, they are easy :-) */
  347.  
  348.   if (bpp == 24)
  349.     {
  350.       for (ypos = height - 1; ypos >= 0; ypos--)  /* for each row   */
  351.     {
  352.       for (i = 0; i < width; i++)  /* for each pixel */
  353.         {
  354.           temp = src + (ypos * rowstride) + (xpos * channels);
  355.           buf[2] = (guchar) *temp;
  356.           temp++;
  357.           buf[1] = (guchar) *temp;
  358.           temp++;
  359.           buf[0] = (guchar) *temp;
  360.           xpos++;
  361.           Write (f, buf, 3);
  362.         }
  363.       Write (f, &buf[3], spzeile - (width * 3));
  364.  
  365.       cur_progress++;
  366.       if ((interactive_bmp) &&
  367.           ((cur_progress % 5) == 0))
  368.         gimp_progress_update ((gdouble) cur_progress /
  369.                   (gdouble) max_progress);
  370.  
  371.       xpos = 0;
  372.     }
  373.     }
  374.   else
  375.     {
  376.       switch (encoded)  /* now it gets more difficult */
  377.     {               /* uncompressed 1,4 and 8 bit */
  378.     case 0:
  379.       {
  380.         thiswidth = (width / (8 / bpp));
  381.         if (width % (8 / bpp))
  382.           thiswidth++;
  383.  
  384.         for (ypos = height - 1; ypos >= 0; ypos--) /* for each row */
  385.           {
  386.         for (xpos = 0; xpos < width;)  /* for each _byte_ */
  387.           {
  388.             v = 0;
  389.             for (i = 1;
  390.              (i <= (8 / bpp)) && (xpos < width);
  391.              i++, xpos++)  /* for each pixel */
  392.               {
  393.             temp = src + (ypos * rowstride) + (xpos * channels);
  394.             v=v | ((guchar) *temp << (8 - (i * bpp)));
  395.               }
  396.             Write (f, &v, 1);
  397.           }
  398.         Write (f, &buf[3], spzeile - thiswidth);
  399.         xpos = 0;
  400.  
  401.         cur_progress++;
  402.         if ((interactive_bmp) &&
  403.             ((cur_progress % 5) == 0))
  404.           gimp_progress_update ((gdouble) cur_progress /
  405.                     (gdouble) max_progress);
  406.           }
  407.         break;
  408.       }
  409.     default:
  410.       {         /* Save RLE encoded file, quite difficult */
  411.         laenge = 0;
  412.         buf[12] = 0;
  413.         buf[13] = 1;
  414.         buf[14] = 0;
  415.         buf[15] = 0;
  416.         Zeile = (guchar *) g_malloc (width / (8 / bpp) + 10);
  417.         ketten = (guchar *) g_malloc (width / (8 / bpp) + 10);
  418.         for (ypos = height - 1; ypos >= 0; ypos--)
  419.           {    /* each row separately */
  420.         /*printf("Line: %i\n",ypos); */
  421.         j = 0;
  422.         /* first copy the pixels to a buffer,
  423.          * making one byte from two 4bit pixels
  424.          */
  425.         for (xpos = 0; xpos < width;)
  426.           {
  427.             v = 0;
  428.             for (i = 1;
  429.              (i <= (8 / bpp)) && (xpos < width);
  430.              i++, xpos++)
  431.               {    /* for each pixel */
  432.             temp = src + (ypos * rowstride) + (xpos * channels);
  433.             v = v | ((guchar) * temp << (8 - (i * bpp)));
  434.               }
  435.             Zeile[j++] = v;
  436.           }
  437.         breite = width / (8 / bpp);
  438.         if (width % (8 / bpp))
  439.           breite++;
  440.         /* then check for strings of equal bytes */
  441.         for (i = 0; i < breite;)
  442.           {
  443.             j = 0;
  444.             while ((i + j < breite) &&
  445.                (j < (255 / (8 / bpp))) &&
  446.                (Zeile[i + j] == Zeile[i]))
  447.               j++;
  448.  
  449.             ketten[i] = j;
  450.             /*printf("%i:",ketten[i]); */
  451.             i += j;
  452.           }
  453.         /*printf("\n"); */
  454.  
  455.         /* then write the strings and the other pixels to the file */
  456.         for (i = 0; i < breite;)
  457.           {
  458.             if (ketten[i] < 3)
  459.               /* strings of different pixels ... */
  460.               {
  461.             j = 0;
  462.             while ((i + j < breite) &&
  463.                    (j < (255 / (8 / bpp))) &&
  464.                    (ketten[i + j] < 3))
  465.               j += ketten[i + j];
  466.  
  467.             /* this can only happen if j jumps over
  468.              * the end with a 2 in ketten[i+j]
  469.              */
  470.             if (j > (255 / (8 / bpp)))
  471.               j -= 2;
  472.             /* 00 01 and 00 02 are reserved */
  473.             if (j > 2)
  474.               {
  475.                 Write (f, &buf[12], 1);
  476.                 n = j * (8 / bpp);
  477.                 if (n + i * (8 / bpp) > width)
  478.                   n--;
  479.                 Write (f, &n, 1);
  480.                 laenge += 2;
  481.                 Write (f, &Zeile[i], j);
  482.                 /*printf("0.%i.",n); */
  483.                 /*for (k=j;k;k--) printf("#"); */
  484.                 laenge += j;
  485.                 if ((j) % 2)
  486.                   {
  487.                 Write (f, &buf[12], 1);
  488.                 laenge++;
  489.                 /*printf("0"); */
  490.                   }
  491.                 /*printf("|"); */
  492.               }
  493.             else
  494.               {
  495.                 for (k = i; k < i + j; k++)
  496.                   {
  497.                 n = (8 / bpp);
  498.                 if (n + i * (8 / bpp) > width)
  499.                   n--;
  500.                 Write (f, &n, 1);
  501.                 Write (f, &Zeile[k], 1);
  502.                 /*printf("%i.#|",n); */
  503.                 laenge += 2;
  504.                   }
  505.               }
  506.             i += j;
  507.               }
  508.             else
  509.               /* strings of equal pixels */
  510.               {
  511.             n = ketten[i] * (8 / bpp);
  512.             if (n + i * (8 / bpp) > width)
  513.               n--;
  514.             Write (f, &n, 1);
  515.             Write (f, &Zeile[i], 1);
  516.             /*printf("%i.#|",n); */
  517.             i += ketten[i];
  518.             laenge += 2;
  519.               }
  520.           }
  521.         /*printf("\n"); */
  522.         Write (f, &buf[14], 2);         /* End of row */
  523.         laenge += 2;
  524.  
  525.         cur_progress++;
  526.         if ((interactive_bmp) &&
  527.             ((cur_progress % 5) == 0))
  528.           gimp_progress_update ((gdouble) cur_progress /
  529.                     (gdouble) max_progress);
  530.           }
  531.         fseek (f, -2, SEEK_CUR);     /* Overwrite last End of row ... */
  532.         Write (f, &buf[12], 2);     /* ... with End of file */
  533.  
  534.         fseek (f, 0x22, SEEK_SET);            /* Write length of image */
  535.         FromL (laenge, puffer);
  536.         Write (f, puffer, 4);
  537.         fseek (f, 0x02, SEEK_SET);            /* Write length of file */
  538.         laenge += (0x36 + MapSize);
  539.         FromL (laenge, puffer);
  540.         Write (f, puffer, 4);
  541.         g_free (ketten);
  542.         g_free (Zeile);
  543.         break;
  544.       }
  545.     }
  546.     }
  547.   if (interactive_bmp)
  548.     gimp_progress_update (1);
  549. }
  550.  
  551. static gint
  552. save_dialog (void)
  553. {
  554.   GtkWidget *dlg;
  555.   GtkWidget *toggle;
  556.   GtkWidget *frame;
  557.   GtkWidget *vbox;
  558.   
  559.   dlg = gimp_dialog_new (_("Save as BMP"), "bmp",
  560.              gimp_standard_help_func, "filters/bmp.html",
  561.              GTK_WIN_POS_MOUSE,
  562.              FALSE, TRUE, FALSE,
  563.  
  564.              _("OK"), save_ok_callback,
  565.              NULL, NULL, NULL, TRUE, FALSE,
  566.              _("Cancel"), gtk_widget_destroy,
  567.              NULL, 1, NULL, FALSE, TRUE,
  568.  
  569.              NULL);
  570.  
  571.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  572.                       GTK_SIGNAL_FUNC (gtk_main_quit),
  573.                       NULL);
  574.  
  575.   /*  parameter settings  */
  576.   frame = gtk_frame_new (_("Save Options"));
  577.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  578.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  579.  
  580.   vbox = gtk_vbox_new (FALSE, 2);
  581.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  582.   gtk_container_add (GTK_CONTAINER (frame), vbox);
  583.  
  584.   toggle = gtk_check_button_new_with_label (_("RLE encoded"));
  585.   gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
  586.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  587.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  588.                       &encoded);
  589.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), encoded);
  590.   gtk_widget_show (toggle);
  591.  
  592.   gtk_widget_show (vbox);
  593.   gtk_widget_show (frame);
  594.   gtk_widget_show (dlg);
  595.  
  596.   gtk_main ();
  597.   gdk_flush ();
  598.  
  599.   return gsint.run;
  600. }
  601.  
  602. static void
  603. save_ok_callback (GtkWidget *widget,
  604.                   gpointer   data)
  605. {
  606.   gsint.run = TRUE;
  607.  
  608.   gtk_widget_destroy (GTK_WIDGET (data));
  609. }
  610.