Bug 674391 - Indexed colours not exported when save a .bmp

It's about importing in fact.

Parse BITMAPV4HEADER / BITMAPV5HEADER and use GIMP's BMP
implementation instead of using gdk_pixbuf.

Also, remove an old hack about 32bits bmp v3 + alpha. The alpha
channel is already ignored in that case.
This commit is contained in:
Téo Mazars 2013-04-13 17:16:56 +02:00
parent 55a70729ae
commit 56f065321c
1 changed files with 92 additions and 109 deletions

View File

@ -55,6 +55,58 @@ static gint32 ReadImage (FILE *fd,
GError **error);
static void
setMasksDefault (gushort biBitCnt, Bitmap_Channel *masks)
{
switch (biBitCnt)
{
case 32:
masks[0].mask = 0x00ff0000;
masks[0].shiftin = 16;
masks[0].max_value= (gfloat)255.0;
masks[1].mask = 0x0000ff00;
masks[1].shiftin = 8;
masks[1].max_value= (gfloat)255.0;
masks[2].mask = 0x000000ff;
masks[2].shiftin = 0;
masks[2].max_value= (gfloat)255.0;
masks[3].mask = 0x00000000;
masks[3].shiftin = 0;
masks[3].max_value= (gfloat)0.0;
break;
case 24:
masks[0].mask = 0xff0000;
masks[0].shiftin = 16;
masks[0].max_value= (gfloat)255.0;
masks[1].mask = 0x00ff00;
masks[1].shiftin = 8;
masks[1].max_value= (gfloat)255.0;
masks[2].mask = 0x0000ff;
masks[2].shiftin = 0;
masks[2].max_value= (gfloat)255.0;
masks[3].mask = 0x0;
masks[3].shiftin = 0;
masks[3].max_value= (gfloat)0.0;
break;
case 16:
masks[0].mask = 0x7c00;
masks[0].shiftin = 10;
masks[0].max_value= (gfloat)31.0;
masks[1].mask = 0x03e0;
masks[1].shiftin = 5;
masks[1].max_value= (gfloat)31.0;
masks[2].mask = 0x001f;
masks[2].shiftin = 0;
masks[2].max_value= (gfloat)31.0;
masks[3].mask = 0x0;
masks[3].shiftin = 0;
masks[3].max_value= (gfloat)0.0;
break;
default:
break;
}
}
static gint32
ToL (const guchar *puffer)
{
@ -137,7 +189,7 @@ ReadBMP (const gchar *name,
GError **error)
{
FILE *fd;
guchar buffer[64];
guchar buffer[124];
gint ColormapSize, rowbytes, Maps;
gboolean Grey = FALSE;
guchar ColorMap[256][3];
@ -257,7 +309,7 @@ ReadBMP (const gchar *name,
Bitmap_Head.biWidth = ToL (&buffer[0x00]); /* 12 */
Bitmap_Head.biHeight = ToL (&buffer[0x04]); /* 16 */
Bitmap_Head.biPlanes = ToS (&buffer[0x08]); /* 1A */
Bitmap_Head.biPlanes = ToS (&buffer[0x08]); /* 1A */
Bitmap_Head.biBitCnt = ToS (&buffer[0x0A]); /* 1C */
Bitmap_Head.biCompr = ToL (&buffer[0x0C]); /* 1E */
Bitmap_Head.biSizeIm = ToL (&buffer[0x10]); /* 22 */
@ -290,55 +342,9 @@ ReadBMP (const gchar *name,
}
else if (Bitmap_Head.biCompr == BI_RGB)
{
switch (Bitmap_Head.biBitCnt)
{
case 32:
masks[0].mask = 0x00ff0000;
masks[0].shiftin = 16;
masks[0].max_value= (gfloat)255.0;
masks[1].mask = 0x0000ff00;
masks[1].shiftin = 8;
masks[1].max_value= (gfloat)255.0;
masks[2].mask = 0x000000ff;
masks[2].shiftin = 0;
masks[2].max_value= (gfloat)255.0;
masks[3].mask = 0x00000000;
masks[3].shiftin = 0;
masks[3].max_value= (gfloat)0.0;
break;
case 24:
masks[0].mask = 0xff0000;
masks[0].shiftin = 16;
masks[0].max_value= (gfloat)255.0;
masks[1].mask = 0x00ff00;
masks[1].shiftin = 8;
masks[1].max_value= (gfloat)255.0;
masks[2].mask = 0x0000ff;
masks[2].shiftin = 0;
masks[2].max_value= (gfloat)255.0;
masks[3].mask = 0x0;
masks[3].shiftin = 0;
masks[3].max_value= (gfloat)0.0;
break;
case 16:
masks[0].mask = 0x7c00;
masks[0].shiftin = 10;
masks[0].max_value= (gfloat)31.0;
masks[1].mask = 0x03e0;
masks[1].shiftin = 5;
masks[1].max_value= (gfloat)31.0;
masks[2].mask = 0x001f;
masks[2].shiftin = 0;
masks[2].max_value= (gfloat)31.0;
masks[3].mask = 0x0;
masks[3].shiftin = 0;
masks[3].max_value= (gfloat)0.0;
break;
default:
break;
}
setMasksDefault (Bitmap_Head.biBitCnt, masks);
}
else
else if ((Bitmap_Head.biCompr != BI_RLE4) && (Bitmap_Head.biCompr != BI_RLE8))
{
/* BI_ALPHABITFIELDS, etc. */
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
@ -376,30 +382,10 @@ ReadBMP (const gchar *name,
Maps = 4;
ReadChannelMasks (&Bitmap_Head.masks[0], masks, 4);
}
else
else if (Bitmap_File_Head.biSize == 108 || Bitmap_File_Head.biSize == 124)
/* BMP Version 4 or 5 */
{
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(filename, NULL);
if (pixbuf)
{
gint32 layer_ID;
image_ID = gimp_image_new (gdk_pixbuf_get_width (pixbuf),
gdk_pixbuf_get_height (pixbuf),
GIMP_RGB);
layer_ID = gimp_layer_new_from_pixbuf (image_ID, _("Background"),
pixbuf,
100.,
GIMP_NORMAL_MODE, 0, 0);
g_object_unref (pixbuf);
gimp_image_set_filename (image_ID, filename);
gimp_image_insert_layer (image_ID, layer_ID, -1, -1);
return image_ID;
}
else
if (!ReadOK (fd, buffer, Bitmap_File_Head.biSize - 4))
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
@ -407,6 +393,39 @@ ReadBMP (const gchar *name,
gimp_filename_to_utf8 (filename));
return -1;
}
Bitmap_Head.biWidth = ToL (&buffer[0x00]);
Bitmap_Head.biHeight = ToL (&buffer[0x04]);
Bitmap_Head.biPlanes = ToS (&buffer[0x08]);
Bitmap_Head.biBitCnt = ToS (&buffer[0x0A]);
Bitmap_Head.biCompr = ToL (&buffer[0x0C]);
Bitmap_Head.biSizeIm = ToL (&buffer[0x10]);
Bitmap_Head.biXPels = ToL (&buffer[0x14]);
Bitmap_Head.biYPels = ToL (&buffer[0x18]);
Bitmap_Head.biClrUsed = ToL (&buffer[0x1C]);
Bitmap_Head.biClrImp = ToL (&buffer[0x20]);
Bitmap_Head.masks[0] = ToL (&buffer[0x24]);
Bitmap_Head.masks[1] = ToL (&buffer[0x28]);
Bitmap_Head.masks[2] = ToL (&buffer[0x2C]);
Bitmap_Head.masks[3] = ToL (&buffer[0x30]);
Maps = 4;
if (Bitmap_Head.biCompr == BI_BITFIELDS)
{
ReadChannelMasks (&Bitmap_Head.masks[0], masks, 4);
}
else if (Bitmap_Head.biCompr == BI_RGB)
{
setMasksDefault (Bitmap_Head.biBitCnt, masks);
}
}
else
{
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
_("Error reading BMP file header from '%s'"),
gimp_filename_to_utf8 (filename));
return -1;
}
/* Valid bit depth is 1, 4, 8, 16, 24, 32 */
@ -697,42 +716,6 @@ ReadImage (FILE *fd,
gimp_progress_update ((gdouble) cur_progress /
(gdouble) max_progress);
}
if (channels == 4)
{
gboolean has_alpha = FALSE;
/* at least one pixel should have nonzero alpha */
for (ypos = 0; ypos < height; ypos++)
{
temp = dest + (ypos * rowstride);
for (xpos = 0; xpos < width; xpos++)
{
if (temp[3])
{
has_alpha = TRUE;
break;
}
temp += 4;
}
if (has_alpha)
break;
}
/* workaround unwanted behaviour when all alpha pixels are zero */
if (!has_alpha)
{
for (ypos = 0; ypos < height; ypos++)
{
temp = dest + (ypos * rowstride);
for (xpos = 0; xpos < width; xpos++)
{
temp[3] = 255;
temp += 4;
}
}
}
}
}
break;