/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * go-image.c: Image formats * * Copyright (C) 2004, 2005 Jody Goldberg (jody@gnome.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 * USA */ #include #include #include #include #include #include #include /** * go_mime_to_image_format: * @mime_type: a mime type string * * returns: file extension for the given mime type. **/ char * go_mime_to_image_format (char const *mime_type) { guint i; const char *suffix; const char* exceptions[] = { "svg+xml", "svg", "x-wmf", "wmf", "x-emf", "emf", }; if (strncmp (mime_type, "image/", 6) != 0) return NULL; suffix = mime_type + 6; for (i = 0; i < G_N_ELEMENTS (exceptions); i +=2) if (strcmp (suffix, exceptions[i]) == 0) return g_strdup (exceptions[i+1]); return g_strdup (suffix); } /** * go_image_format_to_mime: * @format: a file extension string * * returns: corresponding mime type. **/ char * go_image_format_to_mime (char const *format) { char *ret = NULL; guint i; #ifdef GOFFICE_WITH_GTK GSList *ptr, *pixbuf_fmts; GdkPixbufFormat *pfmt; gchar *name; int cmp; gchar **mimes; #endif const char* formats[] = { "svg", "image/svg,image/svg+xml", "wmf", "x-wmf", "emf", "x-emf", }; if (format == NULL) return NULL; for (i = 0; i < G_N_ELEMENTS (formats); i +=2) if (strcmp (format, formats[i]) == 0) return g_strdup (formats[i+1]); #ifdef GOFFICE_WITH_GTK /* Not a format we have special knowledge about - ask gdk-pixbuf */ pixbuf_fmts = gdk_pixbuf_get_formats (); for (ptr = pixbuf_fmts; ptr != NULL; ptr = ptr->next) { pfmt = (GdkPixbufFormat *)ptr->data; name = gdk_pixbuf_format_get_name (pfmt); cmp = strcmp (format, name); g_free (name); if (cmp == 0) { mimes = gdk_pixbuf_format_get_mime_types (pfmt); ret = g_strjoinv (",", mimes); g_strfreev (mimes); break; } } g_slist_free (pixbuf_fmts); #endif return ret; } static GOImageFormatInfo const image_format_infos[GO_IMAGE_FORMAT_UNKNOWN] = { {GO_IMAGE_FORMAT_SVG, (char *) "svg", (char *) N_("SVG (vector graphics)"), (char *) "svg", FALSE, FALSE, TRUE}, {GO_IMAGE_FORMAT_PNG, (char *) "png", (char *) N_("PNG (raster graphics)"), (char *) "png", TRUE, TRUE, TRUE}, {GO_IMAGE_FORMAT_JPG, (char *) "jpeg", (char *) N_("JPEG (photograph)"), (char *) "jpg", TRUE, TRUE, FALSE}, {GO_IMAGE_FORMAT_PDF, (char *) "pdf", (char *) N_("PDF (portable document format)"), (char *) "pdf", FALSE, FALSE, TRUE}, {GO_IMAGE_FORMAT_PS, (char *) "ps", (char *) N_("PS (postscript)"), (char *) "ps", FALSE, TRUE, TRUE}, {GO_IMAGE_FORMAT_EMF, (char *) "emf", (char *) N_("EMF (extended metafile)"), (char *) "emf", FALSE, FALSE, TRUE}, {GO_IMAGE_FORMAT_WMF, (char *) "wmf", (char *) N_("WMF (windows metafile)"), (char *) "wmf", FALSE, FALSE, TRUE} }; static GOImageFormatInfo *pixbuf_image_format_infos = NULL; static unsigned pixbuf_format_nbr = 0; static gboolean pixbuf_format_done = FALSE; #define PIXBUF_IMAGE_FORMAT_OFFSET (1+GO_IMAGE_FORMAT_UNKNOWN) static void go_image_build_pixbuf_format_infos (void) { #ifdef GOFFICE_WITH_GTK GdkPixbufFormat *fmt; GSList *l, *pixbuf_fmts; GOImageFormatInfo *format_info; gchar **exts; unsigned i; if (pixbuf_format_done) return; pixbuf_fmts = gdk_pixbuf_get_formats (); pixbuf_format_nbr = g_slist_length (pixbuf_fmts); if (pixbuf_format_nbr > 0) { pixbuf_image_format_infos = g_new (GOImageFormatInfo, pixbuf_format_nbr); for (l = pixbuf_fmts, i = 1, format_info = pixbuf_image_format_infos; l != NULL; l = l->next, i++, format_info++) { fmt = (GdkPixbufFormat *)l->data; format_info->format = GO_IMAGE_FORMAT_UNKNOWN + i; format_info->name = gdk_pixbuf_format_get_name (fmt); format_info->desc = gdk_pixbuf_format_get_description (fmt); exts = gdk_pixbuf_format_get_extensions (fmt); format_info->ext = g_strdup (exts[0]); if (format_info->ext == NULL) format_info->ext = format_info->name; g_strfreev (exts); format_info->has_pixbuf_saver = gdk_pixbuf_format_is_writable (fmt); format_info->is_dpi_useful = FALSE; format_info->alpha_support = FALSE; } } g_slist_free (pixbuf_fmts); #endif /* GOFFICE_WITH_GTK */ pixbuf_format_done = TRUE; } /** * go_image_get_format_info: * @format: a #GOImageFormat * * Retrieves infromation associated to @format. * * returns: a #GOImageFormatInfo struct. **/ GOImageFormatInfo const * go_image_get_format_info (GOImageFormat format) { if (format > GO_IMAGE_FORMAT_UNKNOWN) go_image_build_pixbuf_format_infos (); g_return_val_if_fail (format >= 0 && format != GO_IMAGE_FORMAT_UNKNOWN && format <= GO_IMAGE_FORMAT_UNKNOWN + pixbuf_format_nbr, NULL); if (format < GO_IMAGE_FORMAT_UNKNOWN) return &image_format_infos[format]; return &pixbuf_image_format_infos[format - PIXBUF_IMAGE_FORMAT_OFFSET]; } /** * go_image_get_format_from_name: * @name: a string * * returns: corresponding #GOImageFormat. **/ GOImageFormat go_image_get_format_from_name (char const *name) { unsigned i; go_image_build_pixbuf_format_infos (); for (i = 0; i < GO_IMAGE_FORMAT_UNKNOWN; i++) { if (strcmp (name, image_format_infos[i].name) == 0) return image_format_infos[i].format; } for (i = 0; i < pixbuf_format_nbr; i++) { if (strcmp (name, pixbuf_image_format_infos[i].name) == 0) return pixbuf_image_format_infos[i].format; } g_warning ("[GOImage::get_format_from_name] Unknown format name (%s)", name); return GO_IMAGE_FORMAT_UNKNOWN; } /** * go_image_get_formats_with_pixbuf_saver: * * returns: a list of #GOImageFormat that can be created from a pixbuf. **/ GSList * go_image_get_formats_with_pixbuf_saver (void) { GSList *list = NULL; unsigned i; for (i = 0; i < GO_IMAGE_FORMAT_UNKNOWN; i++) if (image_format_infos[i].has_pixbuf_saver) list = g_slist_prepend (list, GUINT_TO_POINTER (i)); /* TODO: before enabling this code, we must remove duplicate in pixbuf_image_format_infos */ #if 0 go_image_build_pixbuf_format_infos (); for (i = 0; i < pixbuf_format_nbr; i++) { if (pixbuf_image_format_infos[i].has_pixbuf_saver) list = g_slist_prepend (list, GUINT_TO_POINTER (i + PIXBUF_IMAGE_FORMAT_OFFSET)); } #endif return list; } /********************************* * GOImage object implementation * *********************************/ static GObjectClass *parent_klass; struct _GOImage { GObject parent; guint8 *data; guint width, height, rowstride; gboolean target_cairo; #ifdef GOFFICE_WITH_CAIRO cairo_t *cairo; #endif #ifdef GOFFICE_WITH_GTK GdkPixbuf *pixbuf; #endif }; enum { IMAGE_PROP_0, IMAGE_PROP_WIDTH, IMAGE_PROP_HEIGHT, #ifdef GOFFICE_WITH_GTK IMAGE_PROP_PIXBUF, #endif }; #ifdef GOFFICE_WITH_CAIRO static void pixbuf_to_cairo (GOImage *image) { guint i,j, rowstride; guint t; unsigned char *src, *dst; g_return_if_fail (IS_GO_IMAGE (image) && image->data && image->pixbuf); #define MULT(d,c,a,t) G_STMT_START { t = c * a + 0x7f; d = ((t >> 8) + t) >> 8; } G_STMT_END src = gdk_pixbuf_get_pixels (image->pixbuf); dst = image->data; rowstride = gdk_pixbuf_get_rowstride (image->pixbuf); for (i = 0; i < image->height; i++) { for (j = 0; j < image->width; j++) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN MULT(dst[0], src[2], src[3], t); MULT(dst[1], src[1], src[3], t); MULT(dst[2], src[0], src[3], t); dst[3] = src[3]; #else MULT(dst[3], src[2], src[3], t); MULT(dst[2], src[1], src[3], t); MULT(dst[1], src[0], src[3], t); dst[0] = src[3]; #endif src += 4; dst += 4; } dst += image->rowstride - image->width * 4; src += rowstride - image->width * 4; } #undef MULT } #endif #ifdef GOFFICE_WITH_GTK static void cairo_to_pixbuf (GOImage *image) { guint i,j, rowstride; unsigned char *src, *dst; guint t; g_return_if_fail (IS_GO_IMAGE (image) && image->data && image->pixbuf); #define MULT(d,c,a,t) G_STMT_START { t = (a)? c * 255 / a: 0; d = t;} G_STMT_END dst = gdk_pixbuf_get_pixels (image->pixbuf); rowstride = gdk_pixbuf_get_rowstride (image->pixbuf); src = image->data; for (i = 0; i < image->height; i++) { for (j = 0; j < image->width; j++) { #if G_BYTE_ORDER == G_LITTLE_ENDIAN MULT(dst[0], src[2], src[3], t); MULT(dst[1], src[1], src[3], t); MULT(dst[2], src[0], src[3], t); dst[3] = src[3]; #else MULT(dst[3], src[2], src[3], t); MULT(dst[2], src[1], src[3], t); MULT(dst[1], src[0], src[3], t); dst[0] = src[3]; #endif src += 4; dst += 4; } dst += rowstride - image->width * 4; src += image->rowstride - image->width * 4; } #undef MULT } #endif static void go_image_set_property (GObject *obj, guint param_id, GValue const *value, GParamSpec *pspec) { GOImage *image = GO_IMAGE (obj); gboolean size_changed = FALSE; guint n; switch (param_id) { case IMAGE_PROP_WIDTH: n = g_value_get_uint (value); if (n != image->width) { image->width = n; size_changed = TRUE; } break; case IMAGE_PROP_HEIGHT: n = g_value_get_uint (value); if (n != image->height) { image->height = n; size_changed = TRUE; } break; #ifdef GOFFICE_WITH_GTK case IMAGE_PROP_PIXBUF: { GdkPixbuf *pixbuf = GDK_PIXBUF (g_value_get_object (value)); if (!GDK_IS_PIXBUF (pixbuf)) break; if (!gdk_pixbuf_get_has_alpha (pixbuf)) pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0); else g_object_ref (pixbuf); if (image->pixbuf) g_object_unref (image->pixbuf); image->pixbuf = pixbuf; if (image->data != NULL) { g_free (image->data); image->data = NULL; } image->width = gdk_pixbuf_get_width (pixbuf); image->height = gdk_pixbuf_get_height (pixbuf); image->rowstride = gdk_pixbuf_get_rowstride (pixbuf); image->target_cairo = FALSE; } break; #endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec); return; /* NOTE : RETURN */ } if (size_changed) { if (image->pixbuf) { g_object_unref (image->pixbuf); image->pixbuf = NULL; } if (image->data != NULL) g_free (image->data); /* GOImage only supports pixbuf with alpha values at the moment */ image->rowstride = image->width * 4; image->data = g_new0 (guint8, image->height * image->rowstride); image->target_cairo = TRUE; } } static void go_image_get_property (GObject *obj, guint param_id, GValue *value, GParamSpec *pspec) { GOImage *image = GO_IMAGE (obj); switch (param_id) { case IMAGE_PROP_WIDTH: g_value_set_uint (value, image->width); break; case IMAGE_PROP_HEIGHT: g_value_set_uint (value, image->height); break; #ifdef GOFFICE_WITH_GTK case IMAGE_PROP_PIXBUF: if (image->target_cairo && image->pixbuf) { cairo_to_pixbuf (image); image->target_cairo = FALSE; } g_value_set_object (value, image->pixbuf); break; #endif default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec); return; /* NOTE : RETURN */ } } static void go_image_finalize (GObject *obj) { GOImage *image = GO_IMAGE (obj); if (image->data != NULL) g_free (image->data); #ifdef GOFFICE_WITH_GTK if (image->pixbuf) g_object_unref (image->pixbuf); #endif (parent_klass->finalize) (obj); } typedef GObjectClass GOImageClass; static void go_image_class_init (GOImageClass *klass) { klass->finalize = go_image_finalize; klass->set_property = go_image_set_property; klass->get_property = go_image_get_property; parent_klass = g_type_class_peek_parent (klass); g_object_class_install_property (klass, IMAGE_PROP_WIDTH, g_param_spec_uint ("width", _("Width"), _("Image width in pixels"), 0, G_MAXUINT16, 0, G_PARAM_READWRITE)); g_object_class_install_property (klass, IMAGE_PROP_HEIGHT, g_param_spec_uint ("height", _("Height"), _("Image height in pixels"), 0, G_MAXUINT16, 0, G_PARAM_READWRITE)); #ifdef GOFFICE_WITH_GTK g_object_class_install_property (klass, IMAGE_PROP_PIXBUF, g_param_spec_object ("pixbuf", _("Pixbuf"), _("GdkPixbuf object from which the GOImage is built"), GDK_TYPE_PIXBUF, G_PARAM_READWRITE)); #endif } GSF_CLASS (GOImage, go_image, go_image_class_init, NULL, G_TYPE_OBJECT) #ifdef GOFFICE_WITH_CAIRO cairo_t * go_image_get_cairo (GOImage *image) { cairo_surface_t *surface ; cairo_t *cairo; g_return_val_if_fail (IS_GO_IMAGE (image), NULL); if (image->data == NULL && image->pixbuf == NULL) return NULL; if (image->data == NULL) { /* image built from a pixbuf */ image->data = g_new0 (guint8, image->height * image->rowstride); } if (!image->target_cairo) { pixbuf_to_cairo (image); image->target_cairo = TRUE; } surface = cairo_image_surface_create_for_data ( image->data, CAIRO_FORMAT_ARGB32, image->width, image->height, image->rowstride); cairo = cairo_create (surface); cairo_surface_destroy (surface); image->target_cairo = TRUE; return cairo; } cairo_pattern_t *go_image_create_cairo_pattern (GOImage *image) { cairo_surface_t *surface ; cairo_pattern_t *pat; g_return_val_if_fail (IS_GO_IMAGE (image), NULL); if (image->data == NULL && image->pixbuf == NULL) return NULL; if (image->data == NULL) { /* image built from a pixbuf */ image->data = g_new0 (guint8, image->height * image->rowstride); } if (!image->target_cairo) { pixbuf_to_cairo (image); image->target_cairo = TRUE; } surface = cairo_image_surface_create_for_data ( image->data, CAIRO_FORMAT_ARGB32, image->width, image->height, image->rowstride); pat = cairo_pattern_create_for_surface (surface); cairo_surface_destroy (surface); return pat; } #endif #ifdef GOFFICE_WITH_GTK GOImage * go_image_new_from_pixbuf (GdkPixbuf *pixbuf) { return g_object_new (GO_IMAGE_TYPE, "pixbuf", pixbuf, NULL); } GdkPixbuf * go_image_get_pixbuf (GOImage *image) { g_return_val_if_fail (image != NULL, NULL); if (!image->pixbuf) { if (image->width == 0 || image->height == 0 || image->data == NULL) return NULL; image->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, image->width, image->height); } if (image->target_cairo) { cairo_to_pixbuf (image); image->target_cairo = FALSE; } return image->pixbuf; } #endif GOImage * go_image_new_from_file (const char *filename, GError **error) { #ifdef GOFFICE_WITH_GTK GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, error); GOImage *image = g_object_new (GO_IMAGE_TYPE, "pixbuf", pixbuf, NULL); g_object_unref (pixbuf); image->target_cairo = FALSE; return image; #else g_warning ("go_image_new_from_file not implemented!"); return NULL; #endif } guint8 * go_image_get_pixels (GOImage *image) { g_return_val_if_fail (image, NULL); return image->data; } int go_image_get_rowstride (GOImage *image) { g_return_val_if_fail (image, 0); return image->rowstride; } void go_image_fill (GOImage *image, GOColor color) { guint32 val; guint8 *dst; unsigned i, j; g_return_if_fail (image); dst = image->data; if (image->target_cairo) val = (UINT_RGBA_R (color) << 8) + (UINT_RGBA_G (color) << 16) + (UINT_RGBA_B (color) << 24) + UINT_RGBA_A (color); else val = color; for (i = 0; i < image->height; i++) { for (j = 0; j < image->width; j++) *((guint32*) dst) = val; dst += image->rowstride - image->width * 4; } }