2 * Copyright (C) 2010 Sencha, Inc.
3 * Copyright (C) 2010 Igalia S.L.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
29 #ifdef GTK_API_VERSION_2
32 #include "WidgetRenderingContext.h"
34 #include "GraphicsContext.h"
35 #include "GtkVersioning.h"
36 #include "PlatformContextCairo.h"
37 #include "RefPtrCairo.h"
38 #include "RenderThemeGtk.h"
42 #include <wtf/StdLibExtras.h>
46 static GdkPixmap* gScratchBuffer = 0;
47 static void purgeScratchBuffer()
50 g_object_unref(gScratchBuffer);
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 {
60 virtual void fired() { purgeScratchBuffer(); }
63 static void scheduleScratchBufferPurge()
65 DEFINE_STATIC_LOCAL(PurgeScratchBufferTimer, purgeScratchBufferTimer, ());
66 if (purgeScratchBufferTimer.isActive())
67 purgeScratchBufferTimer.stop();
68 purgeScratchBufferTimer.startOneShot(2);
71 WidgetRenderingContext::WidgetRenderingContext(GraphicsContext* graphicsContext, const IntRect& targetRect)
72 : m_graphicsContext(graphicsContext)
73 , m_targetRect(targetRect)
76 RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get());
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();
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)));
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);
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();
103 int width = m_targetRect.width() + (extraSpace * 2);
104 int height = m_targetRect.height() + (extraSpace * 2);
105 int scratchWidth = 0;
106 int scratchHeight = 0;
108 gdk_pixmap_get_size(gScratchBuffer, &scratchWidth, &scratchHeight);
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;
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);
120 m_target = gScratchBuffer;
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());
128 WidgetRenderingContext::~WidgetRenderingContext()
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())
135 // Don't paint the results back if there was an error.
137 scheduleScratchBufferPurge();
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));
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();
152 void WidgetRenderingContext::calculateClipRect(const IntRect& rect, GdkRectangle* clipRect)
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();
160 void WidgetRenderingContext::gtkPaintBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
162 GdkRectangle clipRect;
163 calculateClipRect(rect, &clipRect);
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);
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());
177 void WidgetRenderingContext::gtkPaintFlatBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
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());
185 void WidgetRenderingContext::gtkPaintFocus(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail)
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());
193 void WidgetRenderingContext::gtkPaintSlider(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail, GtkOrientation orientation)
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);
201 void WidgetRenderingContext::gtkPaintCheck(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
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());
209 void WidgetRenderingContext::gtkPaintOption(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
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());
217 void WidgetRenderingContext::gtkPaintShadow(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
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());
225 void WidgetRenderingContext::gtkPaintArrow(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, int arrowDirection, const gchar* detail)
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());
233 void WidgetRenderingContext::gtkPaintVLine(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail)
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);
244 #endif // GTK_API_VERSION_2