1/*
2 *  Copyright (C) 2007-2009 Torch Mobile Inc.
3 *  Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Library General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Library General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Library General Public License
16 *  along with this library; see the file COPYING.LIB.  If not, write to
17 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 *  Boston, MA 02110-1301, USA.
19 *
20 */
21
22#include "config.h"
23#include "GraphicsContext.h"
24
25#include "AffineTransform.h"
26#include "Font.h"
27#include "GDIExtras.h"
28#include "GlyphBuffer.h"
29#include "Gradient.h"
30#include "NotImplemented.h"
31#include "Path.h"
32#include "PlatformPathWinCE.h"
33#include "SharedBitmap.h"
34#include "SimpleFontData.h"
35#include <windows.h>
36#include <wtf/OwnPtr.h>
37#include <wtf/unicode/CharacterNames.h>
38
39namespace WebCore {
40
41typedef void (*FuncGradientFillRectLinear)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, const Vector<Gradient::ColorStop>& stops);
42typedef void (*FuncGradientFillRectRadial)(HDC hdc, const IntRect& r, const IntPoint& p0, const IntPoint& p1, float r0, float r1, const Vector<Gradient::ColorStop>& stops);
43FuncGradientFillRectLinear g_linearGradientFiller = 0;
44FuncGradientFillRectRadial g_radialGradientFiller = 0;
45
46static inline bool isZero(double d)
47{
48    return d > 0 ? d <= 1.E-10 : d >= -1.E-10;
49}
50
51// stableRound rounds -0.5 to 0, where lround rounds -0.5 to -1.
52static inline int stableRound(double d)
53{
54    if (d > 0)
55        return static_cast<int>(d + 0.5);
56
57    int i = static_cast<int>(d);
58    return i - d > 0.5 ? i - 1 : i;
59}
60
61// Unlike enclosingIntRect(), this function does strict rounding.
62static inline IntRect roundRect(const FloatRect& r)
63{
64    return IntRect(stableRound(r.x()), stableRound(r.y()), stableRound(r.maxX()) - stableRound(r.x()), stableRound(r.maxY()) - stableRound(r.y()));
65}
66
67// Rotation transformation
68class RotationTransform {
69public:
70    RotationTransform()
71        : m_cosA(1.)
72        , m_sinA(0.)
73        , m_preShiftX(0)
74        , m_preShiftY(0)
75        , m_postShiftX(0)
76        , m_postShiftY(0)
77    {
78    }
79    RotationTransform operator-() const
80    {
81        RotationTransform rtn;
82        rtn.m_cosA = m_cosA;
83        rtn.m_sinA = -m_sinA;
84        rtn.m_preShiftX = m_postShiftX;
85        rtn.m_preShiftY = m_postShiftY;
86        rtn.m_postShiftX = m_preShiftX;
87        rtn.m_postShiftY = m_preShiftY;
88        return rtn;
89    }
90    void map(double x1, double y1, double* x2, double* y2) const
91    {
92        x1 += m_preShiftX;
93        y1 += m_preShiftY;
94        *x2 = x1 * m_cosA + y1 * m_sinA + m_postShiftX;
95        *y2 = y1 * m_cosA - x1 * m_sinA + m_postShiftY;
96    }
97    void map(int x1, int y1, int* x2, int* y2) const
98    {
99        x1 += m_preShiftX;
100        y1 += m_preShiftY;
101        *x2 = stableRound(x1 * m_cosA + y1 * m_sinA) + m_postShiftX;
102        *y2 = stableRound(y1 * m_cosA - x1 * m_sinA) + m_postShiftY;
103    }
104
105    double m_cosA;
106    double m_sinA;
107    int m_preShiftX;
108    int m_preShiftY;
109    int m_postShiftX;
110    int m_postShiftY;
111};
112
113template<class T> static inline IntPoint mapPoint(const IntPoint& p, const T& t)
114{
115    int x, y;
116    t.map(p.x(), p.y(), &x, &y);
117    return IntPoint(x, y);
118}
119
120template<class T> static inline FloatPoint mapPoint(const FloatPoint& p, const T& t)
121{
122    double x, y;
123    t.map(p.x(), p.y(), &x, &y);
124    return FloatPoint(static_cast<float>(x), static_cast<float>(y));
125}
126
127template<class Transform, class Rect, class Value> static inline Rect mapRect(const Rect& rect, const Transform& transform)
128{
129    Value x[4], y[4];
130    Value l, t, r, b;
131    r = rect.maxX() - 1;
132    b = rect.maxY() - 1;
133    transform.map(rect.x(), rect.y(), x, y);
134    transform.map(rect.x(), b, x + 1, y + 1);
135    transform.map(r, b, x + 2, y + 2);
136    transform.map(r, rect.y(), x + 3, y + 3);
137    l = r = x[3];
138    t = b = y[3];
139    for (int i = 0; i < 3; ++i) {
140        if (x[i] < l)
141            l = x[i];
142        else if (x[i] > r)
143            r = x[i];
144
145        if (y[i] < t)
146            t = y[i];
147        else if (y[i] > b)
148            b = y[i];
149    }
150
151    return IntRect(l, t, r - l + 1, b - t + 1);
152}
153
154template<class T> static inline IntRect mapRect(const IntRect& rect, const T& transform)
155{
156    return mapRect<T, IntRect, int>(rect, transform);
157}
158
159template<class T> static inline FloatRect mapRect(const FloatRect& rect, const T& transform)
160{
161    return mapRect<T, FloatRect, double>(rect, transform);
162}
163
164class GraphicsContextPlatformPrivateData {
165public:
166    GraphicsContextPlatformPrivateData()
167        : m_transform()
168        , m_opacity(1.0)
169    {
170    }
171
172    AffineTransform m_transform;
173    float m_opacity;
174};
175
176enum AlphaPaintType {
177    AlphaPaintNone,
178    AlphaPaintImage,
179    AlphaPaintOther,
180};
181
182class GraphicsContextPlatformPrivate : public GraphicsContextPlatformPrivateData {
183public:
184    GraphicsContextPlatformPrivate(HDC dc)
185        : m_dc(dc)
186    {
187    }
188    ~GraphicsContextPlatformPrivate()
189    {
190        while (!m_backupData.isEmpty())
191            restore();
192    }
193
194    void translate(float x, float y)
195    {
196        m_transform.translate(x, y);
197    }
198
199    void scale(const FloatSize& size)
200    {
201        m_transform.scaleNonUniform(size.width(), size.height());
202    }
203
204    void rotate(float radians)
205    {
206        m_transform.rotate(rad2deg(radians));
207    }
208
209    void concatCTM(const AffineTransform& transform)
210    {
211        m_transform *= transform;
212    }
213
214    void setCTM(const AffineTransform& transform)
215    {
216        m_transform = transform;
217    }
218
219    IntRect mapRect(const IntRect& rect) const
220    {
221        return m_transform.mapRect(rect);
222    }
223
224    FloatRect mapRect(const FloatRect& rect) const
225    {
226        return m_transform.mapRect(rect);
227    }
228
229    IntPoint mapPoint(const IntPoint& point) const
230    {
231        return m_transform.mapPoint(point);
232    }
233
234    FloatPoint mapPoint(const FloatPoint& point) const
235    {
236        return m_transform.mapPoint(point);
237    }
238
239    FloatSize mapSize(const FloatSize& size) const
240    {
241        double w, h;
242        m_transform.map(size.width(), size.height(), w, h);
243        return FloatSize(static_cast<float>(w), static_cast<float>(h));
244    }
245
246    void save()
247    {
248        if (m_dc)
249            SaveDC(m_dc);
250
251        m_backupData.append(*static_cast<GraphicsContextPlatformPrivateData*>(this));
252    }
253
254    void restore()
255    {
256        if (m_backupData.isEmpty())
257            return;
258
259        if (m_dc)
260            RestoreDC(m_dc, -1);
261
262        GraphicsContextPlatformPrivateData::operator=(m_backupData.last());
263        m_backupData.removeLast();
264    }
265
266    bool hasAlpha() const { return m_bitmap && m_bitmap->hasAlpha(); }
267
268    PassRefPtr<SharedBitmap> getTransparentLayerBitmap(IntRect& origRect, AlphaPaintType alphaPaint, RECT& bmpRect, bool checkClipBox, bool force) const
269    {
270        if (m_opacity <= 0)
271            return 0;
272
273        if (force || m_opacity < 1.)  {
274            if (checkClipBox) {
275                RECT clipBox;
276                int clipType = GetClipBox(m_dc, &clipBox);
277                if (clipType == SIMPLEREGION || clipType == COMPLEXREGION)
278                    origRect.intersect(clipBox);
279                if (origRect.isEmpty())
280                    return 0;
281            }
282
283            RefPtr<SharedBitmap> bmp = SharedBitmap::create(origRect.size(), alphaPaint == AlphaPaintNone ? BitmapInfo::BitCount16 : BitmapInfo::BitCount32, false);
284            SetRect(&bmpRect, 0, 0, origRect.width(), origRect.height());
285            if (bmp) {
286                switch (alphaPaint) {
287                case AlphaPaintNone:
288                case AlphaPaintImage:
289                    {
290                        SharedBitmap::DCHolder dc(bmp.get());
291                        if (dc.get()) {
292                            BitBlt(dc.get(), 0, 0, origRect.width(), origRect.height(), m_dc, origRect.x(), origRect.y(), SRCCOPY);
293                            if (bmp->is32bit() && (!m_bitmap || m_bitmap->is16bit())) {
294                                // Set alpha channel
295                                unsigned* pixels = (unsigned*)bmp->bytes();
296                                const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels();
297                                while (pixels < pixelsEnd) {
298                                    *pixels |= 0xFF000000;
299                                    ++pixels;
300                                }
301                            }
302                            return bmp;
303                        }
304                    }
305                    break;
306                //case AlphaPaintOther:
307                default:
308                    memset(bmp->bytes(), 0xFF, bmp->bitmapInfo().numPixels() * 4);
309                    return bmp;
310                    break;
311                }
312            }
313        }
314
315        bmpRect = origRect;
316        return 0;
317    }
318
319    void paintBackTransparentLayerBitmap(HDC hdc, SharedBitmap* bmp, const IntRect& origRect, AlphaPaintType alphaPaint, const RECT& bmpRect)
320    {
321        if (hdc == m_dc)
322            return;
323
324        if (alphaPaint == AlphaPaintOther && hasAlphaBlendSupport()) {
325            ASSERT(bmp && bmp->bytes() && bmp->is32bit());
326            unsigned* pixels = (unsigned*)bmp->bytes();
327            const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels();
328            while (pixels < pixelsEnd) {
329                *pixels ^= 0xFF000000;
330                ++pixels;
331            }
332        }
333        if ((m_opacity < 1. || alphaPaint == AlphaPaintOther) && hasAlphaBlendSupport()) {
334            const BLENDFUNCTION blend = { AC_SRC_OVER, 0
335                , m_opacity >= 1. ? 255 : (BYTE)(m_opacity * 255)
336                , alphaPaint == AlphaPaintNone ? 0 : AC_SRC_ALPHA };
337            bool success = alphaBlendIfSupported(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, blend);
338            ASSERT_UNUSED(success, success);
339        } else
340            StretchBlt(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, SRCCOPY);
341    }
342
343    HDC m_dc;
344    RefPtr<SharedBitmap> m_bitmap;
345    Vector<GraphicsContextPlatformPrivateData> m_backupData;
346};
347
348static PassOwnPtr<HPEN> createPen(const Color& col, double fWidth, StrokeStyle style)
349{
350    int width = stableRound(fWidth);
351    if (width < 1)
352        width = 1;
353
354    int penStyle = PS_NULL;
355    switch (style) {
356        case SolidStroke:
357            penStyle = PS_SOLID;
358            break;
359        case DottedStroke:  // not supported on Windows CE
360        case DashedStroke:
361            penStyle = PS_DASH;
362            width = 1;
363            break;
364        default:
365            break;
366    }
367
368    return adoptPtr(CreatePen(penStyle, width, RGB(col.red(), col.green(), col.blue())));
369}
370
371static inline PassOwnPtr<HBRUSH> createBrush(const Color& col)
372{
373    return adoptPtr(CreateSolidBrush(RGB(col.red(), col.green(), col.blue())));
374}
375
376template <typename PixelType, bool Is16bit> static void _rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform)
377{
378    int destW = destBmp->width();
379    int destH = destBmp->height();
380    int sourceW = sourceBmp->width();
381    int sourceH = sourceBmp->height();
382    PixelType* dest = (PixelType*)destBmp->bytes();
383    const PixelType* source = (const PixelType*)sourceBmp->bytes();
384    int padding;
385    int paddedSourceW;
386    if (Is16bit) {
387        padding = destW & 1;
388        paddedSourceW = sourceW + (sourceW & 1);
389    } else {
390        padding = 0;
391        paddedSourceW = sourceW;
392    }
393    if (isZero(transform.m_sinA)) {
394        int cosA = transform.m_cosA > 0 ? 1 : -1;
395        for (int y = 0; y < destH; ++y) {
396            for (int x = 0; x < destW; ++x) {
397                int x1 = x + transform.m_preShiftX;
398                int y1 = y + transform.m_preShiftY;
399                int srcX = x1 * cosA + transform.m_postShiftX;
400                int srcY = y1 * cosA - transform.m_postShiftY;
401                if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH)
402                    *dest++ = source[srcY * paddedSourceW + srcX] | 0xFF000000;
403                else
404                    *dest++ |= 0xFF;
405            }
406            dest += padding;
407        }
408    } else if (isZero(transform.m_cosA)) {
409        int sinA = transform.m_sinA > 0 ? 1 : -1;
410        for (int y = 0; y < destH; ++y) {
411            for (int x = 0; x < destW; ++x) {
412                int x1 = x + transform.m_preShiftX;
413                int y1 = y + transform.m_preShiftY;
414                int srcX = y1 * sinA + transform.m_postShiftX;
415                int srcY = -x1 * sinA + transform.m_postShiftY;
416                if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH)
417                    *dest++ = source[srcY * paddedSourceW + srcX];
418            }
419            dest += padding;
420        }
421    } else {
422        for (int y = 0; y < destH; ++y) {
423            for (int x = 0; x < destW; ++x) {
424                // FIXME: for best quality, we should get weighted sum of four neighbours,
425                // but that will be too expensive
426                int srcX, srcY;
427                transform.map(x, y, &srcX, &srcY);
428                if (srcX >= 0 && srcX <= sourceW && srcY >= 0 && srcY <= sourceH)
429                    *dest++ = source[srcY * paddedSourceW + srcX];
430            }
431            dest += padding;
432        }
433    }
434}
435
436static void rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform)
437{
438    ASSERT(destBmp->is16bit() == sourceBmp->is16bit());
439    if (destBmp->is16bit())
440        _rotateBitmap<unsigned short, true>(destBmp, sourceBmp, transform);
441    else
442        _rotateBitmap<unsigned, false>(destBmp, sourceBmp, transform);
443}
444
445class TransparentLayerDC {
446    WTF_MAKE_NONCOPYABLE(TransparentLayerDC);
447public:
448    TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform = 0, int alpha = 255, bool paintImage = false);
449    ~TransparentLayerDC();
450
451    HDC hdc() const { return m_memDc; }
452    const RECT& rect() const { return m_bmpRect; }
453    IntSize toShift() const { return IntSize(m_bmpRect.left - m_origRect.x(), m_bmpRect.top - m_origRect.y()); }
454    void fillAlphaChannel();
455
456private:
457    GraphicsContextPlatformPrivate* m_data;
458    IntRect m_origRect;
459    IntRect m_rotatedOrigRect;
460    HDC m_memDc;
461    RefPtr<SharedBitmap> m_bitmap;
462    RefPtr<SharedBitmap> m_rotatedBitmap;
463    RECT m_bmpRect;
464    unsigned m_key;
465    RotationTransform m_rotation;
466    float m_oldOpacity;
467    AlphaPaintType m_alphaPaintType;
468};
469
470TransparentLayerDC::TransparentLayerDC(GraphicsContextPlatformPrivate* data, IntRect& origRect, const IntRect* rectBeforeTransform, int alpha, bool paintImage)
471: m_data(data)
472, m_origRect(origRect)
473, m_oldOpacity(data->m_opacity)
474// m_key1 and m_key2 are not initalized here. They are used only in the case that
475// SharedBitmap::getDC() is called, I.E., when m_bitmap is not null.
476{
477    m_data->m_opacity *= alpha / 255.;
478    bool mustCreateLayer;
479    if (!m_data->hasAlpha()) {
480        mustCreateLayer = false;
481        m_alphaPaintType = AlphaPaintNone;
482    } else {
483        mustCreateLayer = true;
484        m_alphaPaintType = paintImage ? AlphaPaintImage : AlphaPaintOther;
485    }
486    if (rectBeforeTransform && !isZero(m_data->m_transform.b())) {
487        m_rotatedOrigRect = origRect;
488        m_rotatedBitmap = m_data->getTransparentLayerBitmap(m_rotatedOrigRect, m_alphaPaintType, m_bmpRect, false, true);
489        if (m_rotatedBitmap) {
490            double a = m_data->m_transform.a();
491            double b = m_data->m_transform.b();
492            double c = _hypot(a, b);
493            m_rotation.m_cosA = a / c;
494            m_rotation.m_sinA = b / c;
495
496            int centerX = origRect.x() + origRect.width() / 2;
497            int centerY = origRect.y() + origRect.height() / 2;
498            m_rotation.m_preShiftX = -centerX;
499            m_rotation.m_preShiftY = -centerY;
500            m_rotation.m_postShiftX = centerX;
501            m_rotation.m_postShiftY = centerY;
502
503            m_origRect = mapRect(m_rotatedOrigRect, m_rotation);
504
505            m_rotation.m_preShiftX += m_rotatedOrigRect.x();
506            m_rotation.m_preShiftY += m_rotatedOrigRect.y();
507            m_rotation.m_postShiftX -= m_origRect.x();
508            m_rotation.m_postShiftY -= m_origRect.y();
509
510            FloatPoint topLeft = m_data->m_transform.mapPoint(FloatPoint(rectBeforeTransform->location()));
511            FloatPoint topRight(rectBeforeTransform->maxX() - 1, rectBeforeTransform->y());
512            topRight = m_data->m_transform.mapPoint(topRight);
513            FloatPoint bottomLeft(rectBeforeTransform->x(), rectBeforeTransform->maxY() - 1);
514            bottomLeft = m_data->m_transform.mapPoint(bottomLeft);
515            FloatSize sideTop = topRight - topLeft;
516            FloatSize sideLeft = bottomLeft - topLeft;
517            float width = _hypot(sideTop.width() + 1, sideTop.height() + 1);
518            float height = _hypot(sideLeft.width() + 1, sideLeft.height() + 1);
519
520            origRect.inflateX(stableRound((width - origRect.width()) * 0.5));
521            origRect.inflateY(stableRound((height - origRect.height()) * 0.5));
522
523            m_bitmap = SharedBitmap::create(m_origRect.size(), m_rotatedBitmap->is16bit() ? BitmapInfo::BitCount16 : BitmapInfo::BitCount32, true);
524            if (m_bitmap)
525                rotateBitmap(m_bitmap.get(), m_rotatedBitmap.get(), -m_rotation);
526            else
527                m_rotatedBitmap = 0;
528        }
529    } else
530        m_bitmap = m_data->getTransparentLayerBitmap(m_origRect, m_alphaPaintType, m_bmpRect, true, mustCreateLayer);
531    if (m_bitmap)
532        m_memDc = m_bitmap->getDC(&m_key);
533    else
534        m_memDc = m_data->m_dc;
535}
536
537TransparentLayerDC::~TransparentLayerDC()
538{
539    if (m_rotatedBitmap) {
540        m_bitmap->releaseDC(m_memDc, m_key);
541        m_key = 0;
542        rotateBitmap(m_rotatedBitmap.get(), m_bitmap.get(), m_rotation);
543        m_memDc = m_rotatedBitmap->getDC(&m_key);
544        m_data->paintBackTransparentLayerBitmap(m_memDc, m_rotatedBitmap.get(), m_rotatedOrigRect, m_alphaPaintType, m_bmpRect);
545        m_rotatedBitmap->releaseDC(m_memDc, m_key);
546    } else if (m_bitmap) {
547        m_data->paintBackTransparentLayerBitmap(m_memDc, m_bitmap.get(), m_origRect, m_alphaPaintType, m_bmpRect);
548        m_bitmap->releaseDC(m_memDc, m_key);
549    }
550    m_data->m_opacity = m_oldOpacity;
551}
552
553void TransparentLayerDC::fillAlphaChannel()
554{
555    if (!m_bitmap || !m_bitmap->is32bit())
556        return;
557
558    unsigned* pixels = (unsigned*)m_bitmap->bytes();
559    const unsigned* const pixelsEnd = pixels + m_bitmap->bitmapInfo().numPixels();
560    while (pixels < pixelsEnd) {
561        *pixels |= 0xFF000000;
562        ++pixels;
563    }
564}
565
566class ScopeDCProvider {
567    WTF_MAKE_NONCOPYABLE(ScopeDCProvider);
568public:
569    explicit ScopeDCProvider(GraphicsContextPlatformPrivate* data)
570        : m_data(data)
571    {
572        if (m_data->m_bitmap)
573            m_data->m_dc = m_data->m_bitmap->getDC(&m_key);
574    }
575    ~ScopeDCProvider()
576    {
577        if (m_data->m_bitmap) {
578            m_data->m_bitmap->releaseDC(m_data->m_dc, m_key);
579            m_data->m_dc = 0;
580        }
581    }
582private:
583    GraphicsContextPlatformPrivate* m_data;
584    unsigned m_key;
585};
586
587
588void GraphicsContext::platformInit(PlatformGraphicsContext* dc)
589{
590    m_data = new GraphicsContextPlatformPrivate(dc);
591}
592
593void GraphicsContext::platformDestroy()
594{
595    delete m_data;
596}
597
598void GraphicsContext::setBitmap(PassRefPtr<SharedBitmap> bmp)
599{
600    ASSERT(!m_data->m_dc);
601    m_data->m_bitmap = bmp;
602}
603
604HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
605{
606    // FIXME: Add support for AlphaBlend.
607    ASSERT(!supportAlphaBlend);
608    return m_data->m_dc;
609}
610
611void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap)
612{
613}
614
615void GraphicsContext::savePlatformState()
616{
617    m_data->save();
618}
619
620void GraphicsContext::restorePlatformState()
621{
622    m_data->restore();
623}
624
625void GraphicsContext::drawRect(const IntRect& rect)
626{
627    if (!m_data->m_opacity || paintingDisabled() || rect.isEmpty())
628        return;
629
630    ScopeDCProvider dcProvider(m_data);
631    if (!m_data->m_dc)
632        return;
633
634    IntRect trRect = m_data->mapRect(rect);
635    TransparentLayerDC transparentDC(m_data, trRect, &rect);
636    HDC dc = transparentDC.hdc();
637    if (!dc)
638        return;
639    trRect.move(transparentDC.toShift());
640
641    OwnPtr<HBRUSH> brush;
642    HGDIOBJ oldBrush;
643    if (fillColor().alpha()) {
644        brush = createBrush(fillColor());
645        oldBrush = SelectObject(dc, brush.get());
646    } else
647        oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
648
649    OwnPtr<HPEN> pen;
650    HGDIOBJ oldPen;
651    if (strokeStyle() != NoStroke) {
652        pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
653        oldPen = SelectObject(dc, pen.get());
654    } else
655        oldPen = SelectObject(dc, GetStockObject(NULL_PEN));
656
657    if (brush || pen) {
658        if (trRect.width() <= 0)
659            trRect.setWidth(1);
660        if (trRect.height() <= 0)
661            trRect.setHeight(1);
662
663        Rectangle(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY());
664    }
665
666    SelectObject(dc, oldPen);
667    SelectObject(dc, oldBrush);
668}
669
670void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
671{
672    if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke || !strokeColor().alpha())
673        return;
674
675    ScopeDCProvider dcProvider(m_data);
676    if (!m_data->m_dc)
677        return;
678
679    IntPoint trPoint1 = m_data->mapPoint(point1);
680    IntPoint trPoint2 = m_data->mapPoint(point2);
681
682    IntRect lineRect(trPoint1, trPoint2 - trPoint1);
683    lineRect.setHeight(lineRect.height() + strokeThickness());
684    TransparentLayerDC transparentDC(m_data, lineRect, 0, strokeColor().alpha());
685    HDC dc = transparentDC.hdc();
686    if (!dc)
687        return;
688    trPoint1 += transparentDC.toShift();
689    trPoint2 += transparentDC.toShift();
690
691    OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
692    HGDIOBJ oldPen = SelectObject(dc, pen.get());
693
694    MoveToEx(dc, trPoint1.x(), trPoint1.y(), 0);
695    LineTo(dc, trPoint2.x(), trPoint2.y());
696
697    SelectObject(dc, oldPen);
698}
699
700void GraphicsContext::drawEllipse(const IntRect& rect)
701{
702    if (!m_data->m_opacity || paintingDisabled() || (!fillColor().alpha() && strokeStyle() == NoStroke))
703        return;
704
705    ScopeDCProvider dcProvider(m_data);
706    if (!m_data->m_dc)
707        return;
708
709    IntRect trRect = m_data->mapRect(rect);
710    TransparentLayerDC transparentDC(m_data, trRect, &rect);
711    HDC dc = transparentDC.hdc();
712    if (!dc)
713        return;
714    trRect.move(transparentDC.toShift());
715
716    OwnPtr<HBRUSH> brush;
717    HGDIOBJ oldBrush;
718    if (fillColor().alpha()) {
719        brush = createBrush(fillColor());
720        oldBrush = SelectObject(dc, brush.get());
721    } else
722        oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
723
724    OwnPtr<HPEN> pen;
725    HGDIOBJ oldPen = 0;
726    if (strokeStyle() != NoStroke) {
727        pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
728        oldPen = SelectObject(dc, pen.get());
729    } else
730        oldPen = SelectObject(dc, GetStockObject(NULL_PEN));
731
732    if (brush || pen)
733        Ellipse(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY());
734
735    SelectObject(dc, oldPen);
736    SelectObject(dc, oldBrush);
737}
738
739static inline bool equalAngle(double a, double b)
740{
741    return fabs(a - b) < 1E-5;
742}
743
744void getEllipsePointByAngle(double angle, double a, double b, float& x, float& y)
745{
746    while (angle < 0)
747        angle += 2 * piDouble;
748    while (angle >= 2 * piDouble)
749        angle -= 2 * piDouble;
750
751    if (equalAngle(angle, 0) || equalAngle(angle, 2 * piDouble)) {
752        x = a;
753        y = 0;
754    } else if (equalAngle(angle, piDouble)) {
755        x = -a;
756        y = 0;
757    } else if (equalAngle(angle, .5 * piDouble)) {
758        x = 0;
759        y = b;
760    } else if (equalAngle(angle, 1.5 * piDouble)) {
761        x = 0;
762        y = -b;
763    } else {
764        double k = tan(angle);
765        double sqA = a * a;
766        double sqB = b * b;
767        double tmp = 1. / (1. / sqA + (k * k) / sqB);
768        tmp = tmp <= 0 ? 0 : sqrt(tmp);
769        if (angle > .5 * piDouble && angle < 1.5 * piDouble)
770            tmp = -tmp;
771        x = tmp;
772
773        k = tan(.5 * piDouble - angle);
774        tmp = 1. / ((k * k) / sqA + 1 / sqB);
775        tmp = tmp <= 0 ? 0 : sqrt(tmp);
776        if (angle > piDouble)
777            tmp = -tmp;
778        y = tmp;
779    }
780}
781
782void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSpan)
783{
784    if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke || rect.isEmpty())
785        return;
786
787    ScopeDCProvider dcProvider(m_data);
788    if (!m_data->m_dc)
789        return;
790
791    IntRect trRect = m_data->mapRect(rect);
792    TransparentLayerDC transparentDC(m_data, trRect, &rect);
793    HDC dc = transparentDC.hdc();
794    if (!dc)
795        return;
796    trRect.move(transparentDC.toShift());
797
798    OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
799    HGDIOBJ oldPen = SelectObject(dc, pen.get());
800
801    double a = trRect.width() * 0.5;
802    double b = trRect.height() * 0.5;
803    int centerX = stableRound(trRect.x() + a);
804    int centerY = stableRound(trRect.y() + b);
805    float fstartX, fstartY, fendX, fendY;
806    int startX, startY, endX, endY;
807    getEllipsePointByAngle(deg2rad((double)startAngle), a, b, fstartX, fstartY);
808    getEllipsePointByAngle(deg2rad((double)startAngle + angleSpan), a, b, fendX, fendY);
809    startX = stableRound(fstartX);
810    startY = stableRound(fstartY);
811    endX = stableRound(fendX);
812    endY = stableRound(fendY);
813
814    startX += centerX;
815    startY = centerY - startY;
816    endX += centerX;
817    endY = centerY - endY;
818    RECT clipRect;
819    if (startX < endX) {
820        clipRect.left = startX;
821        clipRect.right = endX;
822    } else {
823        clipRect.left = endX;
824        clipRect.right = startX;
825    }
826    if (startY < endY) {
827        clipRect.top = startY;
828        clipRect.bottom = endY;
829    } else {
830        clipRect.top = endY;
831        clipRect.bottom = startY;
832    }
833
834    OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
835    bool newClip;
836    if (GetClipRgn(dc, clipRgn.get()) <= 0) {
837        newClip = true;
838        clipRgn.set(CreateRectRgn(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom));
839        SelectClipRgn(dc, clipRgn.get());
840    } else {
841        newClip = false;
842        IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
843    }
844
845    HGDIOBJ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
846    Ellipse(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY());
847    SelectObject(dc, oldBrush);
848
849    if (newClip)
850        SelectClipRgn(dc, 0);
851    else
852        SelectClipRgn(dc, clipRgn.get());
853
854    SelectObject(dc, oldPen);
855}
856
857void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias)
858{
859    if (!m_data->m_opacity || paintingDisabled() || npoints <= 1 || !points)
860        return;
861
862    ScopeDCProvider dcProvider(m_data);
863    if (!m_data->m_dc)
864        return;
865
866    Vector<POINT, 20> winPoints(npoints);
867    FloatPoint trPoint = m_data->mapPoint(points[0]);
868    winPoints[0].x = stableRound(trPoint.x());
869    winPoints[0].y = stableRound(trPoint.y());
870    RECT rect = { winPoints[0].x, winPoints[0].y, winPoints[0].x, winPoints[0].y };
871    for (size_t i = 1; i < npoints; ++i) {
872        trPoint = m_data->mapPoint(points[i]);
873        winPoints[i].x = stableRound(trPoint.x());
874        winPoints[i].y = stableRound(trPoint.y());
875        if (rect.left > winPoints[i].x)
876            rect.left = winPoints[i].x;
877        else if (rect.right < winPoints[i].x)
878            rect.right = winPoints[i].x;
879        if (rect.top > winPoints[i].y)
880            rect.top = winPoints[i].y;
881        else if (rect.bottom < winPoints[i].y)
882            rect.bottom = winPoints[i].y;
883    }
884    rect.bottom += 1;
885    rect.right += 1;
886
887    IntRect intRect(rect);
888    TransparentLayerDC transparentDC(m_data, intRect);
889    HDC dc = transparentDC.hdc();
890    if (!dc)
891        return;
892
893    for (size_t i = 0; i < npoints; ++i) {
894        winPoints[i].x += transparentDC.toShift().width();
895        winPoints[i].y += transparentDC.toShift().height();
896    }
897
898    OwnPtr<HBRUSH> brush;
899    HGDIOBJ oldBrush;
900    if (fillColor().alpha()) {
901        brush = createBrush(fillColor());
902        oldBrush = SelectObject(dc, brush.get());
903    } else
904        oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH));
905
906    OwnPtr<HPEN> pen;
907    HGDIOBJ oldPen;
908    if (strokeStyle() != NoStroke) {
909        pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
910        oldPen = SelectObject(dc, pen.get());
911    } else
912        oldPen = SelectObject(dc, GetStockObject(NULL_PEN));
913
914    if (brush || pen)
915        Polygon(dc, winPoints.data(), npoints);
916
917    SelectObject(dc, oldPen);
918    SelectObject(dc, oldBrush);
919}
920
921void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
922{
923    if (paintingDisabled())
924        return;
925
926    if (numPoints <= 1)
927        return;
928
929    // FIXME: IMPLEMENT!!
930}
931
932void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
933{
934    if (paintingDisabled() || !m_data->m_opacity)
935        return;
936
937    int alpha = color.alpha();
938    if (!alpha)
939        return;
940
941    ScopeDCProvider dcProvider(m_data);
942    if (!m_data->m_dc)
943        return;
944
945    IntRect intRect = enclosingIntRect(rect);
946    TransparentLayerDC transparentDC(m_data, m_data->mapRect(intRect), &intRect, alpha);
947
948    if (!transparentDC.hdc())
949        return;
950
951    OwnPtr<HBRUSH> hbrush(CreateSolidBrush(RGB(color.red(), color.green(), color.blue())));
952    FillRect(transparentDC.hdc(), &transparentDC.rect(), hbrush.get());
953}
954
955void GraphicsContext::clip(const FloatRect& rect)
956{
957    if (paintingDisabled())
958        return;
959
960    if (!m_data->m_dc)
961        return;
962
963    IntRect trRect = enclosingIntRect(m_data->mapRect(rect));
964
965    OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
966    if (GetClipRgn(m_data->m_dc, clipRgn.get()) > 0)
967        IntersectClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY());
968    else {
969        clipRgn.set(CreateRectRgn(trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()));
970        SelectClipRgn(m_data->m_dc, clipRgn.get());
971    }
972}
973
974void GraphicsContext::clipOut(const IntRect& rect)
975{
976    if (paintingDisabled())
977        return;
978
979    if (!m_data->m_dc)
980        return;
981
982    IntRect trRect = m_data->mapRect(rect);
983
984    ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY());
985}
986
987void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color)
988{
989    // FIXME: implement
990}
991
992void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
993{
994    if (!m_data->m_opacity || paintingDisabled())
995        return;
996
997    ScopeDCProvider dcProvider(m_data);
998    if (!m_data->m_dc)
999        return;
1000
1001    int radius = (width - 1) / 2;
1002    offset += radius;
1003
1004    unsigned rectCount = rects.size();
1005    IntRect finalFocusRect;
1006    for (unsigned i = 0; i < rectCount; i++) {
1007        IntRect focusRect = rects[i];
1008        focusRect.inflate(offset);
1009        finalFocusRect.unite(focusRect);
1010    }
1011
1012    IntRect intRect = finalFocusRect;
1013    IntRect trRect = m_data->mapRect(finalFocusRect);
1014    TransparentLayerDC transparentDC(m_data, trRect, &intRect);
1015    HDC dc = transparentDC.hdc();
1016    if (!dc)
1017        return;
1018    trRect.move(transparentDC.toShift());
1019
1020    RECT rect = trRect;
1021    DrawFocusRect(dc, &rect);
1022}
1023
1024void GraphicsContext::drawLineForText(const FloatPoint& origin, float width, bool printing)
1025{
1026    if (paintingDisabled())
1027        return;
1028
1029    StrokeStyle oldStyle = strokeStyle();
1030    setStrokeStyle(SolidStroke);
1031    drawLine(roundedIntPoint(origin), roundedIntPoint(origin + FloatSize(width, 0)));
1032    setStrokeStyle(oldStyle);
1033}
1034
1035void GraphicsContext::drawLineForTextChecking(const FloatPoint&, float width, TextCheckingLineStyle style)
1036{
1037    notImplemented();
1038}
1039
1040void GraphicsContext::setPlatformFillColor(const Color& col, ColorSpace colorSpace)
1041{
1042    notImplemented();
1043}
1044
1045void GraphicsContext::setPlatformStrokeColor(const Color& col, ColorSpace colorSpace)
1046{
1047    notImplemented();
1048}
1049
1050void GraphicsContext::setPlatformStrokeThickness(float strokeThickness)
1051{
1052    notImplemented();
1053}
1054
1055void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1056{
1057    notImplemented();
1058}
1059
1060void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
1061{
1062    // We can only clip rectangles on WINCE
1063    clip(rect);
1064}
1065
1066void GraphicsContext::clearRect(const FloatRect& rect)
1067{
1068    if (paintingDisabled())
1069        return;
1070
1071    if (m_data->hasAlpha()) {
1072        IntRect trRect = enclosingIntRect(m_data->mapRect(rect));
1073        m_data->m_bitmap->clearPixels(trRect);
1074        return;
1075    }
1076
1077    fillRect(rect, Color(Color::white), ColorSpaceDeviceRGB);
1078}
1079
1080void GraphicsContext::strokeRect(const FloatRect& rect, float width)
1081{
1082    if (!m_data->m_opacity || paintingDisabled() || strokeStyle() == NoStroke)
1083        return;
1084
1085    ScopeDCProvider dcProvider(m_data);
1086    if (!m_data->m_dc)
1087        return;
1088
1089    IntRect intRect = enclosingIntRect(rect);
1090    IntRect trRect = m_data->mapRect(intRect);
1091    TransparentLayerDC transparentDC(m_data, trRect, &intRect);
1092    HDC dc = transparentDC.hdc();
1093    if (!dc)
1094        return;
1095    trRect.move(transparentDC.toShift());
1096
1097    OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
1098    HGDIOBJ oldPen = SelectObject(dc, pen.get());
1099
1100    int right = trRect.maxX() - 1;
1101    int bottom = trRect.maxY() - 1;
1102    const POINT intPoints[5] =
1103    {
1104        { trRect.x(), trRect.y() },
1105        { right, trRect.y() },
1106        { right, bottom },
1107        { trRect.x(), bottom },
1108        { trRect.x(), trRect.y() }
1109    };
1110
1111    Polyline(dc, intPoints, 5);
1112
1113    SelectObject(dc, oldPen);
1114}
1115
1116void GraphicsContext::beginTransparencyLayer(float opacity)
1117{
1118    m_data->save();
1119    m_data->m_opacity *= opacity;
1120}
1121
1122void GraphicsContext::endTransparencyLayer()
1123{
1124    m_data->restore();
1125}
1126
1127void GraphicsContext::concatCTM(const AffineTransform& transform)
1128{
1129    m_data->concatCTM(transform);
1130}
1131
1132void GraphicsContext::setCTM(const AffineTransform& transform)
1133{
1134    m_data->setCTM(transform);
1135}
1136
1137AffineTransform& GraphicsContext::affineTransform()
1138{
1139    return m_data->m_transform;
1140}
1141
1142const AffineTransform& GraphicsContext::affineTransform() const
1143{
1144    return m_data->m_transform;
1145}
1146
1147void GraphicsContext::resetAffineTransform()
1148{
1149    m_data->m_transform.makeIdentity();
1150}
1151
1152void GraphicsContext::translate(float x, float y)
1153{
1154    m_data->translate(x, y);
1155}
1156
1157void GraphicsContext::rotate(float radians)
1158{
1159    m_data->rotate(radians);
1160}
1161
1162void GraphicsContext::scale(const FloatSize& size)
1163{
1164    m_data->scale(size);
1165}
1166
1167void GraphicsContext::setLineCap(LineCap lineCap)
1168{
1169    notImplemented();
1170}
1171
1172void GraphicsContext::setLineJoin(LineJoin lineJoin)
1173{
1174    notImplemented();
1175}
1176
1177void GraphicsContext::setMiterLimit(float miter)
1178{
1179    notImplemented();
1180}
1181
1182void GraphicsContext::setAlpha(float alpha)
1183{
1184    m_data->m_opacity = alpha;
1185}
1186
1187void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
1188{
1189    notImplemented();
1190}
1191
1192void GraphicsContext::clip(const Path& path)
1193{
1194    notImplemented();
1195}
1196
1197void GraphicsContext::canvasClip(const Path& path)
1198{
1199    clip(path);
1200}
1201
1202void GraphicsContext::clipOut(const Path&)
1203{
1204    notImplemented();
1205}
1206
1207static inline IntPoint rectCenterPoint(const RECT& rect)
1208{
1209    return IntPoint(rect.left + (rect.right - rect.left) / 2, rect.top + (rect.bottom - rect.top) / 2);
1210}
1211void GraphicsContext::fillRoundedRect(const IntRect& fillRect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& c, ColorSpace colorSpace)
1212{
1213    ScopeDCProvider dcProvider(m_data);
1214    if (!m_data->m_dc)
1215        return;
1216
1217    FloatSize shadowOffset;
1218    float shadowBlur = 0;
1219    Color shadowColor;
1220    ColorSpace shadowColorSpace;
1221
1222    getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace);
1223
1224    IntRect dstRect = fillRect;
1225
1226    dstRect.move(stableRound(shadowOffset.width()), stableRound(shadowOffset.height()));
1227    dstRect.inflate(stableRound(shadowBlur));
1228    dstRect = m_data->mapRect(dstRect);
1229
1230    FloatSize newTopLeft(m_data->mapSize(topLeft));
1231    FloatSize newTopRight(m_data->mapSize(topRight));
1232    FloatSize newBottomLeft(m_data->mapSize(bottomLeft));
1233    FloatSize newBottomRight(m_data->mapSize(bottomRight));
1234
1235    TransparentLayerDC transparentDc(m_data, dstRect, &fillRect);
1236    HDC dc = transparentDc.hdc();
1237    if (!dc)
1238        return;
1239
1240    dstRect.move(transparentDc.toShift());
1241
1242    RECT rectWin = dstRect;
1243
1244    OwnPtr<HBRUSH> brush = createBrush(shadowColor);
1245    HGDIOBJ oldBrush = SelectObject(dc, brush.get());
1246
1247    SelectObject(dc, GetStockObject(NULL_PEN));
1248
1249    IntPoint centerPoint = rectCenterPoint(rectWin);
1250    // Draw top left half
1251    RECT clipRect(rectWin);
1252    clipRect.right = centerPoint.x();
1253    clipRect.bottom = centerPoint.y();
1254
1255    OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
1256    bool needsNewClip = (GetClipRgn(dc, clipRgn.get()) <= 0);
1257
1258    drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopLeft.width() * 2), stableRound(newTopLeft.height() * 2));
1259
1260    // Draw top right
1261    clipRect = rectWin;
1262    clipRect.left = centerPoint.x();
1263    clipRect.bottom = centerPoint.y();
1264
1265    drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newTopRight.width() * 2), stableRound(newTopRight.height() * 2));
1266
1267     // Draw bottom left
1268    clipRect = rectWin;
1269    clipRect.right = centerPoint.x();
1270    clipRect.top = centerPoint.y();
1271
1272    drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomLeft.width() * 2), stableRound(newBottomLeft.height() * 2));
1273
1274    // Draw bottom right
1275    clipRect = rectWin;
1276    clipRect.left = centerPoint.x();
1277    clipRect.top = centerPoint.y();
1278
1279    drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomRight.width() * 2), stableRound(newBottomRight.height() * 2));
1280
1281    SelectObject(dc, oldBrush);
1282}
1283
1284
1285void GraphicsContext::drawRoundCorner(bool needsNewClip, RECT clipRect, RECT rectWin, HDC dc, int width, int height)
1286{
1287    if (!dc)
1288        return;
1289
1290    OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0));
1291    if (needsNewClip)  {
1292        clipRgn.set(CreateRectRgn(clipRect.left, clipRect.top, clipRect.right, clipRect.bottom));
1293        SelectClipRgn(dc, clipRgn.get());
1294    } else
1295        IntersectClipRect(dc, clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
1296
1297    ::RoundRect(dc, rectWin.left , rectWin.top , rectWin.right , rectWin.bottom , width, height);
1298
1299    SelectClipRgn(dc, needsNewClip ? 0 : clipRgn.get());
1300}
1301
1302
1303FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect, RoundingMode)
1304{
1305    notImplemented();
1306    return frect;
1307}
1308
1309Color gradientAverageColor(const Gradient* gradient)
1310{
1311    const Vector<Gradient::ColorStop>& stops = gradient->getStops();
1312    if (stops.isEmpty())
1313        return Color();
1314
1315    const Gradient::ColorStop& stop = stops.first();
1316    if (stops.size() == 1)
1317        return Color(stop.red, stop.green, stop.blue, stop.alpha);
1318
1319    const Gradient::ColorStop& lastStop = stops.last();
1320    return Color((stop.red + lastStop.red) * 0.5f
1321        , (stop.green + lastStop.green) * 0.5f
1322        , (stop.blue + lastStop.blue) * 0.5f
1323        , (stop.alpha + lastStop.alpha) * 0.5f);
1324}
1325
1326void GraphicsContext::fillPath(const Path& path)
1327{
1328    Color c = m_state.fillGradient
1329        ? gradientAverageColor(m_state.fillGradient.get())
1330        : fillColor();
1331
1332    if (!c.alpha() || !m_data->m_opacity)
1333        return;
1334
1335    ScopeDCProvider dcProvider(m_data);
1336    if (!m_data->m_dc)
1337        return;
1338
1339    OwnPtr<HBRUSH> brush = createBrush(c);
1340
1341    if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) {
1342        IntRect trRect = enclosingIntRect(m_data->mapRect(path.boundingRect()));
1343        trRect.inflate(1);
1344        TransparentLayerDC transparentDC(m_data, trRect);
1345        HDC dc = transparentDC.hdc();
1346        if (!dc)
1347            return;
1348
1349        AffineTransform tr = m_data->m_transform;
1350        tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height());
1351
1352        SelectObject(dc, GetStockObject(NULL_PEN));
1353        HGDIOBJ oldBrush = SelectObject(dc, brush.get());
1354        path.platformPath()->fillPath(dc, &tr);
1355        SelectObject(dc, oldBrush);
1356    } else {
1357        SelectObject(m_data->m_dc, GetStockObject(NULL_PEN));
1358        HGDIOBJ oldBrush = SelectObject(m_data->m_dc, brush.get());
1359        path.platformPath()->fillPath(m_data->m_dc, &m_data->m_transform);
1360        SelectObject(m_data->m_dc, oldBrush);
1361    }
1362}
1363
1364
1365void GraphicsContext::strokePath(const Path& path)
1366{
1367    if (!m_data->m_opacity)
1368        return;
1369
1370    ScopeDCProvider dcProvider(m_data);
1371    if (!m_data->m_dc)
1372        return;
1373
1374    OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle());
1375
1376    if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) {
1377        IntRect trRect = enclosingIntRect(m_data->mapRect(path.boundingRect()));
1378        trRect.inflate(1);
1379        TransparentLayerDC transparentDC(m_data, trRect);
1380        HDC dc = transparentDC.hdc();
1381        if (!dc)
1382            return;
1383
1384        AffineTransform tr = m_data->m_transform;
1385        tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height());
1386
1387        SelectObject(dc, GetStockObject(NULL_BRUSH));
1388        HGDIOBJ oldPen = SelectObject(dc, pen.get());
1389        path.platformPath()->strokePath(dc, &tr);
1390        SelectObject(dc, oldPen);
1391    } else {
1392        SelectObject(m_data->m_dc, GetStockObject(NULL_BRUSH));
1393        HGDIOBJ oldPen = SelectObject(m_data->m_dc, pen.get());
1394        path.platformPath()->strokePath(m_data->m_dc, &m_data->m_transform);
1395        SelectObject(m_data->m_dc, oldPen);
1396    }
1397}
1398
1399void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient)
1400{
1401    if (!m_data->m_opacity)
1402        return;
1403
1404    const Vector<Gradient::ColorStop>& stops = gradient->getStops();
1405    if (stops.isEmpty())
1406        return;
1407
1408    size_t numStops = stops.size();
1409    if (numStops == 1) {
1410        const Gradient::ColorStop& stop = stops.first();
1411        Color color(stop.red, stop.green, stop.blue, stop.alpha);
1412        fillRect(r, color, ColorSpaceDeviceRGB);
1413        return;
1414    }
1415
1416    ScopeDCProvider dcProvider(m_data);
1417    if (!m_data->m_dc)
1418        return;
1419
1420    IntRect intRect = enclosingIntRect(r);
1421    IntRect rect = m_data->mapRect(intRect);
1422    TransparentLayerDC transparentDC(m_data, rect, &intRect, 255, true);
1423    HDC dc = transparentDC.hdc();
1424    if (!dc)
1425        return;
1426
1427    rect.move(transparentDC.toShift());
1428    FloatPoint fp0 = m_data->mapPoint(gradient->p0());
1429    FloatPoint fp1 = m_data->mapPoint(gradient->p1());
1430    IntPoint p0(stableRound(fp0.x()), stableRound(fp0.y()));
1431    IntPoint p1(stableRound(fp1.x()), stableRound(fp1.y()));
1432    p0 += transparentDC.toShift();
1433    p1 += transparentDC.toShift();
1434
1435    if (gradient->isRadial()) {
1436        if (g_radialGradientFiller) {
1437            // FIXME: don't support 2D scaling at this time
1438            double scale = (m_data->m_transform.a() + m_data->m_transform.d()) * 0.5;
1439            float r0 = gradient->startRadius() * scale;
1440            float r1 = gradient->endRadius() * scale;
1441            g_radialGradientFiller(dc, rect, p0, p1, r0, r1, gradient->getStops());
1442            return;
1443        }
1444    } else if (g_linearGradientFiller) {
1445        g_linearGradientFiller(dc, rect, p0, p1, gradient->getStops());
1446        return;
1447    }
1448
1449    // Simple 1D linear solution that assumes p0 is on the top or left side, and p1 is on the right or bottom side
1450    size_t numRects = (numStops - 1);
1451    Vector<TRIVERTEX, 20> tv;
1452    tv.resize(numRects * 2);
1453    Vector<GRADIENT_RECT, 10> mesh;
1454    mesh.resize(numRects);
1455    int x = rect.x();
1456    int y = rect.y();
1457    int width = rect.width();
1458    int height = rect.height();
1459    FloatSize d = gradient->p1() - gradient->p0();
1460    bool vertical = fabs(d.height()) > fabs(d.width());
1461    for (size_t i = 0; i < numStops; ++i) {
1462        const Gradient::ColorStop& stop = stops[i];
1463        int iTv = i ? 2 * i - 1 : 0;
1464        tv[iTv].Red = stop.red * 0xFFFF;
1465        tv[iTv].Green = stop.green * 0xFFFF;
1466        tv[iTv].Blue = stop.blue * 0xFFFF;
1467        tv[iTv].Alpha = stop.alpha * 0xFFFF;
1468        if (i) {
1469            tv[iTv].x = vertical ? x + width: x + width * stop.stop;
1470            tv[iTv].y = vertical ? y + height * stop.stop : y + height;
1471            mesh[i - 1].UpperLeft = iTv - 1;
1472            mesh[i - 1].LowerRight = iTv;
1473        } else {
1474            tv[iTv].x = x;
1475            tv[iTv].y = y;
1476        }
1477
1478        if (i && i < numRects) {
1479            tv[iTv + 1] = tv[iTv];
1480            if (vertical)
1481                tv[iTv + 1].x = x;
1482            else
1483                tv[iTv + 1].y = y;
1484        }
1485    }
1486
1487    GradientFill(dc, tv.data(), tv.size(), mesh.data(), mesh.size(), vertical ? GRADIENT_FILL_RECT_V : GRADIENT_FILL_RECT_H);
1488}
1489
1490AffineTransform GraphicsContext::getCTM() const
1491{
1492    return m_data->m_transform;
1493}
1494
1495void GraphicsContext::fillRect(const FloatRect& rect)
1496{
1497    savePlatformState();
1498
1499    if (m_state.fillGradient)
1500        fillRect(rect, m_state.fillGradient.get());
1501    else
1502        fillRect(rect, fillColor(), ColorSpaceDeviceRGB);
1503
1504    restorePlatformState();
1505}
1506
1507void GraphicsContext::setPlatformShadow(const FloatSize&, float, const Color&, ColorSpace)
1508{
1509    notImplemented();
1510}
1511
1512void GraphicsContext::clearPlatformShadow()
1513{
1514    notImplemented();
1515}
1516
1517InterpolationQuality GraphicsContext::imageInterpolationQuality() const
1518{
1519    notImplemented();
1520    return InterpolationDefault;
1521}
1522
1523void GraphicsContext::setImageInterpolationQuality(InterpolationQuality)
1524{
1525    notImplemented();
1526}
1527
1528static inline bool isCharVisible(UChar c)
1529{
1530    return c && c != zeroWidthSpace;
1531}
1532
1533void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
1534{
1535    if (paintingDisabled() || !fillColor().alpha() || !m_data->m_opacity)
1536        return;
1537
1538    bool mustSupportAlpha = m_data->hasAlpha();
1539
1540    if (!mustSupportAlpha && fillColor().alpha() == 0xFF && m_data->m_opacity >= 1.0) {
1541        font.drawText(this, run, point, from, to);
1542        return;
1543    }
1544
1545    float oldOpacity = m_data->m_opacity;
1546    m_data->m_opacity *= fillColor().alpha() / 255.0;
1547
1548    FloatRect textRect = font.selectionRectForText(run, point, font.fontMetrics().height(), from, to);
1549    textRect.setY(textRect.y() - font.fontMetrics().ascent());
1550    IntRect trRect = enclosingIntRect(m_data->mapRect(textRect));
1551    RECT bmpRect;
1552    AlphaPaintType alphaPaintType = mustSupportAlpha ? AlphaPaintOther : AlphaPaintNone;
1553    if (RefPtr<SharedBitmap> bmp = m_data->getTransparentLayerBitmap(trRect, alphaPaintType, bmpRect, true, mustSupportAlpha)) {
1554        {
1555            GraphicsContext gc(0);
1556            gc.setBitmap(bmp);
1557            gc.scale(FloatSize(m_data->m_transform.a(), m_data->m_transform.d()));
1558            font.drawText(&gc, run, IntPoint(0, font.fontMetrics().ascent()), from, to);
1559        }
1560        unsigned key1;
1561        HDC memDC = bmp->getDC(&key1);
1562        if (memDC) {
1563            m_data->paintBackTransparentLayerBitmap(memDC, bmp.get(), trRect, alphaPaintType, bmpRect);
1564            bmp->releaseDC(memDC, key1);
1565        }
1566    }
1567
1568    m_data->m_opacity = oldOpacity;
1569}
1570
1571void GraphicsContext::drawText(const SimpleFontData* fontData, const GlyphBuffer& glyphBuffer,
1572                      int from, int numGlyphs, const FloatPoint& point)
1573{
1574    if (!m_data->m_opacity)
1575        return;
1576
1577    for (;;) {
1578        if (!numGlyphs)
1579            return;
1580        if (isCharVisible(*glyphBuffer.glyphs(from)))
1581            break;
1582        ++from;
1583        --numGlyphs;
1584    }
1585
1586    double scaleX = m_data->m_transform.a();
1587    double scaleY = m_data->m_transform.d();
1588
1589    int height = fontData->platformData().size() * scaleY;
1590    int width = fontData->platformData().averageCharWidth() * scaleX;
1591
1592    if (!height || !width)
1593        return;
1594
1595    ScopeDCProvider dcProvider(m_data);
1596    if (!m_data->m_dc)
1597        return;
1598
1599    HFONT hFont = height > 1
1600        ? fontData->platformData().getScaledFontHandle(height, scaleX == scaleY ? 0 : width)
1601        : 0;
1602
1603    FloatPoint startPoint(point.x(), point.y() - fontData->fontMetrics().ascent());
1604    FloatPoint trPoint = m_data->mapPoint(startPoint);
1605    int y = stableRound(trPoint.y());
1606
1607    Color color = fillColor();
1608    if (!color.alpha())
1609        return;
1610
1611    COLORREF fontColor = RGB(color.red(), color.green(), color.blue());
1612
1613    if (!hFont) {
1614        double offset = trPoint.x();
1615        const GlyphBufferAdvance* advance = glyphBuffer.advances(from);
1616        if (scaleX == 1.)
1617            for (int i = 1; i < numGlyphs; ++i)
1618                offset += *advance++;
1619        else
1620            for (int i = 1; i < numGlyphs; ++i)
1621                offset += *advance++ * scaleX;
1622
1623        offset += width;
1624
1625        OwnPtr<HPEN> hPen(CreatePen(PS_DASH, 1, fontColor));
1626        HGDIOBJ oldPen = SelectObject(m_data->m_dc, hPen.get());
1627
1628        MoveToEx(m_data->m_dc, stableRound(trPoint.x()), y, 0);
1629        LineTo(m_data->m_dc, stableRound(offset), y);
1630
1631        SelectObject(m_data->m_dc, oldPen);
1632        return;
1633    }
1634
1635    FloatSize shadowOffset;
1636    float shadowBlur = 0;
1637    Color shadowColor;
1638    ColorSpace shadowColorSpace;
1639    bool hasShadow = textDrawingMode() == TextModeFill
1640        && getShadow(shadowOffset, shadowBlur, shadowColor, shadowColorSpace)
1641        && shadowColor.alpha();
1642    COLORREF shadowRGBColor;
1643    FloatPoint trShadowPoint;
1644    if (hasShadow) {
1645        shadowRGBColor = RGB(shadowColor.red(), shadowColor.green(), shadowColor.blue());
1646        trShadowPoint = m_data->mapPoint(startPoint + shadowOffset);
1647    }
1648
1649    HGDIOBJ hOldFont = SelectObject(m_data->m_dc, hFont);
1650    COLORREF oldTextColor = GetTextColor(m_data->m_dc);
1651    int oldTextAlign = GetTextAlign(m_data->m_dc);
1652    SetTextAlign(m_data->m_dc, 0);
1653
1654    int oldBkMode = GetBkMode(m_data->m_dc);
1655    SetBkMode(m_data->m_dc, TRANSPARENT);
1656
1657    if (numGlyphs > 1) {
1658        double offset = trPoint.x();
1659        Vector<int, 256> glyphSpace(numGlyphs);
1660        Vector<UChar, 256> text(numGlyphs);
1661        int* curSpace = glyphSpace.data();
1662        UChar* curChar = text.data();
1663        const UChar* srcChar = glyphBuffer.glyphs(from);
1664        const UChar* const srcCharEnd = srcChar + numGlyphs;
1665        *curChar++ = *srcChar++;
1666        int firstOffset = stableRound(offset);
1667        int lastOffset = firstOffset;
1668        const GlyphBufferAdvance* advance = glyphBuffer.advances(from);
1669        // FIXME: ExtTextOut() can flip over each word for RTL languages, even when TA_RTLREADING is off.
1670        // (this can be GDI bug or font driver bug?)
1671        // We are not clear how it processes characters and handles specified spaces. On the other side,
1672        // our glyph buffer is already in the correct order for rendering. So, the solution is that we
1673        // call ExtTextOut() for each single character when the text contains any RTL character.
1674        // This solution is not perfect as it is slower than calling ExtTextOut() one time for all characters.
1675        // Drawing characters one by one may be too slow.
1676        bool drawOneByOne = false;
1677        if (scaleX == 1.) {
1678            for (; srcChar < srcCharEnd; ++srcChar) {
1679                offset += *advance++;
1680                int offsetInt = stableRound(offset);
1681                if (isCharVisible(*srcChar)) {
1682                    if (!drawOneByOne && WTF::Unicode::direction(*srcChar) == WTF::Unicode::RightToLeft)
1683                        drawOneByOne = true;
1684                    *curChar++ = *srcChar;
1685                    *curSpace++ = offsetInt - lastOffset;
1686                    lastOffset = offsetInt;
1687                }
1688            }
1689        } else {
1690            for (; srcChar < srcCharEnd; ++srcChar) {
1691                offset += *advance++ * scaleX;
1692                int offsetInt = stableRound(offset);
1693                if (isCharVisible(*srcChar)) {
1694                    if (!drawOneByOne && WTF::Unicode::direction(*srcChar) == WTF::Unicode::RightToLeft)
1695                        drawOneByOne = true;
1696                    *curChar++ = *srcChar;
1697                    *curSpace++ = offsetInt - lastOffset;
1698                    lastOffset = offsetInt;
1699                }
1700            }
1701        }
1702        numGlyphs = curChar - text.data();
1703        if (hasShadow) {
1704            SetTextColor(m_data->m_dc, shadowRGBColor);
1705            if (drawOneByOne) {
1706                int xShadow = firstOffset + stableRound(trShadowPoint.x() - trPoint.x());
1707                int yShadow = stableRound(trShadowPoint.y());
1708                for (int i = 0; i < numGlyphs; ++i) {
1709                    ExtTextOut(m_data->m_dc, xShadow, yShadow, 0, NULL, text.data() + i, 1, 0);
1710                    xShadow += glyphSpace[i];
1711                }
1712            } else
1713                ExtTextOut(m_data->m_dc, firstOffset + stableRound(trShadowPoint.x() - trPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, text.data(), numGlyphs, glyphSpace.data());
1714        }
1715        SetTextColor(m_data->m_dc, fontColor);
1716        if (drawOneByOne) {
1717            int x = firstOffset;
1718            for (int i = 0; i < numGlyphs; ++i) {
1719                ExtTextOut(m_data->m_dc, x, y, 0, NULL, text.data() + i, 1, 0);
1720                x += glyphSpace[i];
1721            }
1722        } else
1723            ExtTextOut(m_data->m_dc, firstOffset, y, 0, NULL, text.data(), numGlyphs, glyphSpace.data());
1724    } else {
1725        UChar c = *glyphBuffer.glyphs(from);
1726        if (hasShadow) {
1727            SetTextColor(m_data->m_dc, shadowRGBColor);
1728            ExtTextOut(m_data->m_dc, stableRound(trShadowPoint.x()), stableRound(trShadowPoint.y()), 0, NULL, &c, 1, 0);
1729        }
1730        SetTextColor(m_data->m_dc, fontColor);
1731        ExtTextOut(m_data->m_dc, stableRound(trPoint.x()), y, 0, NULL, &c, 1, 0);
1732    }
1733
1734    SetTextAlign(m_data->m_dc, oldTextAlign);
1735    SetTextColor(m_data->m_dc, oldTextColor);
1736    SetBkMode(m_data->m_dc, oldBkMode);
1737    SelectObject(m_data->m_dc, hOldFont);
1738}
1739
1740void GraphicsContext::drawFrameControl(const IntRect& rect, unsigned type, unsigned state)
1741{
1742    if (!m_data->m_opacity)
1743        return;
1744
1745    const int boxWidthBest = 8;
1746    const int boxHeightBest = 8;
1747
1748    ScopeDCProvider dcProvider(m_data);
1749    if (!m_data->m_dc)
1750        return;
1751
1752    IntRect trRect = m_data->mapRect(rect);
1753    TransparentLayerDC transparentDC(m_data, trRect, &rect, 255, true);
1754    HDC dc = transparentDC.hdc();
1755    if (!dc)
1756        return;
1757    trRect.move(transparentDC.toShift());
1758
1759    RECT rectWin = trRect;
1760
1761    if ((rectWin.right - rectWin.left) < boxWidthBest) {
1762        RefPtr<SharedBitmap> bmp = SharedBitmap::create(IntSize(boxWidthBest, boxHeightBest), BitmapInfo::BitCount16, true);
1763        SharedBitmap::DCHolder memDC(bmp.get());
1764        if (memDC.get()) {
1765            RECT tempRect = {0, 0, boxWidthBest, boxHeightBest};
1766            DrawFrameControl(memDC.get(), &tempRect, type, state);
1767
1768            ::StretchBlt(dc, rectWin.left, rectWin.top, rectWin.right - rectWin.left, rectWin.bottom - rectWin.top, memDC.get(), 0, 0, boxWidthBest, boxHeightBest, SRCCOPY);
1769            return;
1770        }
1771    }
1772
1773    DrawFrameControl(dc, &rectWin, type, state);
1774}
1775
1776void GraphicsContext::drawFocusRect(const IntRect& rect)
1777{
1778    if (!m_data->m_opacity)
1779        return;
1780
1781    ScopeDCProvider dcProvider(m_data);
1782    if (!m_data->m_dc)
1783        return;
1784
1785    IntRect trRect = m_data->mapRect(rect);
1786    TransparentLayerDC transparentDC(m_data, trRect, &rect);
1787    HDC dc = transparentDC.hdc();
1788    if (!dc)
1789        return;
1790    trRect.move(transparentDC.toShift());
1791
1792    RECT rectWin = trRect;
1793    DrawFocusRect(dc, &rectWin);
1794}
1795
1796void GraphicsContext::paintTextField(const IntRect& rect, unsigned state)
1797{
1798    if (!m_data->m_opacity)
1799        return;
1800
1801    ScopeDCProvider dcProvider(m_data);
1802    if (!m_data->m_dc)
1803        return;
1804
1805    IntRect trRect = m_data->mapRect(rect);
1806    TransparentLayerDC transparentDC(m_data, trRect, &rect);
1807    HDC dc = transparentDC.hdc();
1808    if (!dc)
1809        return;
1810    trRect.move(transparentDC.toShift());
1811
1812    RECT rectWin = trRect;
1813    DrawEdge(dc, &rectWin, EDGE_ETCHED, BF_RECT | BF_ADJUST);
1814    FillRect(dc, &rectWin, reinterpret_cast<HBRUSH>(((state & DFCS_INACTIVE) ? COLOR_BTNFACE : COLOR_WINDOW) + 1));
1815}
1816
1817void GraphicsContext::drawBitmap(SharedBitmap* bmp, const IntRect& dstRectIn, const IntRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp)
1818{
1819    if (!m_data->m_opacity)
1820        return;
1821
1822    ScopeDCProvider dcProvider(m_data);
1823    if (!m_data->m_dc)
1824        return;
1825
1826    IntRect dstRect = m_data->mapRect(dstRectIn);
1827    TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true);
1828    HDC dc = transparentDC.hdc();
1829    if (!dc)
1830        return;
1831    dstRect.move(transparentDC.toShift());
1832
1833    bmp->draw(dc, dstRect, srcRect, compositeOp);
1834
1835    if (bmp->is16bit())
1836        transparentDC.fillAlphaChannel();
1837}
1838
1839void GraphicsContext::drawBitmapPattern(SharedBitmap* bmp, const FloatRect& tileRectIn, const AffineTransform& patternTransform,
1840                const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRectIn, const IntSize& origSourceSize)
1841{
1842    if (!m_data->m_opacity)
1843        return;
1844
1845    ScopeDCProvider dcProvider(m_data);
1846    if (!m_data->m_dc)
1847        return;
1848
1849    IntRect intDstRect = enclosingIntRect(destRectIn);
1850    IntRect trRect = m_data->mapRect(intDstRect);
1851    TransparentLayerDC transparentDC(m_data, trRect, &intDstRect, 255, true);
1852    HDC dc = transparentDC.hdc();
1853    if (!dc)
1854        return;
1855    trRect.move(transparentDC.toShift());
1856    FloatRect movedDstRect = m_data->m_transform.inverse().mapRect(FloatRect(trRect));
1857    FloatSize moved(movedDstRect.location() - destRectIn.location());
1858    AffineTransform transform = m_data->m_transform;
1859    transform.translate(moved.width(), moved.height());
1860
1861    bmp->drawPattern(dc, transform, tileRectIn, patternTransform, phase, styleColorSpace, op, destRectIn, origSourceSize);
1862
1863    if (!bmp->hasAlpha())
1864        transparentDC.fillAlphaChannel();
1865}
1866
1867void GraphicsContext::drawIcon(HICON icon, const IntRect& dstRectIn, UINT flags)
1868{
1869    if (!m_data->m_opacity)
1870        return;
1871
1872    ScopeDCProvider dcProvider(m_data);
1873    if (!m_data->m_dc)
1874        return;
1875
1876    IntRect dstRect = m_data->mapRect(dstRectIn);
1877    TransparentLayerDC transparentDC(m_data, dstRect, &dstRectIn, 255, true);
1878    HDC dc = transparentDC.hdc();
1879    if (!dc)
1880        return;
1881    dstRect.move(transparentDC.toShift());
1882
1883    DrawIconEx(dc, dstRect.x(), dstRect.y(), icon, dstRect.width(), dstRect.height(), 0, NULL, flags);
1884}
1885
1886void GraphicsContext::setPlatformShouldAntialias(bool)
1887{
1888    notImplemented();
1889}
1890
1891void GraphicsContext::setLineDash(const DashArray&, float)
1892{
1893    notImplemented();
1894}
1895
1896void GraphicsContext::clipPath(const Path&, WindRule)
1897{
1898    notImplemented();
1899}
1900
1901} // namespace WebCore
1902