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
43 namespace WebCore {
44
45 static GdkPixmap* gScratchBuffer = 0;
purgeScratchBuffer()46 static void purgeScratchBuffer()
47 {
48 if (gScratchBuffer)
49 g_object_unref(gScratchBuffer);
50 gScratchBuffer = 0;
51 }
52
53 // FIXME: Perhaps we can share some of this code with the ContextShadowCairo.
54 // Widget rendering needs a scratch image as the buffer for the intermediate
55 // render. Instead of creating and destroying the buffer for every operation,
56 // we create a buffer which will be automatically purged via a timer.
57 class PurgeScratchBufferTimer : public TimerBase {
58 private:
fired()59 virtual void fired() { purgeScratchBuffer(); }
60 };
61 static PurgeScratchBufferTimer purgeScratchBufferTimer;
scheduleScratchBufferPurge()62 static void scheduleScratchBufferPurge()
63 {
64 if (purgeScratchBufferTimer.isActive())
65 purgeScratchBufferTimer.stop();
66 purgeScratchBufferTimer.startOneShot(2);
67 }
68
WidgetRenderingContext(GraphicsContext * graphicsContext,const IntRect & targetRect)69 WidgetRenderingContext::WidgetRenderingContext(GraphicsContext* graphicsContext, const IntRect& targetRect)
70 : m_graphicsContext(graphicsContext)
71 , m_targetRect(targetRect)
72 , m_hadError(false)
73 {
74 RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get());
75
76 // Fallback: We failed to create an RGBA colormap earlier, so we cannot properly paint
77 // to a temporary surface and preserve transparency. To ensure decent widget rendering, just
78 // paint directly to the target drawable. This will not render CSS rotational transforms properly.
79 if (!theme->m_themePartsHaveRGBAColormap && graphicsContext->gdkWindow()) {
80 m_paintRect = graphicsContext->getCTM().mapRect(targetRect);
81 m_target = graphicsContext->gdkWindow();
82 return;
83 }
84
85 // Widgets sometimes need to draw outside their boundaries for things such as
86 // exterior focus. We want to allocate a some extra pixels in our surface for this.
87 m_extraSpace = IntSize(15, 15);
88
89 // Offset the target rectangle so that the extra space is within the boundaries of the scratch buffer.
90 m_paintRect = IntRect(IntPoint(m_extraSpace.width(), m_extraSpace.height()),
91 m_targetRect.size());
92
93 int width = m_targetRect.width() + (m_extraSpace.width() * 2);
94 int height = m_targetRect.height() + (m_extraSpace.height() * 2);
95 int scratchWidth = 0;
96 int scratchHeight = 0;
97 if (gScratchBuffer)
98 gdk_pixmap_get_size(gScratchBuffer, &scratchWidth, &scratchHeight);
99
100 // We do not need to recreate the buffer if the current buffer is large enough.
101 if (!gScratchBuffer || scratchWidth < width || scratchHeight < height) {
102 purgeScratchBuffer();
103 // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests.
104 width = (1 + (width >> 5)) << 5;
105 height = (1 + (height >> 5)) << 5;
106
107 gScratchBuffer = gdk_pixmap_new(0, width, height, gdk_colormap_get_visual(theme->m_colormap)->depth);
108 gdk_drawable_set_colormap(gScratchBuffer, theme->m_colormap);
109 }
110 m_target = gScratchBuffer;
111
112 // Clear the scratch buffer.
113 RefPtr<cairo_t> scratchBufferContext = adoptRef(gdk_cairo_create(gScratchBuffer));
114 cairo_set_operator(scratchBufferContext.get(), CAIRO_OPERATOR_CLEAR);
115 cairo_paint(scratchBufferContext.get());
116 }
117
~WidgetRenderingContext()118 WidgetRenderingContext::~WidgetRenderingContext()
119 {
120 // We do not need to blit back to the target in the fallback case. See above.
121 RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get());
122 if (!theme->m_themePartsHaveRGBAColormap && m_graphicsContext->gdkWindow())
123 return;
124
125 // Don't paint the results back if there was an error.
126 if (m_hadError) {
127 scheduleScratchBufferPurge();
128 return;
129 }
130
131 // FIXME: It's unclear if it is necessary to preserve the current source here.
132 cairo_t* cairoContext = m_graphicsContext->platformContext()->cr();
133 RefPtr<cairo_pattern_t> previousSource(cairo_get_source(cairoContext));
134
135 // The blit rectangle is the original target rectangle adjusted for any extra space.
136 IntRect fullTargetRect(m_targetRect);
137 fullTargetRect.inflateX(m_extraSpace.width());
138 fullTargetRect.inflateY(m_extraSpace.height());
139
140 gdk_cairo_set_source_pixmap(cairoContext, gScratchBuffer, fullTargetRect.x(), fullTargetRect.y());
141 cairo_rectangle(cairoContext, fullTargetRect.x(), fullTargetRect.y(), fullTargetRect.width(), fullTargetRect.height());
142 cairo_fill(cairoContext);
143 cairo_set_source(cairoContext, previousSource.get());
144 scheduleScratchBufferPurge();
145 }
146
gtkPaintBox(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,GtkShadowType shadowType,const gchar * detail)147 void WidgetRenderingContext::gtkPaintBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
148 {
149 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
150
151 // Some widgets also need their allocation adjusted to account for extra space.
152 // Right now only scrollbar buttons have significant allocations.
153 GtkAllocation allocation;
154 gtk_widget_get_allocation(widget, &allocation);
155 allocation.x += m_paintRect.x;
156 allocation.y += m_paintRect.y;
157 gtk_widget_set_allocation(widget, &allocation);
158
159 gtk_paint_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &paintRect,
160 widget, detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height);
161 }
162
gtkPaintFlatBox(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,GtkShadowType shadowType,const gchar * detail)163 void WidgetRenderingContext::gtkPaintFlatBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
164 {
165 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
166 gtk_paint_flat_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &paintRect,
167 widget, detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height);
168 }
169
gtkPaintFocus(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,const gchar * detail)170 void WidgetRenderingContext::gtkPaintFocus(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail)
171 {
172 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
173 gtk_paint_focus(gtk_widget_get_style(widget), m_target, stateType, &paintRect, widget,
174 detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height);
175 }
176
gtkPaintSlider(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,GtkShadowType shadowType,const gchar * detail,GtkOrientation orientation)177 void WidgetRenderingContext::gtkPaintSlider(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail, GtkOrientation orientation)
178 {
179 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
180 gtk_paint_slider(gtk_widget_get_style(widget), m_target, stateType, shadowType, &m_paintRect, widget,
181 detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height, orientation);
182 }
183
gtkPaintCheck(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,GtkShadowType shadowType,const gchar * detail)184 void WidgetRenderingContext::gtkPaintCheck(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
185 {
186 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
187 gtk_paint_check(gtk_widget_get_style(widget), m_target, stateType, shadowType, &paintRect, widget,
188 detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height);
189 }
190
gtkPaintOption(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,GtkShadowType shadowType,const gchar * detail)191 void WidgetRenderingContext::gtkPaintOption(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
192 {
193 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
194 gtk_paint_option(gtk_widget_get_style(widget), m_target, stateType, shadowType, &paintRect, widget,
195 detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height);
196 }
197
gtkPaintShadow(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,GtkShadowType shadowType,const gchar * detail)198 void WidgetRenderingContext::gtkPaintShadow(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail)
199 {
200 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
201 gtk_paint_shadow(gtk_widget_get_style(widget), m_target, stateType, shadowType, &paintRect, widget,
202 detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height);
203 }
204
gtkPaintArrow(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,GtkShadowType shadowType,int arrowDirection,const gchar * detail)205 void WidgetRenderingContext::gtkPaintArrow(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, int arrowDirection, const gchar* detail)
206 {
207 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
208 gtk_paint_arrow(gtk_widget_get_style(widget), m_target, stateType, shadowType, &paintRect, widget, detail,
209 static_cast<GtkArrowType>(arrowDirection), TRUE, paintRect.x, paintRect.y, paintRect.width, paintRect.height);
210 }
211
gtkPaintVLine(const IntRect & rect,GtkWidget * widget,GtkStateType stateType,const gchar * detail)212 void WidgetRenderingContext::gtkPaintVLine(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail)
213 {
214 GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() };
215 gtk_paint_vline(gtk_widget_get_style(widget), m_target, stateType, &paintRect, widget, detail,
216 paintRect.y, paintRect.y + paintRect.height, paintRect.x);
217
218 }
219
220 }
221
222 #endif // GTK_API_VERSION_2
223