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