Home | History | Annotate | Download | only in gtk
      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