Merge branch 'bug/26304' into staging
[WebKit-https.git] / WebCore / platform / graphics / gtk / ImageGtk.cpp
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
35 namespace WTF {
36
37 template <> void freeOwnedGPtr<GtkIconInfo>(GtkIconInfo* info)
38 {
39     if (info)
40         gtk_icon_info_free(info);
41 }
42
43 }
44
45 namespace WebCore {
46
47 static 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     if (!iconInfo)
52         iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
53                                               GTK_STOCK_MISSING_IMAGE, size,
54                                               GTK_ICON_LOOKUP_NO_SVG);
55
56     GOwnPtr<GtkIconInfo> info(iconInfo);
57     return CString(gtk_icon_info_get_filename(info.get()));
58 }
59
60 static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(CString name)
61 {
62     GOwnPtr<gchar> content;
63     gsize length;
64     if (!g_file_get_contents(name.data(), &content.outPtr(), &length, 0))
65         return SharedBuffer::create();
66
67     return SharedBuffer::create(content.get(), length);
68 }
69
70 void BitmapImage::initPlatformData()
71 {
72 }
73
74 void BitmapImage::invalidatePlatformData()
75 {
76 }
77
78 PassRefPtr<Image> loadImageFromFile(CString fileName)
79 {
80     RefPtr<BitmapImage> img = BitmapImage::create();
81     RefPtr<SharedBuffer> buffer = loadResourceSharedBuffer(fileName);
82     img->setData(buffer.release(), true);
83     return img.release();
84 }
85
86 PassRefPtr<Image> Image::loadPlatformResource(const char* name)
87 {
88     CString fileName;
89     if (!strcmp("missingImage", name))
90         fileName = getThemeIconFileName(GTK_STOCK_MISSING_IMAGE, 16);
91     if (fileName.isNull())
92         fileName = String::format("%s/webkit-1.0/images/%s.png", DATA_DIR, name).utf8();
93
94     return loadImageFromFile(fileName);
95 }
96
97 PassRefPtr<Image> Image::loadPlatformThemeIcon(const char* name, int size)
98 {
99     return loadImageFromFile(getThemeIconFileName(name, size));
100 }
101
102 static inline unsigned char* getCairoSurfacePixel(unsigned char* data, uint x, uint y, uint rowStride)
103 {
104     return data + (y * rowStride) + x * 4;
105 }
106
107 static inline guchar* getGdkPixbufPixel(guchar* data, uint x, uint y, uint rowStride)
108 {
109     return data + (y * rowStride) + x * 4;
110 }
111
112 GdkPixbuf* BitmapImage::getGdkPixbuf()
113 {
114     int width = cairo_image_surface_get_width(frameAtIndex(currentFrame()));
115     int height = cairo_image_surface_get_height(frameAtIndex(currentFrame()));
116     unsigned char* surfaceData = cairo_image_surface_get_data(frameAtIndex(currentFrame()));
117     int surfaceRowStride = cairo_image_surface_get_stride(frameAtIndex(currentFrame()));
118
119     GdkPixbuf* dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height);
120     if (!dest)
121         return 0;
122
123     guchar* pixbufData = gdk_pixbuf_get_pixels(dest);
124     int pixbufRowStride = gdk_pixbuf_get_rowstride(dest);
125
126     /* From: http://cairographics.org/manual/cairo-image-surface.html#cairo-format-t
127      * "CAIRO_FORMAT_ARGB32: each pixel is a 32-bit quantity, with alpha in
128      * the upper 8 bits, then red, then green, then blue. The 32-bit
129      * quantities are stored native-endian. Pre-multiplied alpha is used.
130      * (That is, 50% transparent red is 0x80800000, not 0x80ff0000.)"
131      *
132      * See http://developer.gimp.org/api/2.0/gdk-pixbuf/gdk-pixbuf-gdk-pixbuf.html#GdkPixbuf
133      * for information on the structure of GdkPixbufs stored with GDK_COLORSPACE_RGB.
134      *
135      * RGB color channels in CAIRO_FORMAT_ARGB32 are stored based on the
136      * endianness of the machine and are also multiplied by the alpha channel.
137      * To properly transfer the data from the Cairo surface we must divide each
138      * of the RGB channels by the alpha channel and then reorder all channels
139      * if this machine is little-endian.
140      */
141     for (int y = 0; y < height; y++) {
142         for (int x = 0; x < width; x++) {
143             unsigned char* source = getCairoSurfacePixel(surfaceData, x, y, surfaceRowStride);
144             guchar* dest = getGdkPixbufPixel(pixbufData, x, y, pixbufRowStride);
145
146 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
147             guchar alpha = source[3];
148             dest[0] = alpha ? ((source[2] * 255) / alpha) : 0;
149             dest[1] = alpha ? ((source[1] * 255) / alpha) : 0;
150             dest[2] = alpha ? ((source[0] * 255) / alpha) : 0;
151             dest[3] = alpha;
152 #else
153             guchar alpha = source[0];
154             dest[0] = alpha ? ((source[1] * 255) / alpha) : 0;
155             dest[1] = alpha ? ((source[2] * 255) / alpha) : 0;
156             dest[2] = alpha ? ((source[3] * 255) / alpha) : 0;
157             dest[3] = alpha;
158 #endif
159         }
160     }
161
162     return dest;
163 }
164
165 }