diff options
author | Ben Murdoch <benm@google.com> | 2009-08-11 17:01:47 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2009-08-11 18:21:02 +0100 |
commit | 0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5 (patch) | |
tree | 2943df35f62d885c89d01063cc528dd73b480fea /WebCore/platform/graphics/wince/GraphicsContextWince.cpp | |
parent | 7e7a70bfa49a1122b2597a1e6367d89eb4035eca (diff) | |
download | external_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.cpp | 1930 |
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 |