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; 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: 59 virtual void fired() { purgeScratchBuffer(); } 60 }; 61 static PurgeScratchBufferTimer purgeScratchBufferTimer; 62 static void scheduleScratchBufferPurge() 63 { 64 if (purgeScratchBufferTimer.isActive()) 65 purgeScratchBufferTimer.stop(); 66 purgeScratchBufferTimer.startOneShot(2); 67 } 68 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 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 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 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 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 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 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 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 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 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 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