|
Packit |
a4058c |
/* Mac OS X .icns icons loader
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* Copyright (c) 2007 Lyonel Vincent <lyonel@ezix.org>
|
|
Packit |
a4058c |
* Copyright (c) 2007 Bastien Nocera <hadess@hadess.net>
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This library is free software; you can redistribute it and/or
|
|
Packit |
a4058c |
* modify it under the terms of the GNU Lesser General Public
|
|
Packit |
a4058c |
* License as published by the Free Software Foundation; either
|
|
Packit |
a4058c |
* version 2 of the License, or (at your option) any later version.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* This library is distributed in the hope that it will be useful,
|
|
Packit |
a4058c |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
a4058c |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
a4058c |
* Lesser General Public License for more details.
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit |
a4058c |
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
a4058c |
*/
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#ifndef _WIN32
|
|
Packit |
a4058c |
#define _GNU_SOURCE
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
#include "config.h"
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include <stdlib.h>
|
|
Packit |
a4058c |
#include <string.h>
|
|
Packit |
a4058c |
#include <errno.h>
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#include "gdk-pixbuf-private.h"
|
|
Packit |
a4058c |
#include "gdk-pixbuf-loader.h"
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
G_MODULE_EXPORT void fill_vtable (GdkPixbufModule * module);
|
|
Packit |
a4058c |
G_MODULE_EXPORT void fill_info (GdkPixbufFormat * info);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#define IN /**/
|
|
Packit |
a4058c |
#define OUT /**/
|
|
Packit |
a4058c |
#define INOUT /**/
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
struct IcnsBlockHeader
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
char id[4];
|
|
Packit |
a4058c |
guint32 size; /* caution: bigendian */
|
|
Packit |
a4058c |
};
|
|
Packit |
a4058c |
typedef struct IcnsBlockHeader IcnsBlockHeader;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
typedef struct
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbufModuleSizeFunc size_func;
|
|
Packit |
a4058c |
GdkPixbufModulePreparedFunc prepared_func;
|
|
Packit |
a4058c |
GdkPixbufModuleUpdatedFunc updated_func;
|
|
Packit |
a4058c |
gpointer user_data;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
GByteArray *byte_array;
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf; /* Our "target" */
|
|
Packit |
a4058c |
} IcnsProgressiveState;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/*
|
|
Packit |
a4058c |
* load raw icon data from 'icns' resource
|
|
Packit |
a4058c |
*
|
|
Packit |
a4058c |
* returns TRUE when successful
|
|
Packit |
a4058c |
*/
|
|
Packit |
a4058c |
static gboolean
|
|
Packit |
a4058c |
load_resources (unsigned size, IN gpointer data, gsize datalen,
|
|
Packit |
a4058c |
OUT guchar ** picture, OUT gsize * plen,
|
|
Packit |
a4058c |
OUT guchar ** mask, OUT gsize * mlen)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
IcnsBlockHeader *header = NULL;
|
|
Packit |
a4058c |
const char *bytes = NULL;
|
|
Packit |
a4058c |
const char *current = NULL;
|
|
Packit |
a4058c |
guint32 blocklen = 0;
|
|
Packit |
a4058c |
guint32 icnslen = 0;
|
|
Packit |
a4058c |
gboolean needs_mask = TRUE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (datalen < 2 * sizeof (guint32))
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
if (!data)
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
*picture = *mask = NULL;
|
|
Packit |
a4058c |
*plen = *mlen = 0;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
bytes = data;
|
|
Packit |
a4058c |
header = (IcnsBlockHeader *) data;
|
|
Packit |
a4058c |
if (memcmp (header->id, "icns", 4) != 0)
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
icnslen = GUINT32_FROM_BE (header->size);
|
|
Packit |
a4058c |
if ((icnslen > datalen) || (icnslen < 2 * sizeof (guint32)))
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
current = bytes + sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
while ((current - bytes < icnslen) && (icnslen - (current - bytes) >= sizeof (IcnsBlockHeader)))
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
header = (IcnsBlockHeader *) current;
|
|
Packit |
a4058c |
blocklen = GUINT32_FROM_BE (header->size);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* Check that blocklen isn't garbage */
|
|
Packit |
a4058c |
if (blocklen > icnslen - (current - bytes))
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
switch (size)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
case 256:
|
|
Packit |
a4058c |
case 512:
|
|
Packit |
a4058c |
if (memcmp (header->id, "ic08", 4) == 0 /* 256x256 icon */
|
|
Packit |
a4058c |
|| memcmp (header->id, "ic09", 4) == 0) /* 512x512 icon */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*picture = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*plen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
needs_mask = FALSE;
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
case 128:
|
|
Packit |
a4058c |
if (memcmp (header->id, "it32", 4) == 0) /* 128x128 icon */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*picture = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*plen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
if (memcmp (*picture, "\0\0\0\0", 4) == 0)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*picture += 4;
|
|
Packit |
a4058c |
*plen -= 4;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
if (memcmp (header->id, "t8mk", 4) == 0) /* 128x128 mask */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*mask = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*mlen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
case 48:
|
|
Packit |
a4058c |
if (memcmp (header->id, "ih32", 4) == 0) /* 48x48 icon */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*picture = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*plen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
if (memcmp (header->id, "h8mk", 4) == 0) /* 48x48 mask */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*mask = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*mlen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
case 32:
|
|
Packit |
a4058c |
if (memcmp (header->id, "il32", 4) == 0) /* 32x32 icon */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*picture = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*plen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
if (memcmp (header->id, "l8mk", 4) == 0) /* 32x32 mask */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*mask = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*mlen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
case 16:
|
|
Packit |
a4058c |
if (memcmp (header->id, "is32", 4) == 0) /* 16x16 icon */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*picture = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*plen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
if (memcmp (header->id, "s8mk", 4) == 0) /* 16x16 mask */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*mask = (gpointer) (current + sizeof (IcnsBlockHeader));
|
|
Packit |
a4058c |
*mlen = blocklen - sizeof (IcnsBlockHeader);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
break;
|
|
Packit |
a4058c |
default:
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
current += blocklen;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!*picture)
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
if (needs_mask && !*mask)
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
return TRUE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/*
|
|
Packit |
a4058c |
* uncompress RLE-encoded bytes into RGBA scratch zone:
|
|
Packit |
a4058c |
* if firstbyte >= 0x80, it indicates the number of identical bytes + 125
|
|
Packit |
a4058c |
* (repeated value is stored next: 1 byte)
|
|
Packit |
a4058c |
* otherwise, it indicates the number of non-repeating bytes - 1
|
|
Packit |
a4058c |
* (non-repeating values are stored next: n bytes)
|
|
Packit |
a4058c |
*/
|
|
Packit |
a4058c |
static gboolean
|
|
Packit |
a4058c |
uncompress (unsigned size, INOUT guchar ** source, OUT guchar * target, INOUT gsize * _remaining)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
guchar *data = *source;
|
|
Packit |
a4058c |
gsize remaining;
|
|
Packit |
a4058c |
gsize i = 0;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* The first time we're called, set remaining */
|
|
Packit |
a4058c |
if (*_remaining == 0) {
|
|
Packit |
a4058c |
remaining = size * size;
|
|
Packit |
a4058c |
} else {
|
|
Packit |
a4058c |
remaining = *_remaining;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
while (remaining > 0)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
guint8 count = 0;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (data[0] & 0x80) /* repeating byte */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
count = data[0] - 125;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (count > remaining)
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
for (i = 0; i < count; i++)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*target = data[1];
|
|
Packit |
a4058c |
target += 4;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
data += 2;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
else /* non-repeating bytes */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
count = data[0] + 1;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (count > remaining)
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
for (i = 0; i < count; i++)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
*target = data[i + 1];
|
|
Packit |
a4058c |
target += 4;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
data += count + 1;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
remaining -= count;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
*source = data;
|
|
Packit |
a4058c |
*_remaining = remaining;
|
|
Packit |
a4058c |
return TRUE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static GdkPixbuf *
|
|
Packit |
a4058c |
load_icon (unsigned size, IN gpointer data, gsize datalen)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
guchar *icon = NULL;
|
|
Packit |
a4058c |
guchar *mask = NULL;
|
|
Packit |
a4058c |
gsize isize = 0, msize = 0, i;
|
|
Packit |
a4058c |
guchar *image = NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!load_resources (size, data, datalen, &icon, &isize, &mask, &msize))
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* 256x256 icons don't use RLE or uncompressed data,
|
|
Packit |
a4058c |
* They're usually JPEG 2000 images */
|
|
Packit |
a4058c |
if (size == 256)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GdkPixbufLoader *loader;
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
loader = gdk_pixbuf_loader_new ();
|
|
Packit |
a4058c |
if (!gdk_pixbuf_loader_write (loader, icon, isize, NULL)
|
|
Packit |
a4058c |
|| !gdk_pixbuf_loader_close (loader, NULL))
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
g_object_unref (loader);
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
|
|
Packit |
a4058c |
g_object_ref (pixbuf);
|
|
Packit |
a4058c |
g_object_unref (loader);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return pixbuf;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_assert (mask);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (msize != size * size) /* wrong mask size */
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
image = (guchar *) g_try_malloc0 (size * size * 4); /* 4 bytes/pixel = RGBA */
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!image)
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (isize == size * size * 4) /* icon data is uncompressed */
|
|
Packit |
a4058c |
for (i = 0; i < size * size; i++) /* 4 bytes/pixel = ARGB (A: ignored) */
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
image[i * 4] = icon[4 * i + 1]; /* R */
|
|
Packit |
a4058c |
image[i * 4 + 1] = icon[4 * i + 2]; /* G */
|
|
Packit |
a4058c |
image[i * 4 + 2] = icon[4 * i + 3]; /* B */
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
else
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
guchar *data = icon;
|
|
Packit |
a4058c |
gsize remaining = 0;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
/* R */
|
|
Packit |
a4058c |
if (!uncompress (size, &data, image, &remaining))
|
|
Packit |
a4058c |
goto bail;
|
|
Packit |
a4058c |
/* G */
|
|
Packit |
a4058c |
if (!uncompress (size, &data, image + 1, &remaining))
|
|
Packit |
a4058c |
goto bail;
|
|
Packit |
a4058c |
/* B */
|
|
Packit |
a4058c |
if (!uncompress (size, &data, image + 2, &remaining))
|
|
Packit |
a4058c |
goto bail;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
for (i = 0; i < size * size; i++) /* copy mask to alpha channel */
|
|
Packit |
a4058c |
image[i * 4 + 3] = mask[i];
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return gdk_pixbuf_new_from_data ((guchar *) image, GDK_COLORSPACE_RGB, /* RGB image */
|
|
Packit |
a4058c |
TRUE, /* with alpha channel */
|
|
Packit |
a4058c |
8, /* 8 bits per sample */
|
|
Packit |
a4058c |
size, /* width */
|
|
Packit |
a4058c |
size, /* height */
|
|
Packit |
a4058c |
size * 4, /* no gap between rows */
|
|
Packit |
a4058c |
(GdkPixbufDestroyNotify)g_free, /* free() function */
|
|
Packit |
a4058c |
NULL); /* param to free() function */
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
bail:
|
|
Packit |
a4058c |
g_free (image);
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static int sizes[] = {
|
|
Packit |
a4058c |
256, /* late-Tiger icons */
|
|
Packit |
a4058c |
128, /* Standard OS X */
|
|
Packit |
a4058c |
48, /* Not very common */
|
|
Packit |
a4058c |
32, /* Standard Mac OS Classic (8 & 9) */
|
|
Packit |
a4058c |
24, /* OS X toolbars */
|
|
Packit |
a4058c |
16 /* used in Mac OS Classic and dialog boxes */
|
|
Packit |
a4058c |
};
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static GdkPixbuf *
|
|
Packit |
a4058c |
icns_image_load (FILE *f, GError ** error)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
GByteArray *data;
|
|
Packit |
a4058c |
GdkPixbuf *pixbuf = NULL;
|
|
Packit |
a4058c |
guint i;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
data = g_byte_array_new ();
|
|
Packit |
a4058c |
while (!feof (f))
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
gint save_errno;
|
|
Packit |
a4058c |
guchar buf[4096];
|
|
Packit |
a4058c |
gsize bytes;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
bytes = fread (buf, 1, sizeof (buf), f);
|
|
Packit |
a4058c |
save_errno = errno;
|
|
Packit |
a4058c |
data = g_byte_array_append (data, buf, bytes);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (ferror (f))
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
g_set_error (error,
|
|
Packit |
a4058c |
G_FILE_ERROR,
|
|
Packit |
a4058c |
g_file_error_from_errno (save_errno),
|
|
Packit |
a4058c |
_("Error reading ICNS image: %s"),
|
|
Packit |
a4058c |
g_strerror (save_errno));
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_byte_array_free (data, TRUE);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return NULL;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
for (i = 0; i < G_N_ELEMENTS(sizes) && !pixbuf; i++)
|
|
Packit |
a4058c |
pixbuf = load_icon (sizes[i], data->data, data->len);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_byte_array_free (data, TRUE);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!pixbuf)
|
|
Packit |
a4058c |
g_set_error_literal (error, GDK_PIXBUF_ERROR,
|
|
Packit |
a4058c |
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
|
|
Packit |
a4058c |
_("Could not decode ICNS file"));
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return pixbuf;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static void
|
|
Packit |
a4058c |
context_free (IcnsProgressiveState *context)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
g_byte_array_free (context->byte_array, TRUE);
|
|
Packit |
a4058c |
g_clear_object (&context->pixbuf);
|
|
Packit |
a4058c |
g_free (context);
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static gpointer
|
|
Packit |
a4058c |
gdk_pixbuf__icns_image_begin_load (GdkPixbufModuleSizeFunc size_func,
|
|
Packit |
a4058c |
GdkPixbufModulePreparedFunc prepared_func,
|
|
Packit |
a4058c |
GdkPixbufModuleUpdatedFunc updated_func,
|
|
Packit |
a4058c |
gpointer user_data,
|
|
Packit |
a4058c |
GError **error)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
IcnsProgressiveState *context;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
context = g_new0 (IcnsProgressiveState, 1);
|
|
Packit |
a4058c |
context->size_func = size_func;
|
|
Packit |
a4058c |
context->prepared_func = prepared_func;
|
|
Packit |
a4058c |
context->updated_func = updated_func;
|
|
Packit |
a4058c |
context->user_data = user_data;
|
|
Packit |
a4058c |
context->byte_array = g_byte_array_new ();
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return context;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static gboolean
|
|
Packit |
a4058c |
gdk_pixbuf__icns_image_stop_load (gpointer data,
|
|
Packit |
a4058c |
GError **error)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
IcnsProgressiveState *context = data;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
g_return_val_if_fail (context != NULL, TRUE);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
context_free (context);
|
|
Packit |
a4058c |
return TRUE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
static gboolean
|
|
Packit |
a4058c |
gdk_pixbuf__icns_image_load_increment (gpointer data,
|
|
Packit |
a4058c |
const guchar *buf,
|
|
Packit |
a4058c |
guint size,
|
|
Packit |
a4058c |
GError **error)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
IcnsProgressiveState *context = data;
|
|
Packit |
a4058c |
int i;
|
|
Packit |
a4058c |
int filesize;
|
|
Packit |
a4058c |
gint w, h;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
context->byte_array = g_byte_array_append (context->byte_array, buf, size);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (context->byte_array->len < 8)
|
|
Packit |
a4058c |
return TRUE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
filesize = (context->byte_array->data[4] << 24) |
|
|
Packit |
a4058c |
(context->byte_array->data[5] << 16) |
|
|
Packit |
a4058c |
(context->byte_array->data[6] << 8) |
|
|
Packit |
a4058c |
(context->byte_array->data[7]);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (context->byte_array->len < filesize)
|
|
Packit |
a4058c |
return TRUE;
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
for (i = 0; i < G_N_ELEMENTS(sizes) && !context->pixbuf; i++)
|
|
Packit |
a4058c |
context->pixbuf = load_icon (sizes[i],
|
|
Packit |
a4058c |
context->byte_array->data,
|
|
Packit |
a4058c |
context->byte_array->len);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (!context->pixbuf)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
g_set_error_literal (error, GDK_PIXBUF_ERROR,
|
|
Packit |
a4058c |
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
|
|
Packit |
a4058c |
_("Could not decode ICNS file"));
|
|
Packit |
a4058c |
return FALSE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
w = gdk_pixbuf_get_width (context->pixbuf);
|
|
Packit |
a4058c |
h = gdk_pixbuf_get_height (context->pixbuf);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (context->size_func != NULL)
|
|
Packit |
a4058c |
(*context->size_func) (&w,
|
|
Packit |
a4058c |
&h,
|
|
Packit |
a4058c |
context->user_data);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (context->prepared_func != NULL)
|
|
Packit |
a4058c |
(*context->prepared_func) (context->pixbuf,
|
|
Packit |
a4058c |
NULL,
|
|
Packit |
a4058c |
context->user_data);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
if (context->updated_func != NULL)
|
|
Packit |
a4058c |
(*context->updated_func) (context->pixbuf,
|
|
Packit |
a4058c |
0,
|
|
Packit |
a4058c |
0,
|
|
Packit |
a4058c |
gdk_pixbuf_get_width (context->pixbuf),
|
|
Packit |
a4058c |
gdk_pixbuf_get_height (context->pixbuf),
|
|
Packit |
a4058c |
context->user_data);
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
return TRUE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
#ifndef INCLUDE_icns
|
|
Packit |
a4058c |
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
|
|
Packit |
a4058c |
#else
|
|
Packit |
a4058c |
#define MODULE_ENTRY(function) void _gdk_pixbuf__icns_ ## function
|
|
Packit |
a4058c |
#endif
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
MODULE_ENTRY (fill_vtable) (GdkPixbufModule * module)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
module->load = icns_image_load;
|
|
Packit |
a4058c |
module->begin_load = gdk_pixbuf__icns_image_begin_load;
|
|
Packit |
a4058c |
module->stop_load = gdk_pixbuf__icns_image_stop_load;
|
|
Packit |
a4058c |
module->load_increment = gdk_pixbuf__icns_image_load_increment;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
MODULE_ENTRY (fill_info) (GdkPixbufFormat * info)
|
|
Packit |
a4058c |
{
|
|
Packit |
a4058c |
static const GdkPixbufModulePattern signature[] = {
|
|
Packit |
a4058c |
{"icns", NULL, 100}, /* file begins with 'icns' */
|
|
Packit |
a4058c |
{NULL, NULL, 0}
|
|
Packit |
a4058c |
};
|
|
Packit |
a4058c |
static const gchar *mime_types[] = {
|
|
Packit |
a4058c |
"image/x-icns",
|
|
Packit |
a4058c |
NULL
|
|
Packit |
a4058c |
};
|
|
Packit |
a4058c |
static const gchar *extensions[] = {
|
|
Packit |
a4058c |
"icns",
|
|
Packit |
a4058c |
NULL
|
|
Packit |
a4058c |
};
|
|
Packit |
a4058c |
|
|
Packit |
a4058c |
info->name = "icns";
|
|
Packit |
a4058c |
info->signature = (GdkPixbufModulePattern *) signature;
|
|
Packit |
a4058c |
info->description = NC_("image format", "MacOS X icon");
|
|
Packit |
a4058c |
info->mime_types = (gchar **) mime_types;
|
|
Packit |
a4058c |
info->extensions = (gchar **) extensions;
|
|
Packit |
a4058c |
info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
|
|
Packit |
a4058c |
info->license = "GPL";
|
|
Packit |
a4058c |
info->disabled = FALSE;
|
|
Packit |
a4058c |
}
|
|
Packit |
a4058c |
|