diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index 940dcdc13b..c461d45b99 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,8 @@ +1999-11-29 Jonathan Blandford + + * src/Makefile.am (ICO_LIB): Added another loader from Arjan to + handle .ico files. Cool. + 1999-11-24 Jonathan Blandford * src/io-bmp.c (OneLine24): new patch from Arjan to handle diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index 9e1a12ff60..afb095e5f1 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -13,6 +13,8 @@ endif GIF_LIB = libpixbuf-gif.la +ICO_LIB = libpixbuf-ico.la + RAS_LIB = libpixbuf-ras.la if HAVE_TIFF @@ -29,6 +31,7 @@ libexec_LTLIBRARIES = \ $(PNG_LIB) \ $(JPEG_LIB) \ $(GIF_LIB) \ + $(ICO_LIB) \ $(RAS_LIB) \ $(XPM_LIB) \ $(TIFF_LIB) \ @@ -104,6 +107,13 @@ libpixbuf_gif_la_SOURCES = io-gif.c libpixbuf_gif_la_LDFLAGS = -avoid-version -module libpixbuf_gif_la_LIBADD = +# +# The ICO loader +# +libpixbuf_ico_la_SOURCES = io-ico.c +libpixbuf_ico_la_LDFLAGS = -avoid-version -module +libpixbuf_ico_la_LIBADD = + # # The RAS loader # diff --git a/gdk-pixbuf/gdk-pixbuf-io.c b/gdk-pixbuf/gdk-pixbuf-io.c index 698d15ed7a..6f09ec2fde 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.c +++ b/gdk-pixbuf/gdk-pixbuf-io.c @@ -136,6 +136,23 @@ pixbuf_check_sunras (guchar *buffer, int size) return TRUE; } +static gboolean +pixbuf_check_ico (guchar *buffer, int size) +{ + /* Note that this may cause false positives, but .ico's don't + have a magic number.*/ + if (size < 6) + return FALSE; + if (buffer [0] != 0x0 || + buffer [1] != 0x0 || + buffer [2] != 0x1 || + buffer [3] != 0x0 || + buffer [5] != 0x0 ) + return FALSE; + + return TRUE; +} + static gboolean pixbuf_check_bmp (guchar *buffer, int size) @@ -159,6 +176,7 @@ GdkPixbufModule file_formats [] = { { "xpm", pixbuf_check_xpm, NULL, NULL, NULL, NULL, NULL, NULL }, { "pnm", pixbuf_check_pnm, NULL, NULL, NULL, NULL, NULL, NULL }, { "ras", pixbuf_check_sunras, NULL, NULL, NULL, NULL, NULL, NULL }, + { "ico", pixbuf_check_ico, NULL, NULL, NULL, NULL, NULL, NULL }, { "bmp", pixbuf_check_bmp, NULL, NULL, NULL, NULL, NULL, NULL }, { NULL, NULL, NULL, NULL, NULL, NULL, NULL } }; diff --git a/gdk-pixbuf/io-ico.c b/gdk-pixbuf/io-ico.c new file mode 100644 index 0000000000..c9e35a0a36 --- /dev/null +++ b/gdk-pixbuf/io-ico.c @@ -0,0 +1,698 @@ +/* GdkPixbuf library - Windows Bitmap image loader + * + * Copyright (C) 1999 The Free Software Foundation + * + * Authors: Arjan van de Ven + * Federico Mena-Quintero + * + * Based on io-bmp.c + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + +Icons are just like BMP's, except for the header. + +Known bugs: + * bi-tonal files aren't tested + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "gdk-pixbuf/gdk-pixbuf.h" +#include "gdk-pixbuf/gdk-pixbuf-io.h" + + + +struct headerpair { + guint width; + guint height; + guint depth; + guint Negative; /* Negative = 1 -> top down BMP, + Negative = 0 -> bottom up BMP */ +}; + +/* + +These structures are actually dummies. These are according to +the "Windows API reference guide volume II" as written by Borland, +but GCC fiddles with the alignment of the internal members. + +*/ + +struct BitmapFileHeader { + gushort bfType; + guint bfSize; + guint reserverd; + guint bfOffbits; +}; + +struct BitmapInfoHeader { + guint biSize; + guint biWidth; + guint biHeight; + gushort biPlanes; + gushort biBitCount; + guint biCompression; + guint biSizeImage; + guint biXPelsPerMeter; + guint biYPelsPerMeter; + guint biClrUsed; + guint biClrImportant; +}; + +static void DumpBIH(unsigned char *BIH) +{ /* For debugging */ + printf("biSize = %i \n", + (BIH[3] << 24) + (BIH[2] << 16) + (BIH[1] << 8) + (BIH[0])); + printf("biWidth = %i \n", + (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4])); + printf("biHeight = %i \n", + (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + + (BIH[8])); + printf("biPlanes = %i \n", (BIH[13] << 8) + (BIH[12])); + printf("biBitCount = %i \n", (BIH[15] << 8) + (BIH[14])); + printf("biCompress = %i \n", + (BIH[19] << 24) + (BIH[18] << 16) + (BIH[17] << 8) + + (BIH[16])); + printf("biSizeImage = %i \n", + (BIH[23] << 24) + (BIH[22] << 16) + (BIH[21] << 8) + + (BIH[20])); + printf("biXPels = %i \n", + (BIH[27] << 24) + (BIH[26] << 16) + (BIH[25] << 8) + + (BIH[24])); + printf("biYPels = %i \n", + (BIH[31] << 24) + (BIH[30] << 16) + (BIH[29] << 8) + + (BIH[28])); + printf("biClrUsed = %i \n", + (BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + + (BIH[32])); + printf("biClrImprtnt= %i \n", + (BIH[39] << 24) + (BIH[38] << 16) + (BIH[37] << 8) + + (BIH[36])); +} + +/* + This does a byte-order swap. Does glib have something like + be32_to_cpu() ?? +*/ + +static unsigned int le32_to_cpu(guint i) +{ + unsigned int i2; + return i2; +} + +/* + Destroy notification function for the libart pixbuf +*/ + +static void free_buffer(gpointer user_data, gpointer data) +{ + free(data); +} + + +/* Progressive loading */ + +struct ico_progressive_state { + ModulePreparedNotifyFunc prepared_func; + ModuleUpdatedNotifyFunc updated_func; + gpointer user_data; + + gint HeaderSize; /* The size of the header-part (incl colormap) */ + guchar *HeaderBuf; /* The buffer for the header (incl colormap) */ + gint BytesInHeaderBuf; /* The size of the allocated HeaderBuf */ + gint HeaderDone; /* The nr of bytes actually in HeaderBuf */ + + gint LineWidth; /* The width of a line in bytes */ + guchar *LineBuf; /* Buffer for 1 line */ + gint LineDone; /* # of bytes in LineBuf */ + gint Lines; /* # of finished lines */ + + gint Type; /* + 24 = RGB + 8 = 8 bit colormapped + 1 = 1 bit bitonal + */ + + + struct headerpair Header; /* Decoded (BE->CPU) header */ + + gint DIBoffset; + gint ImageScore; + + + GdkPixbuf *pixbuf; /* Our "target" */ +}; + +gpointer +image_begin_load(ModulePreparedNotifyFunc prepared_func, + ModuleUpdatedNotifyFunc updated_func, gpointer user_data); +void image_stop_load(gpointer data); +gboolean image_load_increment(gpointer data, guchar * buf, guint size); + + + +/* Shared library entry point */ +GdkPixbuf *image_load(FILE * f) +{ + guchar *membuf; + size_t length; + struct ico_progressive_state *State; + int fd; + + GdkPixbuf *pb; + + State = image_begin_load(NULL, NULL, NULL); + membuf = g_malloc(4096); + + g_assert(membuf != NULL); + + + while (feof(f) == 0) { + length = fread(membuf, 1, 4096, f); + if (length > 0) + image_load_increment(State, membuf, length); + + } + g_free(membuf); + if (State->pixbuf != NULL) + gdk_pixbuf_ref(State->pixbuf); + + pb = State->pixbuf; + + image_stop_load(State); + return State->pixbuf; +} + +static void DecodeHeader(guchar *Data, gint Bytes, + struct ico_progressive_state *State) +{ +/* For ICO's we have to be very clever. There are multiple images possible + in an .ICO. For now, we select (in order of priority): + 1) The one with the highest number of colors + 2) The largest one + */ + + gint IconCount = 0; /* The number of icon-versions in the file */ + guchar *BIH; /* The DIB for the used icon */ + guchar *Ptr; + gint I; + + /* Step 1: The ICO header */ + + IconCount = (Data[5] << 8) + (Data[4]); + + printf("There are %i icons in this file \n",IconCount); + State->HeaderSize = 6 + IconCount*16; + + if (State->HeaderSize>State->BytesInHeaderBuf) { + State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize); + State->BytesInHeaderBuf = State->HeaderSize; + } + if (Bytes < State->HeaderSize) + return; + + /* We now have all the "short-specs" of the versions + So we iterate through them and select the best one */ + + State->ImageScore = 0; + State->DIBoffset = 0; + Ptr = Data + 6; + for (I=0;IState->ImageScore) { + State->ImageScore = ThisScore; + State->DIBoffset = (Ptr[15]<<24)+(Ptr[14]<<16)+ + (Ptr[13]<<8) + (Ptr[12]); + printf("*"); + + } + + printf("\n"); + + Ptr += 16; + } + + /* We now have a winner, pointed to in State->DIBoffset, + so we know how many bytes are in the "header" part. */ + + State->HeaderSize = State->DIBoffset + 40; /* 40 = sizeof(InfoHeader) */ + + if (State->HeaderSize>State->BytesInHeaderBuf) { + State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize); + State->BytesInHeaderBuf = State->HeaderSize; + } + if (BytesHeaderSize) + return; + + BIH = Data+State->DIBoffset; + +/* DumpBIH(BIH);*/ + + /* Add the palette to the headersize */ + + State->Header.width = + (BIH[7] << 24) + (BIH[6] << 16) + (BIH[5] << 8) + (BIH[4]); + State->Header.height = + (BIH[11] << 24) + (BIH[10] << 16) + (BIH[9] << 8) + (BIH[8])/2; + /* /2 because the BIH height includes the transparency mask */ + State->Header.depth = (BIH[15] << 8) + (BIH[14]);; + + State->Type = State->Header.depth; /* This may be less trivial someday */ + if (State->Lines>State->Header.height) + State->Type = 1; + + I =(BIH[35] << 24) + (BIH[34] << 16) + (BIH[33] << 8) + (BIH[32]); + if ((I==0)&&(State->Type==1)) + I = 2*4; + if ((I==0)&&(State->Type==4)) + I = 16*4; + if ((I==0)&&(State->Type==8)) + I = 256*4; + + State->HeaderSize+=I; + + if (State->HeaderSize>State->BytesInHeaderBuf) { + State->HeaderBuf=g_realloc(State->HeaderBuf,State->HeaderSize); + State->BytesInHeaderBuf = State->HeaderSize; + } + if (Bytes < State->HeaderSize) + return; + + if ((BIH[16] != 0) || (BIH[17] != 0) || (BIH[18] != 0) + || (BIH[19] != 0)) { + g_assert(0); /* Compressed icons aren't allowed */ + } + + if (State->Header.height < 0) { + State->Header.height = -State->Header.height; + State->Header.Negative = 1; + } + if (State->Header.width < 0) { + State->Header.width = -State->Header.width; + State->Header.Negative = 0; + } + + if (State->Type == 24) + State->LineWidth = State->Header.width * 3; + if (State->Type == 8) + State->LineWidth = State->Header.width * 1; + if (State->Type == 4) { + State->LineWidth = State->Header.width/2; + if ((State->Header.width & 1) != 0) + State->LineWidth++; + } + if (State->Type == 1) { + State->LineWidth = State->Header.width / 8; + if ((State->Header.width & 7) != 0) + State->LineWidth++; + } + + /* Pad to a 32 bit boundary */ + if (((State->LineWidth % 4) > 0)) + State->LineWidth = (State->LineWidth / 3) * 3 + 3; + + + if (State->LineBuf == NULL) + State->LineBuf = g_malloc(State->LineWidth); + + g_assert(State->LineBuf != NULL); + + + if (State->pixbuf == NULL) { + State->pixbuf = + gdk_pixbuf_new(ART_PIX_RGB, TRUE, 8, + (gint) State->Header.width, + (gint) State->Header.height); + + if (State->prepared_func != NULL) + /* Notify the client that we are ready to go */ + (*State->prepared_func) (State->pixbuf, + State->user_data); + + } + +} + +/* + * func - called when we have pixmap created (but no image data) + * user_data - passed as arg 1 to func + * return context (opaque to user) + */ + +gpointer +image_begin_load(ModulePreparedNotifyFunc prepared_func, + ModuleUpdatedNotifyFunc updated_func, gpointer user_data) +{ + struct ico_progressive_state *context; + + context = g_new0(struct ico_progressive_state, 1); + context->prepared_func = prepared_func; + context->updated_func = updated_func; + context->user_data = user_data; + + context->HeaderSize = 54; + context->HeaderBuf = g_malloc(14 + 40 + 4*256 + 512); + /* 4*256 for the colormap */ + context->BytesInHeaderBuf = 14 + 40 + 4*256 + 512 ; + context->HeaderDone = 0; + + context->LineWidth = 0; + context->LineBuf = NULL; + context->LineDone = 0; + context->Lines = 0; + + context->Type = 0; + + memset(&context->Header, 0, sizeof(struct headerpair)); + + + context->pixbuf = NULL; + + + return (gpointer) context; +} + +/* + * context - returned from image_begin_load + * + * free context, unref gdk_pixbuf + */ +void image_stop_load(gpointer data) +{ + struct ico_progressive_state *context = + (struct ico_progressive_state *) data; + + + g_return_if_fail(context != NULL); + + if (context->LineBuf != NULL) + g_free(context->LineBuf); + context->LineBuf = NULL; + if (context->HeaderBuf != NULL) + g_free(context->HeaderBuf); + + if (context->pixbuf) + gdk_pixbuf_unref(context->pixbuf); + + g_free(context); +} + + +static void OneLine24(struct ico_progressive_state *context) +{ + gint X; + guchar *Pixels; + + X = 0; + if (context->Header.Negative == 0) + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + (context->Header.height - context->Lines - 1); + else + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + context->Lines; + while (X < context->Header.width) { + Pixels[X * 4 + 0] = context->LineBuf[X * 3 + 2]; + Pixels[X * 4 + 1] = context->LineBuf[X * 3 + 1]; + Pixels[X * 4 + 2] = context->LineBuf[X * 3 + 0]; + X++; + } + +} + +static void OneLine8(struct ico_progressive_state *context) +{ + gint X; + guchar *Pixels; + + X = 0; + if (context->Header.Negative == 0) + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + (context->Header.height - context->Lines - 1); + else + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + context->Lines; + while (X < context->Header.width) { + /* The joys of having a BGR byteorder */ + Pixels[X * 4 + 0] = + context->HeaderBuf[4 * context->LineBuf[X] + 42+context->DIBoffset]; + Pixels[X * 4 + 1] = + context->HeaderBuf[4 * context->LineBuf[X] + 41+context->DIBoffset]; + Pixels[X * 4 + 2] = + context->HeaderBuf[4 * context->LineBuf[X] + 40+context->DIBoffset]; + X++; + } +} +static void OneLine4(struct ico_progressive_state *context) +{ + gint X; + guchar *Pixels; + + X = 0; + if (context->Header.Negative == 0) + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + (context->Header.height - context->Lines - 1); + else + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + context->Lines; + + while (X < context->Header.width) { + guchar Pix; + + Pix = context->LineBuf[X/2]; + + Pixels[X * 4 + 0] = + context->HeaderBuf[4 * (Pix>>4) + 42+context->DIBoffset]; + Pixels[X * 4 + 1] = + context->HeaderBuf[4 * (Pix>>4) + 41+context->DIBoffset]; + Pixels[X * 4 + 2] = + context->HeaderBuf[4 * (Pix>>4) + 40+context->DIBoffset]; + X++; + if (XHeader.width) { + /* Handle the other 4 bit pixel only when there is one */ + Pixels[X * 4 + 0] = + context->HeaderBuf[4 * (Pix&15) + 42+context->DIBoffset]; + Pixels[X * 4 + 1] = + context->HeaderBuf[4 * (Pix&15) + 41+context->DIBoffset]; + Pixels[X * 4 + 2] = + context->HeaderBuf[4 * (Pix&15) + 40+context->DIBoffset]; + X++; + } + } + +} + +static void OneLine1(struct ico_progressive_state *context) +{ + gint X; + guchar *Pixels; + + X = 0; + if (context->Header.Negative == 0) + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + (context->Header.height - context->Lines - 1); + else + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + context->Lines; + while (X < context->Header.width) { + int Bit; + + Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7)); + Bit = Bit & 1; + /* The joys of having a BGR byteorder */ + Pixels[X * 4 + 0] = context->HeaderBuf[Bit + 32]; + Pixels[X * 4 + 1] = context->HeaderBuf[Bit + 2 + 32]; + Pixels[X * 4 + 2] = context->HeaderBuf[Bit + 4 + 32]; + X++; + } +} + +static void OneLineTransp(struct ico_progressive_state *context) +{ + gint X; + guchar *Pixels; + + X = 0; + if (context->Header.Negative == 0) + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + (2*context->Header.height - context->Lines - 1); + else + Pixels = context->pixbuf->art_pixbuf->pixels + + gdk_pixbuf_get_rowstride(context->pixbuf) * + (context->Lines-context->Header.height); + while (X < context->Header.width) { + int Bit; + + Bit = (context->LineBuf[X / 8]) >> (7 - (X & 7)); + Bit = Bit & 1; + /* The joys of having a BGR byteorder */ + Pixels[X * 4 + 3] = 255-Bit*255; +#if 0 + if (Bit){ + Pixels[X*4+0] = 255; + Pixels[X*4+1] = 255; + Pixels[X*4+2] = 255; + } else { + Pixels[X*4+0] = 0; + Pixels[X*4+1] = 0; + Pixels[X*4+2] = 0; + } +#endif + X++; + } +} + + +static void OneLine(struct ico_progressive_state *context) +{ + context->LineDone = 0; + + if (context->Lines >= context->Header.height*2) { + return; + } + + if (context->Lines Header.height) { + + if (context->Type == 24) + OneLine24(context); + if (context->Type == 8) + OneLine8(context); + if (context->Type == 4) + OneLine4(context); + if (context->Type == 1) + OneLine1(context); + } else + { + OneLineTransp(context); + } + + context->Lines++; + if (context->Lines>=context->Header.height) { + context->Type = 1; + context->LineWidth = context->Header.width / 8; + if ((context->Header.width & 7) != 0) + context->LineWidth++; + /* Pad to a 32 bit boundary */ + if (((context->LineWidth % 4) > 0)) + context->LineWidth = (context->LineWidth / 4) * 4 + 4; + + } + + + if (context->updated_func != NULL) { + (*context->updated_func) (context->pixbuf, + context->user_data, + 0, + context->Lines, + context->Header.width, + context->Header.height); + + } +} + +/* + * context - from image_begin_load + * buf - new image data + * size - length of new image data + * + * append image data onto inrecrementally built output image + */ +gboolean image_load_increment(gpointer data, guchar * buf, guint size) +{ + struct ico_progressive_state *context = + (struct ico_progressive_state *) data; + + gint BytesToCopy; + + while (size > 0) { + printf("Y=%i C=%i H=%i\n",context->Lines,context->Type,context->Header.height); + g_assert(context->LineDone >= 0); + if (context->HeaderDone < context->HeaderSize) { /* We still + have headerbytes to do */ + BytesToCopy = + context->HeaderSize - context->HeaderDone; + if (BytesToCopy > size) + BytesToCopy = size; + + memcpy(context->HeaderBuf + context->HeaderDone, + buf, BytesToCopy); + + size -= BytesToCopy; + buf += BytesToCopy; + context->HeaderDone += BytesToCopy; + + } else + { + BytesToCopy = + context->LineWidth - context->LineDone; + if (BytesToCopy > size) + BytesToCopy = size; + + if (BytesToCopy > 0) { + memcpy(context->LineBuf + + context->LineDone, buf, + BytesToCopy); + + size -= BytesToCopy; + buf += BytesToCopy; + context->LineDone += BytesToCopy; + } + if ((context->LineDone >= context->LineWidth) && + (context->LineWidth > 0)) + OneLine(context); + + + } + + if (context->HeaderDone >= 6) + DecodeHeader(context->HeaderBuf, + context->HeaderDone, context); + + + } + + return TRUE; +}