summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/wince/GraphicsContextWince.cpp
diff options
context:
space:
mode:
authorBen Murdoch <benm@google.com>2009-08-11 17:01:47 +0100
committerBen Murdoch <benm@google.com>2009-08-11 18:21:02 +0100
commit0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch)
tree2943df35f62d885c89d01063cc528dd73b480fea /WebCore/platform/graphics/wince/GraphicsContextWince.cpp
parent7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff)
downloadexternal_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.zip
external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.gz
external_webkit-0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5.tar.bz2
Merge in WebKit r47029.
Diffstat (limited to 'WebCore/platform/graphics/wince/GraphicsContextWince.cpp')
-rw-r--r--WebCore/platform/graphics/wince/GraphicsContextWince.cpp1930
1 files changed, 1930 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/wince/GraphicsContextWince.cpp b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp
new file mode 100644
index 0000000..c114c0e
--- /dev/null
+++ b/WebCore/platform/graphics/wince/GraphicsContextWince.cpp
@@ -0,0 +1,1930 @@
+/*
+ * Copyright (C) 2007-2009 Torch Mobile Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "config.h"
+#include "GraphicsContext.h"
+
+#include "CharacterNames.h"
+#include "GlyphBuffer.h"
+#include "Gradient.h"
+#include "GraphicsContextPrivate.h"
+#include "NotImplemented.h"
+#include "Path.h"
+#include "PlatformPathWince.h"
+#include "SharedBitmap.h"
+#include "SimpleFontData.h"
+#include "TransformationMatrix.h"
+#include <wtf/OwnPtr.h>
+
+#include <windows.h>
+
+namespace WebCore {
+
+typedef void (*FuncGradientFillRectLinear)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, const Vector<Gradient::ColorStop>& stops);
+typedef void (*FuncGradientFillRectRadial)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, float r0, float r1, const Vector<Gradient::ColorStop>& stops);
+FuncGradientFillRectLinear g_linearGradientFiller = 0;
+FuncGradientFillRectRadial g_radialGradientFiller = 0;
+
+static inline bool isZero(double d)
+{
+ return d > 0 ? d <= 1.E-10 : d >= -1.E-10;
+}
+
+// stableRound rounds -0.5 to 0, where lround rounds -0.5 to -1.
+static inline int stableRound(double d)
+{
+ if (d > 0)
+ return static_cast<int>(d + 0.5);
+
+ int i = static_cast<int>(d);
+ return i - d > 0.5 ? i - 1 : i;
+}
+
+// Unlike enclosingIntRect(), this function does strict rounding.
+static inline IntRect roundRect(const FloatRect& r)
+{
+ return IntRect(stableRound(r.x()), stableRound(r.y()), stableRound(r.right()) - stableRound(r.x()), stableRound(r.bottom()) - stableRound(r.y()));
+}
+
+// Rotation transformation
+class RotationTransform {
+public:
+ RotationTransform()
+ : m_cosA(1.)
+ , m_sinA(0.)
+ , m_preShiftX(0)
+ , m_preShiftY(0)
+ , m_postShiftX(0)
+ , m_postShiftY(0)
+ {
+ }
+ RotationTransform operator-() const
+ {
+ RotationTransform rtn;
+ rtn.m_cosA = m_cosA;
+ rtn.m_sinA = -m_sinA;
+ rtn.m_preShiftX = m_postShiftX;
+ rtn.m_preShiftY = m_postShiftY;
+ rtn.m_postShiftX = m_preShiftX;
+ rtn.m_postShiftY = m_preShiftY;
+ return rtn;
+ }
+ void map(double x1, double y1, double* x2, double* y2) const
+ {
+ x1 += m_preShiftX;
+ y1 += m_preShiftY;
+ *x2 = x1 * m_cosA + y1 * m_sinA + m_postShiftX;
+ *y2 = y1 * m_cosA - x1 * m_sinA + m_postShiftY;
+ }
+ void map(int x1, int y1, int* x2, int* y2) const
+ {
+ x1 += m_preShiftX;
+ y1 += m_preShiftY;
+ *x2 = stableRound(x1 * m_cosA + y1 * m_sinA) + m_postShiftX;
+ *y2 = stableRound(y1 * m_cosA - x1 * m_sinA) + m_postShiftY;
+ }
+
+ double m_cosA;
+ double m_sinA;
+ int m_preShiftX;
+ int m_preShiftY;
+ int m_postShiftX;
+ int m_postShiftY;
+};
+
+template<class T> static inline IntPoint mapPoint(const IntPoint& p, const T& t)
+{
+ int x, y;
+ t.map(p.x(), p.y(), &x, &y);
+ return IntPoint(x, y);
+}
+
+template<class T> static inline FloatPoint mapPoint(const FloatPoint& p, const T& t)
+{
+ double x, y;
+ t.map(p.x(), p.y(), &x, &y);
+ return FloatPoint(static_cast<float>(x), static_cast<float>(y));
+}
+
+template<class Transform, class Rect, class Value> static inline Rect mapRect(const Rect& rect, const Transform& transform)
+{
+ Value x[4], y[4];
+ Value l, t, r, b;
+ r = rect.right() - 1;
+ b = rect.bottom() - 1;
+ transform.map(rect.x(), rect.y(), x, y);
+ transform.map(rect.x(), b, x + 1, y + 1);
+ transform.map(r, b, x + 2, y + 2);
+ transform.map(r, rect.y(), x + 3, y + 3);
+ l = r = x[3];
+ t = b = y[3];
+ for (int i = 0; i < 3; ++i) {
+ if (x[i] < l)
+ l = x[i];
+ else if (x[i] > r)
+ r = x[i];
+
+ if (y[i] < t)
+ t = y[i];
+ else if (y[i] > b)
+ b = y[i];
+ }
+
+ return IntRect(l, t, r - l + 1, b - t + 1);
+}
+
+template<class T> static inline IntRect mapRect(const IntRect& rect, const T& transform)
+{
+ return mapRect<T, IntRect, int>(rect, transform);
+}
+
+template<class T> static inline FloatRect mapRect(const FloatRect& rect, const T& transform)
+{
+ return mapRect<T, FloatRect, double>(rect, transform);
+}
+
+class GraphicsContextPlatformPrivateData {
+public:
+ GraphicsContextPlatformPrivateData()
+ : m_transform()
+ , m_opacity(1.0)
+ {
+ }
+
+ TransformationMatrix m_transform;
+ float m_opacity;
+ Vector<Path> m_paths;
+};
+
+enum AlphaPaintType {
+ AlphaPaintNone,
+ AlphaPaintImage,
+ AlphaPaintOther,
+};
+
+class GraphicsContextPlatformPrivate : public GraphicsContextPlatformPrivateData {
+public:
+ GraphicsContextPlatformPrivate(HDC dc)
+ : m_dc(dc)
+ {
+ }
+ ~GraphicsContextPlatformPrivate()
+ {
+ while (!m_backupData.isEmpty())
+ restore();
+ }
+
+ IntPoint origin() const
+ {
+ return IntPoint(stableRound(-m_transform.e()), stableRound(-m_transform.f()));
+ }
+
+ void translate(float x, float y)
+ {
+ m_transform.translate(x, y);
+ }
+
+ void scale(const FloatSize& size)
+ {
+ m_transform.scaleNonUniform(size.width(), size.height());
+ }
+
+ void rotate(float radians)
+ {
+ m_transform.rotate(rad2deg(radians));
+ }
+
+ void concatCTM(const TransformationMatrix& transform)
+ {
+ m_transform = transform * m_transform;
+ }
+
+ IntRect mapRect(const IntRect& rect) const
+ {
+ return m_transform.mapRect(rect);
+ }
+
+ FloatRect mapRect(const FloatRect& rect) const
+ {
+ return m_transform.mapRect(rect);
+ }
+
+ IntPoint mapPoint(const IntPoint& point) const
+ {
+ return m_transform.mapPoint(point);
+ }
+
+ FloatPoint mapPoint(const FloatPoint& point) const
+ {
+ return m_transform.mapPoint(point);
+ }
+
+ FloatSize mapSize(const FloatSize& size) const
+ {
+ double w, h;
+ m_transform.map(size.width(), size.height(), w, h);
+ return FloatSize(static_cast<float>(w), static_cast<float>(h));
+ }
+
+ void save()
+ {
+ if (m_dc)
+ SaveDC(m_dc);
+
+ m_backupData.append(*static_cast<GraphicsContextPlatformPrivateData*>(this));
+ }
+
+ void restore()
+ {
+ if (m_backupData.isEmpty())
+ return;
+
+ if (m_dc)
+ RestoreDC(m_dc, -1);
+
+ GraphicsContextPlatformPrivateData::operator=(m_backupData.last());
+ m_backupData.removeLast();
+ }
+
+ bool hasAlpha() const { return m_bitmap && m_bitmap->hasAlpha(); }
+
+ PassRefPtr<SharedBitmap> getTransparentLayerBitmap(IntRect& origRect, AlphaPaintType alphaPaint, RECT& bmpRect, bool checkClipBox, bool force) const
+ {
+ if (m_opacity <= 0)
+ return 0;
+
+ if (force || m_opacity < 1.) {
+ if (checkClipBox) {
+ RECT clipBox;
+ int clipType = GetClipBox(m_dc, &clipBox);
+ if (clipType == SIMPLEREGION || clipType == COMPLEXREGION)
+ origRect.intersect(clipBox);
+ if (origRect.isEmpty())
+ return 0;
+ }
+
+ RefPtr<SharedBitmap> bmp = SharedBitmap::createInstance(alphaPaint == AlphaPaintNone, origRect.width(), origRect.height(), false);
+ SetRect(&bmpRect, 0, 0, origRect.width(), origRect.height());
+ if (bmp) {
+ switch (alphaPaint) {
+ case AlphaPaintNone:
+ case AlphaPaintImage:
+ {
+ SharedBitmap::DCHolder dc(bmp.get());
+ if (dc.get()) {
+ BitBlt(dc.get(), 0, 0, origRect.width(), origRect.height(), m_dc, origRect.x(), origRect.y(), SRCCOPY);
+ if (bmp->is32bit() && (!m_bitmap || m_bitmap->is16bit())) {
+ // Set alpha channel
+ unsigned* pixels = (unsigned*)bmp->bytes();
+ const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels();
+ while (pixels < pixelsEnd) {
+ *pixels |= 0xFF000000;
+ ++pixels;
+ }
+ }
+ return bmp;
+ }
+ }
+ break;
+ //case AlphaPaintOther:
+ default:
+ memset(bmp->bytes(), 0xFF, bmp->bitmapInfo().numPixels() * 4);
+ return bmp;
+ break;
+ }
+ }
+ }
+
+ bmpRect = origRect;
+ return 0;
+ }
+
+ void paintBackTransparentLayerBitmap(HDC hdc, SharedBitmap* bmp, const IntRect& origRect, AlphaPaintType alphaPaint, const RECT& bmpRect)
+ {
+ if (hdc == m_dc)
+ return;
+
+#if !defined(NO_ALPHABLEND)
+ if (alphaPaint == AlphaPaintOther) {
+ ASSERT(bmp && bmp->bytes() && bmp->is32bit());
+ unsigned* pixels = (unsigned*)bmp->bytes();
+ const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels();
+ while (pixels < pixelsEnd) {
+ *pixels ^= 0xFF000000;
+ ++pixels;
+ }
+ }
+ if (m_opacity < 1. || alphaPaint == AlphaPaintOther) {
+ const BLENDFUNCTION blend = { AC_SRC_OVER, 0
+ , m_opacity >= 1. ? 255 : (BYTE)(m_opacity * 255)
+ , alphaPaint == AlphaPaintNone ? 0 : AC_SRC_ALPHA };
+ AlphaBlend(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, blend);
+ } else
+#endif
+ StretchBlt(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, SRCCOPY);
+ }
+
+ HDC m_dc;
+ RefPtr<SharedBitmap> m_bitmap;
+ Vector<GraphicsContextPlatformPrivateData> m_backupData;
+};
+
+static HPEN createPen(const Color& col, double fWidth, StrokeStyle style)
+{
+ int width = stableRound(fWidth);
+ if (width < 1)
+ width = 1;
+
+ int penStyle = PS_NULL;
+ switch (style) {
+ case SolidStroke:
+ penStyle = PS_SOLID;
+ break;
+ case DottedStroke: // not supported on Windows CE
+ case DashedStroke:
+ penStyle = PS_DASH;
+ width = 1;
+ break;
+ default:
+ break;
+ }
+
+ return CreatePen(penStyle, width, RGB(col.red(), col.green(), col.blue()));
+}
+
+static inline HGDIOBJ createBrush(const Color& col)
+{
+ return CreateSolidBrush(RGB(col.red(), col.green(), col.blue()));
+}
+
+template <typename PixelType, bool Is16bit> static void _rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform)
+{
+ int destW = destBmp->width();
+ int destH = destBmp->height();
+ int sourceW = sourceBmp->width();
+ int sourceH = sourceBmp->height();
+ PixelType* dest = (PixelType*)destBmp->bytes();
+ const PixelType* source = (const PixelType*)sourceBmp->bytes();
+ int padding;
+ int paddedSourceW;
+ if (Is16bit) {
+ padding = destW & 1;
+ paddedSourceW = sourceW + (sourceW & 1);
+ } else {
+ padding = 0;
+ paddedSourceW = sourceW;
+ }
+ if (isZero(transform.m_sinA)) {
+ int cosA = transform.m_cosA > 0 ? 1 : -1;
+ for (int y = 0; y < destH; ++y) {
+ for (int x = 0; x < destW; ++x) {
+ int x1 = x + transform.m_preShiftX;
+ int y1 = y + transform.m_preShiftY;
+ int srcX = x1 * cosA + transform.m_postShiftX;
+ int srcY = y1 * cosA - transform.m_postShiftY;
+ if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH)
+ *dest++ = source[srcY * paddedSourceW + srcX] | 0xFF000000;
+ else
+ *dest++ |= 0xFF;
+ }
+ dest += padding;
+ }
+ } else if (isZero(transform.m_cosA)) {
+ int sinA = transform.m_sinA > 0 ? 1 : -1;
+ for (int y = 0; y < destH; ++y) {
+ for (int x = 0; x < destW; ++x) {
+ int x1 = x + transform.m_preShiftX;
+ int y1 = y + transform.m_preShiftY;
+ int srcX = y1 * sinA + transform.m_postShiftX;
+ int srcY = -x1 * sinA + transform.m_postShiftY;
+ if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH)
+ *dest++ = source[srcY * paddedSourceW + srcX];
+ }
+ dest += padding;
+ }
+ } else {
+ for (int y = 0; y < destH; ++y) {
+ for (int x = 0; x < destW; ++x) {
+ // FIXME: for best quality, we should get weighted sum of four neighbours,
+ // but that will be too expensive
+ int srcX, srcY;
+ transform.map(x, y, &srcX, &srcY);
+ if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH)
+ *dest++ = source[srcY * paddedSourceW + srcX];
+ }
+ dest += padding;
+ }
+ }
+}
+
+static void rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform)
+{
+ ASSERT(destBmp->is16bit() == sourceBmp->is16bit());
+ if (destBmp->is16bit())
+ _rotateBitmap<unsigned short, true>(destBmp, sourceBmp, transform);
+ else
+ _rotateBitmap<unsigned, false>(destBmp, sourceBmp, transform);
+}
+
+class TransparentLayerDC : Noncopyable {
+public:
+ TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform = 0, int alpha = 255, bool paintImage = false);
+ ~TransparentLayerDC();
+
+ HDC hdc() const { return m_memDc; }
+ const RECT& rect() const { return m_bmpRect; }
+ IntSize toShift() const { return IntSize(m_bmpRect.left - m_origRect.x(), m_bmpRect.top - m_origRect.y()); }
+ void fillAlphaChannel();
+
+private:
+ GraphicsContextPlatformPrivate* m_data;
+ IntRect m_origRect;
+ IntRect m_rotatedOrigRect;
+ HDC m_memDc;
+ RefPtr<SharedBitmap> m_bitmap;
+ RefPtr<SharedBitmap> m_rotatedBitmap;
+ RECT m_bmpRect;
+ unsigned m_key1;
+ unsigned m_key2;
+ RotationTransform m_rotation;
+ float m_oldOpacity;
+ AlphaPaintType m_alphaPaintType;
+};
+
+TransparentLayerDC::TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform, int alpha, bool paintImage)
+: m_data(data)
+, m_origRect(origRect)
+, m_oldOpacity(data->m_opacity)
+// m_key1 and m_key2 are not initalized here. They are used only in the case that
+// SharedBitmap::getDC() is called, I.E., when m_bitmap is not null.
+{
+ m_data->m_opacity *= alpha / 255.;
+ bool mustCreateLayer;
+ if (!m_data->hasAlpha()) {
+ mustCreateLayer = false;
+ m_alphaPaintType = AlphaPaintNone;
+ } else {
+ mustCreateLayer = true;
+ m_alphaPaintType = paintImage ? AlphaPaintImage : AlphaPaintOther;
+ }
+ if (rectBeforeTransform && !isZero(m_data->m_transform.b())) {
+ m_rotatedOrigRect = origRect;
+ m_rotatedBitmap = m_data->getTransparentLayerBitmap(m_rotatedOrigRect, m_alphaPaintType, m_bmpRect, false, true);
+ if (m_rotatedBitmap) {
+ double a = m_data->m_transform.a();
+ double b = m_data->m_transform.b();
+ double c = _hypot(a, b);
+ m_rotation.m_cosA = a / c;
+ m_rotation.m_sinA = b / c;
+
+ int centerX = origRect.x() + origRect.width() / 2;
+ int centerY = origRect.y() + origRect.height() / 2;
+ m_rotation.m_preShiftX = -centerX;
+ m_rotation.m_preShiftY = -centerY;
+ m_rotation.m_postShiftX = centerX;
+ m_rotation.m_postShiftY = centerY;
+
+ m_origRect = mapRect(m_rotatedOrigRect, m_rotation);
+
+ m_rotation.m_preShiftX += m_rotatedOrigRect.x();
+ m_rotation.m_preShiftY += m_rotatedOrigRect.y();
+ m_rotation.m_postShiftX -= m_origRect.x();
+ m_rotation.m_postShiftY -= m_origRect.y();
+
+ FloatPoint topLeft = m_data->m_transform.mapPoint(FloatPoint(rectBeforeTransform->topLeft()));
+ FloatPoint topRight(rectBeforeTransform->right() - 1, rectBeforeTransform->y());
+ topRight = m_data->m_transform.mapPoint(topRight);
+ FloatPoint bottomLeft(rectBeforeTransform->x(), rectBeforeTransform->bottom() - 1);
+ bottomLeft = m_data->m_transform.mapPoint(bottomLeft);
+ FloatSize sideTop = topRight - topLeft;
+ FloatSize sideLeft = bottomLeft - topLeft;
+ float width = _hypot(sideTop.width() + 1, sideTop.height() + 1);
+ float height = _hypot(sideLeft.width() + 1, sideLeft.height() + 1);
+
+ origRect.inflateX(stableRound((width - origRect.width()) * 0.5));
+ origRect.inflateY(stableRound((height - origRect.height()) * 0.5));
+
+ m_bitmap = SharedBitmap::createInstance(m_rotatedBitmap->is16bit(), m_origRect.width(), m_origRect.height(), true);
+ if (m_bitmap)
+ rotateBitmap(m_bitmap.get(), m_rotatedBitmap.get(), -m_rotation);
+ else
+ m_rotatedBitmap = 0;
+ }
+ } else
+ m_bitmap = m_data->getTransparentLayerBitmap(m_origRect, m_alphaPaintType, m_bmpRect, true, mustCreateLayer);
+ if (m_bitmap)
+ m_memDc = m_bitmap->getDC(&m_key1, &m_key2);
+ else
+ m_memDc = m_data->m_dc;
+}
+
+TransparentLayerDC::~TransparentLayerDC()
+{
+ if (m_rotatedBitmap) {
+ m_bitmap->releaseDC(m_memDc, m_key1, m_key2);
+ m_key1 = m_key2 = 0;
+ rotateBitmap(m_rotatedBitmap.get(), m_bitmap.get(), m_rotation);
+ m_memDc = m_rotatedBitmap->getDC(&m_key1, &m_key2);
+ m_data->paintBackTransparentLayerBitmap(m_memDc, m_rotatedBitmap.get(), m_rotatedOrigRect, m_alphaPaintType, m_bmpRect);
+ m_rotatedBitmap->releaseDC(m_memDc, m_key1, m_key2);
+ } else if (m_bitmap) {
+ m_data->paintBackTransparentLayerBitmap(m_memDc, m_bitmap.get(), m_origRect, m_alphaPaintType, m_bmpRect);
+ m_bitmap->releaseDC(m_memDc, m_key1, m_key2);
+ }
+ m_data->m_opacity = m_oldOpacity;
+}
+
+void TransparentLayerDC::fillAlphaChannel()
+{
+ if (!m_bitmap || !m_bitmap->is32bit())
+ return;
+
+ unsigned* pixels = (unsigned*)m_bitmap->bytes();
+ const unsigned* const pixelsEnd = pixels + m_bitmap->bitmapInfo().numPixels();
+ while (pixels < pixelsEnd) {
+ *pixels |= 0xFF000000;
+ ++pixels;
+ }
+}
+
+class ScopeDCProvider : Noncopyable {
+public:
+ explicit ScopeDCProvider(GraphicsContextPlatformPrivate* data)
+ : m_data(data)
+ {
+ if (m_data->m_bitmap)
+ m_data->m_dc = m_data->m_bitmap->getDC(&m_key1, &m_key2);
+ }
+ ~ScopeDCProvider()
+ {
+ if (m_data->m_bitmap) {
+ m_data->m_bitmap->releaseDC(m_data->m_dc, m_key1, m_key2);
+ m_data->m_dc = 0;
+ }
+ }
+private:
+ GraphicsContextPlatformPrivate* m_data;
+ unsigned m_key1;
+ unsigned m_key2;
+};
+
+
+GraphicsContext::GraphicsContext(PlatformGraphicsContext* dc)
+: m_common(createGraphicsContextPrivate())
+, m_data(new GraphicsContextPlatformPrivate(dc))
+{
+}
+
+GraphicsContext::~GraphicsContext()
+{
+ destroyGraphicsContextPrivate(m_common);
+ delete m_data;
+}
+
+void GraphicsContext::setBitmap(PassRefPtr<SharedBitmap> bmp)
+{
+ ASSERT(!m_data->m_dc);
+ m_data->m_bitmap = bmp;
+}
+
+HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
+{
+ notImplemented();
+ ASSERT_NOT_REACHED();
+ return 0;
+}
+
+void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
+{
+ notImplemented();
+ ASSERT_NOT_REACHED();
+}
+
+void GraphicsContext::savePlatformState()
+{
+ m_data->save();
+}
+
+void GraphicsContext::restorePlatformState()
+{
+ m_data->restore();
+}
+
+void GraphicsContext::drawRect(const IntRect& rect)
+{
+ if (!m_data->m_opacity || paintingDisabled() || rect.isEmpty())
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = m_data->mapRect(rect);
+ TransparentLayerDC transparentDC(m_data, trRect, &rect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ HGDIOBJ brush = 0;
+ HGDIOBJ oldBrush;
+ if (fillColor().alpha()) {
+ brush = createBrush(fillColor());
+ oldBrush = SelectObject(dc, brush);
+ } else
+ SelectObject(dc, GetStockObject(NULL_BRUSH));
+
+ HGDIOBJ pen = 0;
+ HGDIOBJ oldPen;
+ if (strokeStyle() != NoStroke) {
+ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ oldPen = SelectObject(dc, pen);
+ } else
+ SelectObject(dc, GetStockObject(NULL_PEN));
+
+ if (!brush && !pen)
+ return;
+
+ if (trRect.width() <= 0)
+ trRect.setWidth(1);
+ if (trRect.height() <= 0)
+ trRect.setHeight(1);
+
+ Rectangle(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom());
+
+ if (pen) {
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+ }
+
+ if (brush) {
+ SelectObject(dc, oldBrush);
+ DeleteObject(brush);
+ }
+}
+
+void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
+{
+ if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke || !strokeColor().alpha())
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntPoint trPoint1 = m_data->mapPoint(point1);
+ IntPoint trPoint2 = m_data->mapPoint(point2);
+
+ IntRect lineRect(trPoint1, trPoint2 - trPoint1);
+ lineRect.setHeight(lineRect.height() + strokeThickness());
+ TransparentLayerDC transparentDC(m_data, lineRect, 0, strokeColor().alpha());
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trPoint1 += transparentDC.toShift();
+ trPoint2 += transparentDC.toShift();
+
+ HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ HGDIOBJ oldPen = SelectObject(dc, pen);
+
+ MoveToEx(dc, trPoint1.x(), trPoint1.y(), 0);
+ LineTo(dc, trPoint2.x(), trPoint2.y());
+
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+}
+
+void GraphicsContext::drawEllipse(const IntRect& rect)
+{
+ if (!m_data->m_opacity || paintingDisabled() || (!fillColor().alpha() && strokeStyle() == NoStroke))
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = m_data->mapRect(rect);
+ TransparentLayerDC transparentDC(m_data, trRect, &rect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ HGDIOBJ brush = 0;
+ HGDIOBJ oldBrush;
+ if (fillColor().alpha()) {
+ brush = createBrush(fillColor());
+ oldBrush = SelectObject(dc, brush);
+ } else
+ SelectObject(dc, GetStockObject(NULL_BRUSH));
+ HGDIOBJ pen = 0;
+ HGDIOBJ oldPen;
+ if (strokeStyle() != NoStroke) {
+ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ oldPen = SelectObject(dc, pen);
+ } else
+ SelectObject(dc, GetStockObject(NULL_PEN));
+
+ Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom());
+
+ if (pen) {
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+ }
+
+ if (brush) {
+ SelectObject(dc, oldBrush);
+ DeleteObject(brush);
+ }
+}
+
+static inline bool equalAngle(double a, double b)
+{
+ return fabs(a - b) < 1E-5;
+}
+
+void getEllipsePointByAngle(double angle, double a, double b, float& x, float& y)
+{
+ while (angle < 0)
+ angle += 2 * piDouble;
+ while (angle >= 2 * piDouble)
+ angle -= 2 * piDouble;
+
+ if (equalAngle(angle, 0) || equalAngle(angle, 2 * piDouble)) {
+ x = a;
+ y = 0;
+ } else if (equalAngle(angle, piDouble)) {
+ x = -a;
+ y = 0;
+ } else if (equalAngle(angle, .5 * piDouble)) {
+ x = 0;
+ y = b;
+ } else if (equalAngle(angle, 1.5 * piDouble)) {
+ x = 0;
+ y = -b;
+ } else {
+ double k = tan(angle);
+ double sqA = a * a;
+ double sqB = b * b;
+ double tmp = 1. / (1. / sqA + (k * k) / sqB);
+ tmp = tmp <= 0 ? 0 : sqrt(tmp);
+ if (angle > .5 * piDouble && angle < 1.5 * piDouble)
+ tmp = -tmp;
+ x = tmp;
+
+ k = tan(.5 * piDouble - angle);
+ tmp = 1. / ((k * k) / sqA + 1 / sqB);
+ tmp = tmp <= 0 ? 0 : sqrt(tmp);
+ if (angle > piDouble)
+ tmp = -tmp;
+ y = tmp;
+ }
+}
+
+void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
+{
+ if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke || rect.isEmpty())
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = m_data->mapRect(rect);
+ TransparentLayerDC transparentDC(m_data, trRect, &rect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ HGDIOBJ oldPen = SelectObject(dc, pen);
+
+ double a = trRect.width() * 0.5;
+ double b = trRect.height() * 0.5;
+ int centerX = stableRound(trRect.x() + a);
+ int centerY = stableRound(trRect.y() + b);
+ float fstartX, fstartY, fendX, fendY;
+ int startX, startY, endX, endY;
+ getEllipsePointByAngle(deg2rad((double)startAngle), a, b, fstartX, fstartY);
+ getEllipsePointByAngle(deg2rad((double)startAngle + angleSpan), a, b, fendX, fendY);
+ startX = stableRound(fstartX);
+ startY = stableRound(fstartY);
+ endX = stableRound(fendX);
+ endY = stableRound(fendY);
+
+ startX += centerX;
+ startY = centerY - startY;
+ endX += centerX;
+ endY = centerY - endY;
+ RECT clipRect;
+ if (startX < endX) {
+ clipRect.left = startX;
+ clipRect.right = endX;
+ } else {
+ clipRect.left = endX;
+ clipRect.right = startX;
+ }
+ if (startY < endY) {
+ clipRect.top = startY;
+ clipRect.bottom = endY;
+ } else {
+ clipRect.top = endY;
+ clipRect.bottom = startY;
+ }
+
+ OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
+ bool newClip;
+ if (GetClipRgn(dc, clipRgn.get()) <= 0) {
+ newClip = true;
+ clipRgn.set(CreateRectRgn(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom));
+ SelectClipRgn(dc, clipRgn.get());
+ } else {
+ newClip = false;
+ IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+ }
+
+ HGDIOBJ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
+ Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom());
+ SelectObject(dc, oldBrush);
+
+ if (newClip)
+ SelectClipRgn(dc, 0);
+ else
+ SelectClipRgn(dc, clipRgn.get());
+
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+}
+
+void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
+{
+ if (!m_data->m_opacity || paintingDisabled() || npoints <= 1 || !points)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ Vector<POINT, 20> winPoints(npoints);
+ FloatPoint trPoint = m_data->mapPoint(points[0]);
+ winPoints[0].x = stableRound(trPoint.x());
+ winPoints[0].y = stableRound(trPoint.y());
+ RECT rect = { winPoints[0].x, winPoints[0].y, winPoints[0].x, winPoints[0].y };
+ for (size_t i = 1; i < npoints; ++i) {
+ trPoint = m_data->mapPoint(points[i]);
+ winPoints[i].x = stableRound(trPoint.x());
+ winPoints[i].y = stableRound(trPoint.y());
+ if (rect.left > winPoints[i].x)
+ rect.left = winPoints[i].x;
+ else if (rect.right < winPoints[i].x)
+ rect.right = winPoints[i].x;
+ if (rect.top > winPoints[i].y)
+ rect.top = winPoints[i].y;
+ else if (rect.bottom < winPoints[i].y)
+ rect.bottom = winPoints[i].y;
+ }
+ rect.bottom += 1;
+ rect.right += 1;
+
+ IntRect intRect(rect);
+ TransparentLayerDC transparentDC(m_data, intRect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+
+ for (size_t i = 0; i < npoints; ++i) {
+ winPoints[i].x += transparentDC.toShift().width();
+ winPoints[i].y += transparentDC.toShift().height();
+ }
+
+ HGDIOBJ brush = 0;
+ HGDIOBJ oldBrush;
+ if (fillColor().alpha()) {
+ brush = createBrush(fillColor());
+ oldBrush = SelectObject(dc, brush);
+ } else
+ SelectObject(dc, GetStockObject(NULL_BRUSH));
+
+ HGDIOBJ pen = 0;
+ HGDIOBJ oldPen;
+ if (strokeStyle() != NoStroke) {
+ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ oldPen = SelectObject(dc, pen);
+ } else
+ SelectObject(dc, GetStockObject(NULL_PEN));
+
+ if (!brush && !pen)
+ return;
+
+ Polygon(dc, winPoints.data(), npoints);
+
+ if (pen) {
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+ }
+
+ if (brush) {
+ SelectObject(dc, oldBrush);
+ DeleteObject(brush);
+ }
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
+{
+ if (paintingDisabled() || !m_data->m_opacity)
+ return;
+
+ int alpha = color.alpha();
+ if (!alpha)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect intRect = enclosingIntRect(rect);
+ TransparentLayerDC transparentDC(m_data, m_data->mapRect(intRect), &intRect, alpha);
+
+ if (!transparentDC.hdc())
+ return;
+
+ OwnPtr<HBRUSH> hbrush(CreateSolidBrush(RGB(color.red(), color.green(), color.blue())));
+ FillRect(transparentDC.hdc(), &transparentDC.rect(), hbrush.get());
+}
+
+void GraphicsContext::clip(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = enclosingIntRect(m_data->mapRect(rect));
+
+ OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
+ if (GetClipRgn(m_data->m_dc, clipRgn.get()) > 0)
+ IntersectClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom());
+ else {
+ clipRgn.set(CreateRectRgn(trRect.x(), trRect.y(), trRect.right(), trRect.bottom()));
+ SelectClipRgn(m_data->m_dc, clipRgn.get());
+ }
+}
+
+void GraphicsContext::clipOut(const IntRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = m_data->mapRect(rect);
+
+ ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom());
+}
+
+void GraphicsContext::drawFocusRing(const Color& color)
+{
+ if (!m_data->m_opacity || paintingDisabled())
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ int radius = (focusRingWidth() - 1) / 2;
+ int offset = radius + focusRingOffset();
+
+ const Vector<IntRect>& rects = focusRingRects();
+ unsigned rectCount = rects.size();
+ IntRect finalFocusRect;
+ for (unsigned i = 0; i < rectCount; i++) {
+ IntRect focusRect = rects[i];
+ focusRect.inflate(offset);
+ finalFocusRect.unite(focusRect);
+ }
+
+ IntRect intRect = finalFocusRect;
+ IntRect trRect = m_data->mapRect(finalFocusRect);
+ TransparentLayerDC transparentDC(m_data, trRect, &intRect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ RECT rect = trRect;
+ DrawFocusRect(dc, &rect);
+}
+
+void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool printing)
+{
+ if (paintingDisabled())
+ return;
+
+ StrokeStyle oldStyle = strokeStyle();
+ setStrokeStyle(SolidStroke);
+ drawLine(origin, origin + IntSize(width, 0));
+ setStrokeStyle(oldStyle);
+}
+
+void GraphicsContext::drawLineForMisspellingOrBadGrammar(const IntPoint&, int width, bool grammar)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setPlatformFillColor(const Color& col)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setPlatformStrokeColor(const Color& col)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
+{
+ notImplemented();
+}
+
+void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
+{
+ // We can only clip rectangles on WINCE
+ clip(rect);
+}
+
+void GraphicsContext::clearRect(const FloatRect& rect)
+{
+ if (paintingDisabled())
+ return;
+
+ if (m_data->hasAlpha()) {
+ IntRect trRect = enclosingIntRect(m_data->mapRect(rect));
+ m_data->m_bitmap->clearPixels(trRect);
+ return;
+ }
+
+ fillRect(rect, Color(Color::white));
+}
+
+void GraphicsContext::strokeRect(const FloatRect& rect, float width)
+{
+ if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect intRect = enclosingIntRect(rect);
+ IntRect trRect = m_data->mapRect(intRect);
+ TransparentLayerDC transparentDC(m_data, trRect, &intRect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ HGDIOBJ oldPen = SelectObject(dc, pen);
+
+ int right = trRect.right() - 1;
+ int bottom = trRect.bottom() - 1;
+ const POINT intPoints[5] =
+ {
+ { trRect.x(), trRect.y() },
+ { right, trRect.y() },
+ { right, bottom },
+ { trRect.x(), bottom },
+ { trRect.x(), trRect.y() }
+ };
+
+ Polyline(dc, intPoints, 5);
+
+ SelectObject(dc, oldPen);
+ DeleteObject(pen);
+}
+
+void GraphicsContext::beginTransparencyLayer(float opacity)
+{
+ m_data->save();
+ m_data->m_opacity *= opacity;
+}
+
+void GraphicsContext::endTransparencyLayer()
+{
+ m_data->restore();
+}
+
+void GraphicsContext::concatCTM(const TransformationMatrix& transform)
+{
+ m_data->concatCTM(transform);
+}
+
+TransformationMatrix& GraphicsContext::affineTransform()
+{
+ return m_data->m_transform;
+}
+
+const TransformationMatrix& GraphicsContext::affineTransform() const
+{
+ return m_data->m_transform;
+}
+
+void GraphicsContext::resetAffineTransform()
+{
+ m_data->m_transform.makeIdentity();
+}
+
+void GraphicsContext::translate(float x, float y)
+{
+ m_data->translate(x, y);
+}
+
+void GraphicsContext::rotate(float radians)
+{
+ m_data->rotate(radians);
+}
+
+IntPoint GraphicsContext::origin()
+{
+ return m_data->origin();
+}
+
+void GraphicsContext::scale(const FloatSize& size)
+{
+ m_data->scale(size);
+}
+
+void GraphicsContext::setLineCap(LineCap lineCap)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setLineJoin(LineJoin lineJoin)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setMiterLimit(float miter)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setAlpha(float alpha)
+{
+ m_data->m_opacity = alpha;
+}
+
+void GraphicsContext::setCompositeOperation(CompositeOperator op)
+{
+ notImplemented();
+}
+
+void GraphicsContext::beginPath()
+{
+ m_data->m_paths.clear();
+}
+
+void GraphicsContext::addPath(const Path& path)
+{
+ m_data->m_paths.append(path);
+}
+
+void GraphicsContext::clip(const Path& path)
+{
+ notImplemented();
+}
+
+void GraphicsContext::clipOut(const Path&)
+{
+ notImplemented();
+}
+
+void GraphicsContext::clipOutEllipseInRect(const IntRect&)
+{
+ notImplemented();
+}
+
+static inline IntPoint rectCenterPoint(const RECT& rect)
+{
+ return IntPoint(rect.left + (rect.right - rect.left) / 2, rect.top + (rect.bottom - rect.top) / 2);
+}
+void GraphicsContext::fillRoundedRect(const IntRect& fillRect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& c)
+{
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntSize shadowSize;
+ int shadowBlur = 0;
+ Color shadowColor;
+
+ getShadow(shadowSize, shadowBlur, shadowColor);
+
+ IntRect dstRect = fillRect;
+
+ dstRect.move(shadowSize);
+ dstRect.inflate(shadowBlur);
+ dstRect = m_data->mapRect(dstRect);
+
+ FloatSize newTopLeft(m_data->mapSize(topLeft));
+ FloatSize newTopRight(m_data->mapSize(topRight));
+ FloatSize newBottomLeft(m_data->mapSize(bottomLeft));
+ FloatSize newBottomRight(m_data->mapSize(bottomRight));
+
+ TransparentLayerDC transparentDc(m_data, dstRect, &fillRect);
+ HDC dc = transparentDc.hdc();
+ if (!dc)
+ return;
+
+ dstRect.move(transparentDc.toShift());
+
+ RECT rectWin = dstRect;
+
+ HGDIOBJ brush = createBrush(shadowColor);
+ HGDIOBJ oldBrush = SelectObject(dc, brush);
+
+ SelectObject(dc, GetStockObject(NULL_PEN));
+
+ IntPoint centerPoint = rectCenterPoint(rectWin);
+ // Draw top left half
+ RECT clipRect(rectWin);
+ clipRect.right = centerPoint.x();
+ clipRect.bottom = centerPoint.y();
+
+ OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
+ bool needsNewClip = (GetClipRgn(dc, clipRgn.get()) <= 0);
+
+ drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopLeft.width() * 2), stableRound(newTopLeft.height() * 2));
+
+ // Draw top right
+ clipRect = rectWin;
+ clipRect.left = centerPoint.x();
+ clipRect.bottom = centerPoint.y();
+
+ drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopRight.width() * 2), stableRound(newTopRight.height() * 2));
+
+ // Draw bottom left
+ clipRect = rectWin;
+ clipRect.right = centerPoint.x();
+ clipRect.top = centerPoint.y();
+
+ drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomLeft.width() * 2), stableRound(newBottomLeft.height() * 2));
+
+ // Draw bottom right
+ clipRect = rectWin;
+ clipRect.left = centerPoint.x();
+ clipRect.top = centerPoint.y();
+
+ drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomRight.width() * 2), stableRound(newBottomRight.height() * 2));
+
+ SelectObject(dc, oldBrush);
+ DeleteObject(brush);
+}
+
+
+void GraphicsContext::drawRoundCorner(bool needsNewClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height)
+{
+ if (!dc)
+ return;
+
+ OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
+ if (needsNewClip) {
+ clipRgn.set(CreateRectRgn(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom));
+ SelectClipRgn(dc, clipRgn.get());
+ } else
+ IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
+
+ ::RoundRect(dc, rectWin.left , rectWin.top , rectWin.right , rectWin.bottom , width, height);
+
+ SelectClipRgn(dc, needsNewClip ? 0 : clipRgn.get());
+}
+
+
+FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect)
+{
+ notImplemented();
+ return frect;
+}
+
+Color gradientAverageColor(const Gradient* gradient)
+{
+ const Vector<Gradient::ColorStop>& stops = gradient->getStops();
+ if (stops.isEmpty())
+ return Color();
+
+ const Gradient::ColorStop& stop = stops.first();
+ if (stops.size() == 1)
+ return Color(stop.red, stop.green, stop.blue, stop.alpha);
+
+ const Gradient::ColorStop& lastStop = stops.last();
+ return Color((stop.red + lastStop.red) * 0.5f
+ , (stop.green + lastStop.green) * 0.5f
+ , (stop.blue + lastStop.blue) * 0.5f
+ , (stop.alpha + lastStop.alpha) * 0.5f);
+}
+
+void GraphicsContext::fillPath()
+{
+ Color c = m_common->state.fillColorSpace == GradientColorSpace && m_common->state.fillGradient
+ ? gradientAverageColor(m_common->state.fillGradient.get())
+ : fillColor();
+
+ if (!c.alpha() || !m_data->m_opacity)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) {
+ HGDIOBJ brush = createBrush(c);
+ for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) {
+ IntRect trRect = enclosingIntRect(m_data->mapRect(i->boundingRect()));
+ trRect.inflate(1);
+ TransparentLayerDC transparentDC(m_data, trRect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ continue;
+
+ TransformationMatrix tr = m_data->m_transform;
+ tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height());
+
+ SelectObject(dc, GetStockObject(NULL_PEN));
+ HGDIOBJ oldBrush = SelectObject(dc, brush);
+ i->platformPath()->fillPath(dc, &tr);
+ SelectObject(dc, oldBrush);
+ }
+ DeleteObject(brush);
+ } else {
+ SelectObject(m_data->m_dc, GetStockObject(NULL_PEN));
+ HGDIOBJ brush = createBrush(c);
+ HGDIOBJ oldBrush = SelectObject(m_data->m_dc, brush);
+ for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i)
+ i->platformPath()->fillPath(m_data->m_dc, &m_data->m_transform);
+ SelectObject(m_data->m_dc, oldBrush);
+ DeleteObject(brush);
+ }
+}
+
+
+void GraphicsContext::strokePath()
+{
+ if (!m_data->m_opacity)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) {
+ HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) {
+ IntRect trRect = enclosingIntRect(m_data->mapRect(i->boundingRect()));
+ trRect.inflate(1);
+ TransparentLayerDC transparentDC(m_data, trRect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ continue;
+
+ TransformationMatrix tr = m_data->m_transform;
+ tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height());
+
+ SelectObject(dc, GetStockObject(NULL_BRUSH));
+ HGDIOBJ oldPen = SelectObject(dc, pen);
+ i->platformPath()->strokePath(dc, &tr);
+ SelectObject(dc, oldPen);
+ }
+ DeleteObject(pen);
+ } else {
+ SelectObject(m_data->m_dc, GetStockObject(NULL_BRUSH));
+ HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
+ HGDIOBJ oldPen = SelectObject(m_data->m_dc, pen);
+ for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i)
+ i->platformPath()->strokePath(m_data->m_dc, &m_data->m_transform);
+ SelectObject(m_data->m_dc, oldPen);
+ DeleteObject(pen);
+ }
+}
+
+void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ const Vector<Gradient::ColorStop>& stops = gradient->getStops();
+ if (stops.isEmpty())
+ return;
+
+ size_t numStops = stops.size();
+ if (numStops == 1) {
+ const Gradient::ColorStop& stop = stops.first();
+ Color color(stop.red, stop.green, stop.blue, stop.alpha);
+ fillRect(r, color);
+ return;
+ }
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect intRect = enclosingIntRect(r);
+ IntRect rect = m_data->mapRect(intRect);
+ TransparentLayerDC transparentDC(m_data, rect, &intRect, 255, true);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+
+ rect.move(transparentDC.toShift());
+ FloatPoint fp0 = m_data->mapPoint(gradient->p0());
+ FloatPoint fp1 = m_data->mapPoint(gradient->p1());
+ IntPoint p0(stableRound(fp0.x()), stableRound(fp0.y()));
+ IntPoint p1(stableRound(fp1.x()), stableRound(fp1.y()));
+ p0 += transparentDC.toShift();
+ p1 += transparentDC.toShift();
+
+ if (gradient->isRadial()) {
+ if (g_radialGradientFiller) {
+ // FIXME: don't support 2D scaling at this time
+ double scale = (m_data->m_transform.a() + m_data->m_transform.d()) * 0.5;
+ float r0 = gradient->r0() * scale;
+ float r1 = gradient->r1() * scale;
+ g_radialGradientFiller(dc, rect, p0, p1, r0, r1, gradient->getStops());
+ return;
+ }
+ } else if (g_linearGradientFiller) {
+ g_linearGradientFiller(dc, rect, p0, p1, gradient->getStops());
+ return;
+ }
+
+ // Simple 1D linear solution that assumes p0 is on the top or left side, and p1 is on the right or bottom side
+ size_t numRects = (numStops - 1);
+ Vector<TRIVERTEX, 20> tv;
+ tv.resize(numRects * 2);
+ Vector<GRADIENT_RECT, 10> mesh;
+ mesh.resize(numRects);
+ int x = rect.x();
+ int y = rect.y();
+ int width = rect.width();
+ int height = rect.height();
+ FloatSize d = gradient->p1() - gradient->p0();
+ bool vertical = abs(d.height()) > abs(d.width());
+ for (size_t i = 0; i < numStops; ++i) {
+ const Gradient::ColorStop& stop = stops[i];
+ int iTv = i ? 2 * i - 1 : 0;
+ tv[iTv].Red = stop.red * 0xFFFF;
+ tv[iTv].Green = stop.green * 0xFFFF;
+ tv[iTv].Blue = stop.blue * 0xFFFF;
+ tv[iTv].Alpha = stop.alpha * 0xFFFF;
+ if (i) {
+ tv[iTv].x = vertical ? x + width: x + width * stop.stop;
+ tv[iTv].y = vertical ? y + height * stop.stop : y + height;
+ mesh[i - 1].UpperLeft = iTv - 1;
+ mesh[i - 1].LowerRight = iTv;
+ } else {
+ tv[iTv].x = x;
+ tv[iTv].y = y;
+ }
+
+ if (i && i < numRects) {
+ tv[iTv + 1] = tv[iTv];
+ if (vertical)
+ tv[iTv + 1].x = x;
+ else
+ tv[iTv + 1].y = y;
+ }
+ }
+
+ GradientFill(dc, tv.data(), tv.size(), mesh.data(), mesh.size(), vertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H);
+}
+
+TransformationMatrix GraphicsContext::getCTM() const
+{
+ return m_data->m_transform;
+}
+
+void GraphicsContext::clipToImageBuffer(const FloatRect&, const ImageBuffer*)
+{
+ notImplemented();
+}
+
+void GraphicsContext::fillRect(const FloatRect& rect)
+{
+ if (m_common->state.fillColorSpace == GradientColorSpace && m_common->state.fillGradient)
+ fillRect(rect, m_common->state.fillGradient.get());
+ else
+ fillRect(rect, fillColor());
+}
+
+void GraphicsContext::setPlatformShadow(const IntSize&, int, const Color&)
+{
+ notImplemented();
+}
+
+void GraphicsContext::clearPlatformShadow()
+{
+ notImplemented();
+}
+
+void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
+{
+ notImplemented();
+}
+
+static inline bool isCharVisible(UChar c)
+{
+ return c && c != zeroWidthSpace;
+}
+
+void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to)
+{
+ if (paintingDisabled() || !fillColor().alpha() || !m_data->m_opacity)
+ return;
+
+ bool mustSupportAlpha = m_data->hasAlpha();
+
+ if (!mustSupportAlpha && fillColor().alpha() == 0xFF && m_data->m_opacity >= 1.0) {
+ font.drawText(this, run, point, from, to);
+ return;
+ }
+
+ float oldOpacity = m_data->m_opacity;
+ m_data->m_opacity *= fillColor().alpha() / 255.0;
+
+ FloatRect textRect = font.selectionRectForText(run, point, font.height(), from, to);
+ textRect.setY(textRect.y() - font.ascent());
+ IntRect trRect = enclosingIntRect(m_data->mapRect(textRect));
+ RECT bmpRect;
+ AlphaPaintType alphaPaintType = mustSupportAlpha ? AlphaPaintOther : AlphaPaintNone;
+ if (RefPtr<SharedBitmap> bmp = m_data->getTransparentLayerBitmap(trRect, alphaPaintType, bmpRect, true, mustSupportAlpha)) {
+ {
+ GraphicsContext gc(0);
+ gc.setBitmap(bmp);
+ gc.scale(FloatSize(m_data->m_transform.a(), m_data->m_transform.d()));
+ font.drawText(&gc, run, IntPoint(0, font.ascent()), from, to);
+ }
+ unsigned key1, key2;
+ HDC memDC = bmp->getDC(&key1, &key2);
+ if (memDC) {
+ m_data->paintBackTransparentLayerBitmap(memDC, bmp.get(), trRect, alphaPaintType, bmpRect);
+ bmp->releaseDC(memDC, key1, key2);
+ }
+ }
+
+ m_data->m_opacity = oldOpacity;
+}
+
+void GraphicsContext::drawText(const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer,
+ int from, int numGlyphs, const FloatPoint& point)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ for (;;) {
+ if (!numGlyphs)
+ return;
+ if (isCharVisible(*glyphBuffer.glyphs(from)))
+ break;
+ ++from;
+ --numGlyphs;
+ }
+
+ double scaleX = m_data->m_transform.a();
+ double scaleY = m_data->m_transform.d();
+
+ int height = fontData->platformData().size() * scaleY;
+ int width = fontData->platformData().averageCharWidth() * scaleX;
+
+ if (!height || !width)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ HFONT hFont = height > 1
+ ? fontData->platformData().getScaledFontHandle(height, scaleX == scaleY ? 0 : width)
+ : 0;
+
+ FloatPoint startPoint(point.x(), point.y() - fontData->ascent());
+ FloatPoint trPoint = m_data->mapPoint(startPoint);
+ int y = stableRound(trPoint.y());
+
+ Color color = fillColor();
+ if (!color.alpha())
+ return;
+
+ COLORREF fontColor = RGB(color.red(), color.green(), color.blue());
+
+ if (!hFont) {
+ double offset = trPoint.x();
+ const GlyphBufferAdvance* advance = glyphBuffer.advances(from);
+ if (scaleX == 1.)
+ for (int i = 1; i < numGlyphs; ++i)
+ offset += *advance++;
+ else
+ for (int i = 1; i < numGlyphs; ++i)
+ offset += *advance++ * scaleX;
+
+ offset += width;
+
+ OwnPtr<HPEN> hPen(CreatePen(PS_DASH, 1, fontColor));
+ HGDIOBJ oldPen = SelectObject(m_data->m_dc, hPen.get());
+
+ MoveToEx(m_data->m_dc, stableRound(trPoint.x()), y, 0);
+ LineTo(m_data->m_dc, stableRound(offset), y);
+
+ SelectObject(m_data->m_dc, oldPen);
+ return;
+ }
+
+ IntSize shadowSize;
+ int shadowBlur = 0;
+ Color shadowColor;
+ bool hasShadow = textDrawingMode() == cTextFill
+ && getShadow(shadowSize, shadowBlur, shadowColor)
+ && shadowColor.alpha();
+ COLORREF shadowRGBColor;
+ FloatPoint trShadowPoint;
+ if (hasShadow) {
+ shadowRGBColor = RGB(shadowColor.red(), shadowColor.green(), shadowColor.blue());
+ trShadowPoint = m_data->mapPoint(startPoint + shadowSize);
+ }
+
+ HGDIOBJ hOldFont = SelectObject(m_data->m_dc, hFont);
+ COLORREF oldTextColor = GetTextColor(m_data->m_dc);
+ int oldTextAlign = GetTextAlign(m_data->m_dc);
+ SetTextAlign(m_data->m_dc, 0);
+
+ int oldBkMode = GetBkMode(m_data->m_dc);
+ SetBkMode(m_data->m_dc, TRANSPARENT);
+
+ if (numGlyphs > 1) {
+ double offset = trPoint.x();
+ Vector<int, 256> glyphSpace(numGlyphs);
+ Vector<UChar, 256> text(numGlyphs);
+ int* curSpace = glyphSpace.data();
+ UChar* curChar = text.data();
+ const UChar* srcChar = glyphBuffer.glyphs(from);
+ const UChar* const srcCharEnd = srcChar + numGlyphs;
+ *curChar++ = *srcChar++;
+ int firstOffset = stableRound(offset);
+ int lastOffset = firstOffset;
+ const GlyphBufferAdvance* advance = glyphBuffer.advances(from);
+ // FIXME: ExtTextOut() can flip over each word for RTL languages, even when TA_RTLREADING is off.
+ // (this can be GDI bug or font driver bug?)
+ // We are not clear how it processes characters and handles specified spaces. On the other side,
+ // our glyph buffer is already in the correct order for rendering. So, the solution is that we
+ // call ExtTextOut() for each single character when the text contains any RTL character.
+ // This solution is not perfect as it is slower than calling ExtTextOut() one time for all characters.
+ // Drawing characters one by one may be too slow.
+ bool drawOneByOne = false;
+ if (scaleX == 1.) {
+ for (; srcChar < srcCharEnd; ++srcChar) {
+ offset += *advance++;
+ int offsetInt = stableRound(offset);
+ if (isCharVisible(*srcChar)) {
+ if (!drawOneByOne && WTF::Unicode::direction(*srcChar) == WTF::Unicode::RightToLeft)
+ drawOneByOne = true;
+ *curChar++ = *srcChar;
+ *curSpace++ = offsetInt - lastOffset;
+ lastOffset = offsetInt;
+ }
+ }
+ } else {
+ for (; srcChar < srcCharEnd; ++srcChar) {
+ offset += *advance++ * scaleX;
+ int offsetInt = stableRound(offset);
+ if (isCharVisible(*srcChar)) {
+ if (!drawOneByOne && WTF::Unicode::direction(*srcChar) == WTF::Unicode::RightToLeft)
+ drawOneByOne = true;
+ *curChar++ = *srcChar;
+ *curSpace++ = offsetInt - lastOffset;
+ lastOffset = offsetInt;
+ }
+ }
+ }
+ numGlyphs = curChar - text.data();
+ if (hasShadow) {
+ SetTextColor(m_data->m_dc, shadowRGBColor);
+ if (drawOneByOne) {
+ int xShadow = firstOffset + stableRound(trShadowPoint.x() - trPoint.x());
+ int yShadow = stableRound(trShadowPoint.y());
+ for (int i = 0; i < numGlyphs; ++i) {
+ ExtTextOut(m_data->m_dc, xShadow, yShadow, 0, NULL, text.data() + i, 1, 0);
+ xShadow += glyphSpace[i];
+ }
+ } else
+ ExtTextOut(m_data->m_dc, firstOffset + stableRound(trShadowPoint.x() - trPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, text.data(), numGlyphs, glyphSpace.data());
+ }
+ SetTextColor(m_data->m_dc, fontColor);
+ if (drawOneByOne) {
+ int x = firstOffset;
+ for (int i = 0; i < numGlyphs; ++i) {
+ ExtTextOut(m_data->m_dc, x, y, 0, NULL, text.data() + i, 1, 0);
+ x += glyphSpace[i];
+ }
+ } else
+ ExtTextOut(m_data->m_dc, firstOffset, y, 0, NULL, text.data(), numGlyphs, glyphSpace.data());
+ } else {
+ UChar c = *glyphBuffer.glyphs(from);
+ if (hasShadow) {
+ SetTextColor(m_data->m_dc, shadowRGBColor);
+ ExtTextOut(m_data->m_dc, stableRound(trShadowPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, &c, 1, 0);
+ }
+ SetTextColor(m_data->m_dc, fontColor);
+ ExtTextOut(m_data->m_dc, stableRound(trPoint.x()), y, 0, NULL, &c, 1, 0);
+ }
+
+ SetTextAlign(m_data->m_dc, oldTextAlign);
+ SetTextColor(m_data->m_dc, oldTextColor);
+ SetBkMode(m_data->m_dc, oldBkMode);
+ SelectObject(m_data->m_dc, hOldFont);
+}
+
+void GraphicsContext::drawFrameControl(const IntRect& rect, unsigned type, unsigned state)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ const int boxWidthBest = 8;
+ const int boxHeightBest = 8;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = m_data->mapRect(rect);
+ TransparentLayerDC transparentDC(m_data, trRect, &rect, 255, true);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ RECT rectWin = trRect;
+
+ if ((rectWin.right - rectWin.left) < boxWidthBest) {
+ RefPtr<SharedBitmap> bmp = SharedBitmap::createInstance(true, boxWidthBest, boxHeightBest, true);
+ SharedBitmap::DCHolder memDC(bmp.get());
+ if (memDC.get()) {
+ RECT tempRect = {0, 0, boxWidthBest, boxHeightBest};
+ DrawFrameControl(memDC.get(), &tempRect, type, state);
+
+ ::StretchBlt(dc, rectWin.left, rectWin.top, rectWin.right - rectWin.left, rectWin.bottom - rectWin.top, memDC.get(), 0, 0, boxWidthBest, boxHeightBest, SRCCOPY);
+ return;
+ }
+ }
+
+ DrawFrameControl(dc, &rectWin, type, state);
+}
+
+void GraphicsContext::drawFocusRect(const IntRect& rect)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = m_data->mapRect(rect);
+ TransparentLayerDC transparentDC(m_data, trRect, &rect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ RECT rectWin = trRect;
+ DrawFocusRect(dc, &rectWin);
+}
+
+void GraphicsContext::paintTextField(const IntRect& rect, unsigned state)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect trRect = m_data->mapRect(rect);
+ TransparentLayerDC transparentDC(m_data, trRect, &rect);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+
+ RECT rectWin = trRect;
+ DrawEdge(dc, &rectWin, EDGE_ETCHED, BF_RECT | BF_ADJUST);
+ FillRect(dc, &rectWin, reinterpret_cast<HBRUSH>(((state & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW) + 1));
+}
+
+void GraphicsContext::drawBitmap(SharedBitmap* bmp, const IntRect& dstRectIn, const IntRect& srcRect, CompositeOperator compositeOp)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect dstRect = m_data->mapRect(dstRectIn);
+ TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ dstRect.move(transparentDC.toShift());
+
+ bmp->draw(dc, dstRect, srcRect, compositeOp);
+
+ if (bmp->is16bit())
+ transparentDC.fillAlphaChannel();
+}
+
+void GraphicsContext::drawBitmapPattern(SharedBitmap* bmp, const FloatRect& tileRectIn, const TransformationMatrix& patternTransform,
+ const FloatPoint& phase, CompositeOperator op, const FloatRect& destRectIn, const IntSize& origSourceSize)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect intDstRect = enclosingIntRect(destRectIn);
+ IntRect trRect = m_data->mapRect(intDstRect);
+ TransparentLayerDC transparentDC(m_data, trRect, &intDstRect, 255, true);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ trRect.move(transparentDC.toShift());
+ FloatRect movedDstRect = m_data->m_transform.inverse().mapRect(FloatRect(trRect));
+ FloatSize moved(movedDstRect.location() - destRectIn.location());
+ TransformationMatrix transform = m_data->m_transform;
+ transform.translate(moved.width(), moved.height());
+
+ bmp->drawPattern(dc, transform, tileRectIn, patternTransform, phase, op, destRectIn, origSourceSize);
+
+ if (!bmp->hasAlpha())
+ transparentDC.fillAlphaChannel();
+}
+
+void GraphicsContext::drawIcon(HICON icon, const IntRect& dstRectIn, UINT flags)
+{
+ if (!m_data->m_opacity)
+ return;
+
+ ScopeDCProvider dcProvider(m_data);
+ if (!m_data->m_dc)
+ return;
+
+ IntRect dstRect = m_data->mapRect(dstRectIn);
+ TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true);
+ HDC dc = transparentDC.hdc();
+ if (!dc)
+ return;
+ dstRect.move(transparentDC.toShift());
+
+ DrawIconEx(dc, dstRect.x(), dstRect.y(), icon, dstRect.width(), dstRect.height(), 0, NULL, flags);
+}
+
+void GraphicsContext::setPlatformShouldAntialias(bool)
+{
+ notImplemented();
+}
+
+void GraphicsContext::setLineDash(const DashArray&, float)
+{
+ notImplemented();
+}
+
+void GraphicsContext::clipPath(WindRule)
+{
+ notImplemented();
+}
+
+} // namespace WebCore