initial import
[vuplus_webkit] / Source / WebCore / platform / gtk / WidgetRenderingContext.cpp
1 /*
2  * Copyright (C) 2010 Sencha, Inc.
3  * Copyright (C) 2010 Igalia S.L.
4  *
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #ifdef GTK_API_VERSION_2
30
31 #include "config.h"
32 #include "WidgetRenderingContext.h"
33
34 #include "GraphicsContext.h"
35 #include "GtkVersioning.h"
36 #include "PlatformContextCairo.h"
37 #include "RefPtrCairo.h"
38 #include "RenderThemeGtk.h"
39 #include "Timer.h"
40 #include <gdk/gdk.h>
41 #include <gtk/gtk.h>
42 #include <wtf/StdLibExtras.h>
43
44 namespace WebCore {
45
46 static GdkPixmap* gScratchBuffer = 0;
47 static void purgeScratchBuffer()
48 {
49     if (gScratchBuffer)
50         g_object_unref(gScratchBuffer);
51     gScratchBuffer = 0;
52 }
53
54 // FIXME: Perhaps we can share some of this code with the ContextShadowCairo.
55 // Widget rendering needs a scratch image as the buffer for the intermediate
56 // render. Instead of creating and destroying the buffer for every operation,
57 // we create a buffer which will be automatically purged via a timer.
58 class PurgeScratchBufferTimer : public TimerBase {
59 private:
60     virtual void fired() { purgeScratchBuffer(); }
61 };
62
63 static void scheduleScratchBufferPurge()
64 {
65     DEFINE_STATIC_LOCAL(PurgeScratchBufferTimer, purgeScratchBufferTimer, ());
66     if (purgeScratchBufferTimer.isActive())
67         purgeScratchBufferTimer.stop();
68     purgeScratchBufferTimer.startOneShot(2);
69 }
70
71 WidgetRenderingContext::WidgetRenderingContext(GraphicsContext* graphicsContext, const IntRect& targetRect)
72     : m_graphicsContext(graphicsContext)
73     , m_targetRect(targetRect)
74     , m_hadError(false)
75 {
76     RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get());
77
78     // Fallback: We failed to create an RGBA colormap earlier, so we cannot properly paint 
79     // to a temporary surface and preserve transparency. To ensure decent widget rendering, just
80     // paint directly to the target drawable. This will not render CSS rotational transforms properly.
81     if (!theme->m_themePartsHaveRGBAColormap && graphicsContext->gdkWindow()) {
82         m_target = graphicsContext->gdkWindow();
83         return;
84     }
85
86     // We never want to create a scratch buffer larger than the size of our target GdkDrawable.
87     // This prevents giant pixmap allocations for very large widgets in smaller views.
88     // FIXME: We need to implement this check for WebKit2 as well.
89     if (graphicsContext->gdkWindow()) {
90         int maxWidth = 0, maxHeight = 0;
91         getGdkDrawableSize(graphicsContext->gdkWindow(), &maxWidth, &maxHeight);
92         m_targetRect.setSize(m_targetRect.size().shrunkTo(IntSize(maxWidth, maxHeight)));
93     }
94
95     // Widgets sometimes need to draw outside their boundaries for things such as
96     // exterior focus. We want to allocate a some extra pixels in our surface for this.
97     static int extraSpace = 15;
98     m_targetRect.inflate(extraSpace);
99
100     // This offset will map a point in the coordinate system of the widget to the coordinate system of the painting buffer.
101     m_paintOffset = targetRect.location() - m_targetRect.location();
102
103     int width = m_targetRect.width() + (extraSpace * 2);
104     int height = m_targetRect.height() + (extraSpace * 2);
105     int scratchWidth = 0;
106     int scratchHeight = 0;
107     if (gScratchBuffer)
108         gdk_pixmap_get_size(gScratchBuffer, &scratchWidth, &scratchHeight);
109
110     // We do not need to recreate the buffer if the current buffer is large enough.
111     if (!gScratchBuffer || scratchWidth < width || scratchHeight < height) {
112         purgeScratchBuffer();
113         // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
114         width = (1 + (width >> 5)) << 5;
115         height = (1 + (height >> 5)) << 5;
116
117         gScratchBuffer = gdk_pixmap_new(0, width, height, gdk_colormap_get_visual(theme->m_colormap)->depth);
118         gdk_drawable_set_colormap(gScratchBuffer, theme->m_colormap);
119     }
120     m_target = gScratchBuffer;
121
122     // Clear the scratch buffer.
123     RefPtr<cairo_t> scratchBufferContext = adoptRef(gdk_cairo_create(gScratchBuffer));
124     cairo_set_operator(scratchBufferContext.get(), CAIRO_OPERATOR_CLEAR);
125     cairo_paint(scratchBufferContext.get());
126 }
127
128 WidgetRenderingContext::~WidgetRenderingContext()
129 {
130     // We do not need to blit back to the target in the fallback case. See above.
131     RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get());
132     if (!theme->m_themePartsHaveRGBAColormap && m_graphicsContext->gdkWindow())
133         return;
134
135     // Don't paint the results back if there was an error.
136     if (m_hadError) {
137         scheduleScratchBufferPurge();
138         return;
139     }
140
141     // FIXME: It's unclear if it is necessary to preserve the current source here.
142     cairo_t* cairoContext = m_graphicsContext->platformContext()->cr();
143     RefPtr<cairo_pattern_t> previousSource(cairo_get_source(cairoContext));
144
145     gdk_cairo_set_source_pixmap(cairoContext, gScratchBuffer, m_targetRect.x(), m_targetRect.y());
146     cairo_rectangle(cairoContext, m_targetRect.x(), m_targetRect.y(), m_targetRect.width(), m_targetRect.height());
147     cairo_fill(cairoContext);
148     cairo_set_source(cairoContext, previousSource.get());
149     scheduleScratchBufferPurge();
150 }
151
152 void WidgetRenderingContext::calculateClipRect(const IntRect& rect, GdkRectangle* clipRect)
153 {
154     clipRect->x = m_paintOffset.width() + rect.x();
155     clipRect->y = m_paintOffset.height() + rect.y();
156     clipRect->width = m_targetRect.width();
157     clipRect->height = m_targetRect.height();
158 }
159
160 void WidgetRenderingContext::gtkPaintBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
161 {
162     GdkRectangle clipRect;
163     calculateClipRect(rect, &clipRect);
164
165     // Some widgets also need their allocation adjusted to account for extra space.
166     // Right now only scrollbar buttons have significant allocations.
167     GtkAllocation allocation;
168     gtk_widget_get_allocation(widget, &allocation);
169     allocation.x += clipRect.x;
170     allocation.y += clipRect.y;
171     gtk_widget_set_allocation(widget, &allocation);
172
173     gtk_paint_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &clipRect,
174                   widget, detail, clipRect.x, clipRect.y, rect.width(), rect.height());
175 }
176
177 void WidgetRenderingContext::gtkPaintFlatBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
178 {
179     GdkRectangle clipRect;
180     calculateClipRect(rect, &clipRect);
181     gtk_paint_flat_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &clipRect,
182                        widget, detail, clipRect.x, clipRect.y, rect.width(), rect.height());
183 }
184
185 void WidgetRenderingContext::gtkPaintFocus(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail)
186 {
187     GdkRectangle clipRect;
188     calculateClipRect(rect, &clipRect);
189     gtk_paint_focus(gtk_widget_get_style(widget), m_target, stateType, &clipRect, widget,
190                     detail, clipRect.x, clipRect.y, rect.width(), rect.height());
191 }
192
193 void WidgetRenderingContext::gtkPaintSlider(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail, GtkOrientation orientation)
194 {
195     GdkRectangle clipRect;
196     calculateClipRect(rect, &clipRect);
197     gtk_paint_slider(gtk_widget_get_style(widget), m_target, stateType, shadowType, &clipRect, widget,
198                      detail, clipRect.x, clipRect.y, rect.width(), rect.height(), orientation);
199 }
200
201 void WidgetRenderingContext::gtkPaintCheck(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
202 {
203     GdkRectangle clipRect;
204     calculateClipRect(rect, &clipRect);
205     gtk_paint_check(gtk_widget_get_style(widget), m_target, stateType, shadowType, &clipRect, widget,
206                     detail, clipRect.x, clipRect.y, rect.width(), rect.height());
207 }
208
209 void WidgetRenderingContext::gtkPaintOption(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
210 {
211     GdkRectangle clipRect;
212     calculateClipRect(rect, &clipRect);
213     gtk_paint_option(gtk_widget_get_style(widget), m_target, stateType, shadowType, &clipRect, widget,
214                      detail, clipRect.x, clipRect.y, rect.width(), rect.height());
215 }
216
217 void WidgetRenderingContext::gtkPaintShadow(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
218 {
219     GdkRectangle clipRect;
220     calculateClipRect(rect, &clipRect);
221     gtk_paint_shadow(gtk_widget_get_style(widget), m_target, stateType, shadowType, &clipRect, widget,
222                      detail, clipRect.x, clipRect.y, rect.width(), rect.height());
223 }
224
225 void WidgetRenderingContext::gtkPaintArrow(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, int arrowDirection, const gchar* detail)
226 {
227     GdkRectangle clipRect;
228     calculateClipRect(rect, &clipRect);
229     gtk_paint_arrow(gtk_widget_get_style(widget), m_target, stateType, shadowType, &clipRect, widget, detail,
230                     static_cast<GtkArrowType>(arrowDirection), TRUE, clipRect.x, clipRect.y, rect.width(), rect.height());
231 }
232
233 void WidgetRenderingContext::gtkPaintVLine(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail)
234 {
235     GdkRectangle clipRect;
236     calculateClipRect(rect, &clipRect);
237     gtk_paint_vline(gtk_widget_get_style(widget), m_target, stateType, &clipRect, widget, detail,
238                     clipRect.y, clipRect.y + rect.height(), clipRect.x);
239
240 }
241
242 }
243
244 #endif // GTK_API_VERSION_2