1/*
2 * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#include "BitmapImage.h"
29#include "CString.h"
30#include "GOwnPtr.h"
31
32#include <cairo.h>
33#include <gtk/gtk.h>
34
35namespace WTF {
36
37template <> void freeOwnedGPtr<GtkIconInfo>(GtkIconInfo* info)
38{
39    if (info)
40        gtk_icon_info_free(info);
41}
42
43}
44
45namespace WebCore {
46
47static CString getThemeIconFileName(const char* name, int size)
48{
49    GtkIconInfo* iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
50                                                       name, size, GTK_ICON_LOOKUP_NO_SVG);
51    // Try to fallback on MISSING_IMAGE.
52    if (!iconInfo)
53        iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
54                                              GTK_STOCK_MISSING_IMAGE, size,
55                                              GTK_ICON_LOOKUP_NO_SVG);
56    if (iconInfo) {
57        GOwnPtr<GtkIconInfo> info(iconInfo);
58        return CString(gtk_icon_info_get_filename(info.get()));
59    }
60
61    // No icon was found, this can happen if not GTK theme is set. In
62    // that case an empty Image will be created.
63    return CString();
64}
65
66static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(CString name)
67{
68    GOwnPtr<gchar> content;
69    gsize length;
70    if (!g_file_get_contents(name.data(), &content.outPtr(), &length, 0))
71        return SharedBuffer::create();
72
73    return SharedBuffer::create(content.get(), length);
74}
75
76void BitmapImage::initPlatformData()
77{
78}
79
80void BitmapImage::invalidatePlatformData()
81{
82}
83
84PassRefPtr<Image> loadImageFromFile(CString fileName)
85{
86    RefPtr<BitmapImage> img = BitmapImage::create();
87    if (!fileName.isNull()) {
88        RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(fileName);
89        img->setData(buffer.release(), true);
90    }
91    return img.release();
92}
93
94PassRefPtr<Image> Image::loadPlatformResource(const char* name)
95{
96    CString fileName;
97    if (!strcmp("missingImage", name))
98        fileName = getThemeIconFileName(GTK_STOCK_MISSING_IMAGE, 16);
99    if (fileName.isNull())
100        fileName = String::format("%s/webkit-1.0/images/%s.png", DATA_DIR, name).utf8();
101
102    return loadImageFromFile(fileName);
103}
104
105PassRefPtr<Image> Image::loadPlatformThemeIcon(const char* name, int size)
106{
107    return loadImageFromFile(getThemeIconFileName(name, size));
108}
109
110static inline unsigned char* getCairoSurfacePixel(unsigned char* data, uint x, uint y, uint rowStride)
111{
112    return data + (y * rowStride) + x * 4;
113}
114
115static inline guchar* getGdkPixbufPixel(guchar* data, uint x, uint y, uint rowStride)
116{
117    return data + (y * rowStride) + x * 4;
118}
119
120GdkPixbuf* BitmapImage::getGdkPixbuf()
121{
122    int width = cairo_image_surface_get_width(frameAtIndex(currentFrame()));
123    int height = cairo_image_surface_get_height(frameAtIndex(currentFrame()));
124    unsigned char* surfaceData = cairo_image_surface_get_data(frameAtIndex(currentFrame()));
125    int surfaceRowStride = cairo_image_surface_get_stride(frameAtIndex(currentFrame()));
126
127    GdkPixbuf* dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
128    if (!dest)
129        return 0;
130
131    guchar* pixbufData = gdk_pixbuf_get_pixels(dest);
132    int pixbufRowStride = gdk_pixbuf_get_rowstride(dest);
133
134    /* From: http://cairographics.org/manual/cairo-image-surface.html#cairo-format-t
135     * "CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with alpha in
136     * the upper 8 bits, then red, then green, then blue. The 32-bit
137     * quantities are stored native-endian. Pre-multiplied alpha is used.
138     * (That is, 50% transparent red is 0x80800000, not 0x80ff0000.)"
139     *
140     * See http://developer.gimp.org/api/2.0/gdk-pixbuf/gdk-pixbuf-gdk-pixbuf.html#GdkPixbuf
141     * for information on the structure of GdkPixbufs stored with GDK_COLORSPACE_RGB.
142     *
143     * RGB color channels in CAIRO_FORMAT_ARGB32 are stored based on the
144     * endianness of the machine and are also multiplied by the alpha channel.
145     * To properly transfer the data from the Cairo surface we must divide each
146     * of the RGB channels by the alpha channel and then reorder all channels
147     * if this machine is little-endian.
148     */
149    for (int y = 0; y < height; y++) {
150        for (int x = 0; x < width; x++) {
151            unsigned char* source = getCairoSurfacePixel(surfaceData, x, y, surfaceRowStride);
152            guchar* dest = getGdkPixbufPixel(pixbufData, x, y, pixbufRowStride);
153
154#if G_BYTE_ORDER == G_LITTLE_ENDIAN
155            guchar alpha = source[3];
156            dest[0] = alpha ? ((source[2] * 255) / alpha) : 0;
157            dest[1] = alpha ? ((source[1] * 255) / alpha) : 0;
158            dest[2] = alpha ? ((source[0] * 255) / alpha) : 0;
159            dest[3] = alpha;
160#else
161            guchar alpha = source[0];
162            dest[0] = alpha ? ((source[1] * 255) / alpha) : 0;
163            dest[1] = alpha ? ((source[2] * 255) / alpha) : 0;
164            dest[2] = alpha ? ((source[3] * 255) / alpha) : 0;
165            dest[3] = alpha;
166#endif
167        }
168    }
169
170    return dest;
171}
172
173}
174