diff options
Diffstat (limited to 'Source/WebCore/platform/gtk/WidgetRenderingContext.cpp')
-rw-r--r-- | Source/WebCore/platform/gtk/WidgetRenderingContext.cpp | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/Source/WebCore/platform/gtk/WidgetRenderingContext.cpp b/Source/WebCore/platform/gtk/WidgetRenderingContext.cpp new file mode 100644 index 0000000..6d1be31 --- /dev/null +++ b/Source/WebCore/platform/gtk/WidgetRenderingContext.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2010 Sencha, Inc. + * Copyright (C) 2010 Igalia S.L. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef GTK_API_VERSION_2 + +#include "config.h" +#include "WidgetRenderingContext.h" + +#include "GraphicsContext.h" +#include "GtkVersioning.h" +#include "RefPtrCairo.h" +#include "RenderThemeGtk.h" +#include "Timer.h" +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +namespace WebCore { + +static GdkPixmap* gScratchBuffer = 0; +static void purgeScratchBuffer() +{ + if (gScratchBuffer) + g_object_unref(gScratchBuffer); + gScratchBuffer = 0; +} + +// FIXME: Perhaps we can share some of this code with the ContextShadowCairo. +// Widget rendering needs a scratch image as the buffer for the intermediate +// render. Instead of creating and destroying the buffer for every operation, +// we create a buffer which will be automatically purged via a timer. +class PurgeScratchBufferTimer : public TimerBase { +private: + virtual void fired() { purgeScratchBuffer(); } +}; +static PurgeScratchBufferTimer purgeScratchBufferTimer; +static void scheduleScratchBufferPurge() +{ + if (purgeScratchBufferTimer.isActive()) + purgeScratchBufferTimer.stop(); + purgeScratchBufferTimer.startOneShot(2); +} + +static IntSize getExtraSpaceForWidget(RenderThemeGtk* theme) +{ + // Checkboxes and scrollbar thumbs often draw outside their boundaries. Here we figure out + // the maximum amount of space we need for any type of widget and use that to increase the + // size of the scratch buffer, while preserving the final widget position. + + // The checkbox extra space is calculated by looking at the widget style. + int indicatorSize, indicatorSpacing; + theme->getIndicatorMetrics(CheckboxPart, indicatorSize, indicatorSpacing); + IntSize extraSpace(indicatorSpacing, indicatorSpacing); + + // Scrollbar thumbs need at least an extra pixel along their movement axis. + return extraSpace.expandedTo(IntSize(1, 1)); +} + +WidgetRenderingContext::WidgetRenderingContext(GraphicsContext* graphicsContext, const IntRect& targetRect) + : m_graphicsContext(graphicsContext) + , m_targetRect(targetRect) + , m_hadError(false) +{ + RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get()); + + // Fallback: We failed to create an RGBA colormap earlier, so we cannot properly paint + // to a temporary surface and preserve transparency. To ensure decent widget rendering, just + // paint directly to the target drawable. This will not render CSS rotational transforms properly. + if (!theme->m_themePartsHaveRGBAColormap && graphicsContext->gdkWindow()) { + m_paintRect = graphicsContext->getCTM().mapRect(targetRect); + m_target = graphicsContext->gdkWindow(); + return; + } + + // Some widgets render outside their rectangles. We need to account for this. + m_extraSpace = getExtraSpaceForWidget(theme); + + // Offset the target rectangle so that the extra space is within the boundaries of the scratch buffer. + m_paintRect = IntRect(IntPoint(m_extraSpace.width(), m_extraSpace.height()), + m_targetRect.size()); + + int width = m_targetRect.width() + (m_extraSpace.width() * 2); + int height = m_targetRect.height() + (m_extraSpace.height() * 2); + int scratchWidth = 0; + int scratchHeight = 0; + if (gScratchBuffer) + gdk_pixmap_get_size(gScratchBuffer, &scratchWidth, &scratchHeight); + + // We do not need to recreate the buffer if the current buffer is large enough. + if (!gScratchBuffer || scratchWidth < width || scratchHeight < height) { + purgeScratchBuffer(); + // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests. + width = (1 + (width >> 5)) << 5; + height = (1 + (height >> 5)) << 5; + + gScratchBuffer = gdk_pixmap_new(0, width, height, gdk_colormap_get_visual(theme->m_themeParts.colormap)->depth); + gdk_drawable_set_colormap(gScratchBuffer, theme->m_themeParts.colormap); + } + m_target = gScratchBuffer; + + // Clear the scratch buffer. + RefPtr<cairo_t> scratchBufferContext = adoptRef(gdk_cairo_create(gScratchBuffer)); + cairo_set_operator(scratchBufferContext.get(), CAIRO_OPERATOR_CLEAR); + cairo_paint(scratchBufferContext.get()); +} + +WidgetRenderingContext::~WidgetRenderingContext() +{ + // We do not need to blit back to the target in the fallback case. See above. + RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get()); + if (!theme->m_themePartsHaveRGBAColormap && m_graphicsContext->gdkWindow()) + return; + + // Don't paint the results back if there was an error. + if (m_hadError) { + scheduleScratchBufferPurge(); + return; + } + + // FIXME: It's unclear if it is necessary to preserve the current source here. + cairo_t* cairoContext = m_graphicsContext->platformContext(); + RefPtr<cairo_pattern_t> previousSource(cairo_get_source(cairoContext)); + + // The blit rectangle is the original target rectangle adjusted for any extra space. + IntRect fullTargetRect(m_targetRect); + fullTargetRect.inflateX(m_extraSpace.width()); + fullTargetRect.inflateY(m_extraSpace.height()); + + gdk_cairo_set_source_pixmap(cairoContext, gScratchBuffer, fullTargetRect.x(), fullTargetRect.y()); + cairo_rectangle(cairoContext, fullTargetRect.x(), fullTargetRect.y(), fullTargetRect.width(), fullTargetRect.height()); + cairo_fill(cairoContext); + cairo_set_source(cairoContext, previousSource.get()); + scheduleScratchBufferPurge(); +} + +bool WidgetRenderingContext::paintMozillaWidget(GtkThemeWidgetType type, GtkWidgetState* state, int flags, GtkTextDirection textDirection) +{ + // Sometimes moz_gtk_widget_paint modifies the clipping rectangle, so we must use a copy. + GdkRectangle clipRect = m_paintRect; + m_hadError = moz_gtk_widget_paint(type, m_target, &clipRect, &m_paintRect, + state, flags, textDirection) != MOZ_GTK_SUCCESS; + return !m_hadError; +} + +void WidgetRenderingContext::gtkPaintBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &m_paintRect, + widget, detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height); +} + +void WidgetRenderingContext::gtkPaintFocus(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, const gchar* detail) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_focus(gtk_widget_get_style(widget), m_target, stateType, &m_paintRect, widget, + detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height); +} + +void WidgetRenderingContext::gtkPaintSlider(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail, GtkOrientation orientation) +{ + GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; + gtk_paint_slider(gtk_widget_get_style(widget), m_target, stateType, shadowType, &m_paintRect, widget, + detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height, orientation); +} + +} + +#endif // GTK_API_VERSION_2 |