1/*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "GraphicsContext.h"
28
29#include "BidiResolver.h"
30#include "BitmapImage.h"
31#include "Gradient.h"
32#include "ImageBuffer.h"
33#include "IntRect.h"
34#include "RoundedRect.h"
35#include "TextRun.h"
36
37#include "stdio.h"
38
39using namespace std;
40
41namespace WebCore {
42
43class TextRunIterator {
44public:
45    TextRunIterator()
46        : m_textRun(0)
47        , m_offset(0)
48    {
49    }
50
51    TextRunIterator(const TextRun* textRun, unsigned offset)
52        : m_textRun(textRun)
53        , m_offset(offset)
54    {
55    }
56
57    TextRunIterator(const TextRunIterator& other)
58        : m_textRun(other.m_textRun)
59        , m_offset(other.m_offset)
60    {
61    }
62
63    unsigned offset() const { return m_offset; }
64    void increment() { m_offset++; }
65    bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
66    UChar current() const { return (*m_textRun)[m_offset]; }
67    WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
68
69    bool operator==(const TextRunIterator& other)
70    {
71        return m_offset == other.m_offset && m_textRun == other.m_textRun;
72    }
73
74    bool operator!=(const TextRunIterator& other) { return !operator==(other); }
75
76private:
77    const TextRun* m_textRun;
78    int m_offset;
79};
80
81GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
82    : m_updatingControlTints(false)
83    , m_transparencyCount(0)
84{
85    platformInit(platformGraphicsContext);
86}
87
88GraphicsContext::~GraphicsContext()
89{
90    ASSERT(m_stack.isEmpty());
91    ASSERT(!m_transparencyCount);
92    platformDestroy();
93}
94
95void GraphicsContext::save()
96{
97    if (paintingDisabled())
98        return;
99
100    m_stack.append(m_state);
101
102    savePlatformState();
103}
104
105void GraphicsContext::restore()
106{
107    if (paintingDisabled())
108        return;
109
110    if (m_stack.isEmpty()) {
111        LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
112        return;
113    }
114    m_state = m_stack.last();
115    m_stack.removeLast();
116
117    restorePlatformState();
118}
119
120void GraphicsContext::setStrokeThickness(float thickness)
121{
122    m_state.strokeThickness = thickness;
123    setPlatformStrokeThickness(thickness);
124}
125
126void GraphicsContext::setStrokeStyle(StrokeStyle style)
127{
128    m_state.strokeStyle = style;
129    setPlatformStrokeStyle(style);
130}
131
132void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
133{
134    m_state.strokeColor = color;
135    m_state.strokeColorSpace = colorSpace;
136    m_state.strokeGradient.clear();
137    m_state.strokePattern.clear();
138    setPlatformStrokeColor(color, colorSpace);
139}
140
141void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
142{
143    m_state.shadowOffset = offset;
144    m_state.shadowBlur = blur;
145    m_state.shadowColor = color;
146    m_state.shadowColorSpace = colorSpace;
147    setPlatformShadow(offset, blur, color, colorSpace);
148}
149
150void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
151{
152    m_state.shadowOffset = offset;
153    m_state.shadowBlur = blur;
154    m_state.shadowColor = color;
155    m_state.shadowColorSpace = colorSpace;
156#if USE(CG)
157    m_state.shadowsUseLegacyRadius = true;
158#endif
159    setPlatformShadow(offset, blur, color, colorSpace);
160}
161
162void GraphicsContext::clearShadow()
163{
164    m_state.shadowOffset = FloatSize();
165    m_state.shadowBlur = 0;
166    m_state.shadowColor = Color();
167    m_state.shadowColorSpace = ColorSpaceDeviceRGB;
168    clearPlatformShadow();
169}
170
171bool GraphicsContext::hasShadow() const
172{
173    return m_state.shadowColor.isValid() && m_state.shadowColor.alpha()
174           && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height());
175}
176
177bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const
178{
179    offset = m_state.shadowOffset;
180    blur = m_state.shadowBlur;
181    color = m_state.shadowColor;
182    colorSpace = m_state.shadowColorSpace;
183
184    return hasShadow();
185}
186
187bool GraphicsContext::hasBlurredShadow() const
188{
189    return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() && m_state.shadowBlur;
190}
191
192#if PLATFORM(QT) || USE(CAIRO)
193bool GraphicsContext::mustUseShadowBlur() const
194{
195    // We can't avoid ShadowBlur if the shadow has blur.
196    if (hasBlurredShadow())
197        return true;
198    // We can avoid ShadowBlur and optimize, since we're not drawing on a
199    // canvas and box shadows are affected by the transformation matrix.
200    if (!m_state.shadowsIgnoreTransforms)
201        return false;
202    // We can avoid ShadowBlur, since there are no transformations to apply to the canvas.
203    if (getCTM().isIdentity())
204        return false;
205    // Otherwise, no chance avoiding ShadowBlur.
206    return true;
207}
208#endif
209
210float GraphicsContext::strokeThickness() const
211{
212    return m_state.strokeThickness;
213}
214
215StrokeStyle GraphicsContext::strokeStyle() const
216{
217    return m_state.strokeStyle;
218}
219
220Color GraphicsContext::strokeColor() const
221{
222    return m_state.strokeColor;
223}
224
225ColorSpace GraphicsContext::strokeColorSpace() const
226{
227    return m_state.strokeColorSpace;
228}
229
230WindRule GraphicsContext::fillRule() const
231{
232    return m_state.fillRule;
233}
234
235void GraphicsContext::setFillRule(WindRule fillRule)
236{
237    m_state.fillRule = fillRule;
238}
239
240void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
241{
242    m_state.fillColor = color;
243    m_state.fillColorSpace = colorSpace;
244    m_state.fillGradient.clear();
245    m_state.fillPattern.clear();
246    setPlatformFillColor(color, colorSpace);
247}
248
249Color GraphicsContext::fillColor() const
250{
251    return m_state.fillColor;
252}
253
254ColorSpace GraphicsContext::fillColorSpace() const
255{
256    return m_state.fillColorSpace;
257}
258
259void GraphicsContext::setShouldAntialias(bool b)
260{
261    m_state.shouldAntialias = b;
262    setPlatformShouldAntialias(b);
263}
264
265bool GraphicsContext::shouldAntialias() const
266{
267    return m_state.shouldAntialias;
268}
269
270void GraphicsContext::setShouldSmoothFonts(bool b)
271{
272    m_state.shouldSmoothFonts = b;
273    setPlatformShouldSmoothFonts(b);
274}
275
276bool GraphicsContext::shouldSmoothFonts() const
277{
278    return m_state.shouldSmoothFonts;
279}
280
281void GraphicsContext::setShouldSubpixelQuantizeFonts(bool b)
282{
283    m_state.shouldSubpixelQuantizeFonts = b;
284}
285
286bool GraphicsContext::shouldSubpixelQuantizeFonts() const
287{
288    return m_state.shouldSubpixelQuantizeFonts;
289}
290
291const GraphicsContextState& GraphicsContext::state() const
292{
293    return m_state;
294}
295
296void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
297{
298    ASSERT(pattern);
299    if (!pattern) {
300        setStrokeColor(Color::black, ColorSpaceDeviceRGB);
301        return;
302    }
303    m_state.strokeGradient.clear();
304    m_state.strokePattern = pattern;
305}
306
307void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
308{
309    ASSERT(pattern);
310    if (!pattern) {
311        setFillColor(Color::black, ColorSpaceDeviceRGB);
312        return;
313    }
314    m_state.fillGradient.clear();
315    m_state.fillPattern = pattern;
316}
317
318void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
319{
320    ASSERT(gradient);
321    if (!gradient) {
322        setStrokeColor(Color::black, ColorSpaceDeviceRGB);
323        return;
324    }
325    m_state.strokeGradient = gradient;
326    m_state.strokePattern.clear();
327}
328
329void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
330{
331    ASSERT(gradient);
332    if (!gradient) {
333        setFillColor(Color::black, ColorSpaceDeviceRGB);
334        return;
335    }
336    m_state.fillGradient = gradient;
337    m_state.fillPattern.clear();
338}
339
340Gradient* GraphicsContext::fillGradient() const
341{
342    return m_state.fillGradient.get();
343}
344
345Gradient* GraphicsContext::strokeGradient() const
346{
347    return m_state.strokeGradient.get();
348}
349
350Pattern* GraphicsContext::fillPattern() const
351{
352    return m_state.fillPattern.get();
353}
354
355Pattern* GraphicsContext::strokePattern() const
356{
357    return m_state.strokePattern.get();
358}
359
360void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
361{
362    m_state.shadowsIgnoreTransforms = ignoreTransforms;
363}
364
365bool GraphicsContext::shadowsIgnoreTransforms() const
366{
367    return m_state.shadowsIgnoreTransforms;
368}
369
370void GraphicsContext::beginTransparencyLayer(float opacity)
371{
372    beginPlatformTransparencyLayer(opacity);
373    ++m_transparencyCount;
374}
375
376void GraphicsContext::endTransparencyLayer()
377{
378    endPlatformTransparencyLayer();
379    ASSERT(m_transparencyCount > 0);
380    --m_transparencyCount;
381}
382
383#if !PLATFORM(QT)
384bool GraphicsContext::isInTransparencyLayer() const
385{
386    return (m_transparencyCount > 0) && supportsTransparencyLayers();
387}
388#endif
389
390bool GraphicsContext::updatingControlTints() const
391{
392    return m_updatingControlTints;
393}
394
395void GraphicsContext::setUpdatingControlTints(bool b)
396{
397    setPaintingDisabled(b);
398    m_updatingControlTints = b;
399}
400
401void GraphicsContext::setPaintingDisabled(bool f)
402{
403    m_state.paintingDisabled = f;
404}
405
406bool GraphicsContext::paintingDisabled() const
407{
408    return m_state.paintingDisabled;
409}
410
411#if !USE(WINGDI)
412void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
413{
414    if (paintingDisabled())
415        return;
416
417    font.drawText(this, run, point, from, to);
418}
419#endif
420
421void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to)
422{
423    if (paintingDisabled())
424        return;
425
426    font.drawEmphasisMarks(this, run, mark, point, from, to);
427}
428
429void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
430{
431    if (paintingDisabled())
432        return;
433
434    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
435    bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
436    bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
437
438    // FIXME: This ownership should be reversed. We should pass BidiRunList
439    // to BidiResolver in createBidiRunsForLine.
440    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
441    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
442    if (!bidiRuns.runCount())
443        return;
444
445    FloatPoint currPoint = point;
446    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
447    while (bidiRun) {
448        TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
449        bool isRTL = bidiRun->level() % 2;
450        subrun.setDirection(isRTL ? RTL : LTR);
451        subrun.setDirectionalOverride(bidiRun->dirOverride(false));
452
453        font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
454
455        bidiRun = bidiRun->next();
456        // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
457        if (bidiRun)
458            currPoint.move(font.width(subrun), 0);
459    }
460
461    bidiRuns.deleteRuns();
462}
463
464void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to)
465{
466    if (paintingDisabled())
467        return;
468
469    fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace);
470}
471
472void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
473{
474    if (!image)
475        return;
476    drawImage(image, styleColorSpace, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
477}
478
479void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation, bool useLowQualityScale)
480{
481    if (!image)
482        return;
483    drawImage(image, styleColorSpace, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation, useLowQualityScale);
484}
485
486void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
487{
488    drawImage(image, styleColorSpace, FloatRect(IntRect(dest, srcRect.size())), FloatRect(srcRect), op, shouldRespectImageOrientation);
489}
490
491void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation, bool useLowQualityScale)
492{
493    drawImage(image, styleColorSpace, dest, src, op, BlendModeNormal, shouldRespectImageOrientation, useLowQualityScale);
494}
495
496void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest)
497{
498    if (!image)
499        return;
500    drawImage(image, styleColorSpace, dest, FloatRect(IntRect(IntPoint(), image->size())));
501}
502
503void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, BlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation, bool useLowQualityScale)
504{    if (paintingDisabled() || !image)
505        return;
506
507    InterpolationQuality previousInterpolationQuality = InterpolationDefault;
508
509    if (useLowQualityScale) {
510        previousInterpolationQuality = imageInterpolationQuality();
511        // FIXME (49002): Should be InterpolationLow
512        setImageInterpolationQuality(InterpolationNone);
513    }
514
515    image->draw(this, dest, src, styleColorSpace, op, blendMode, shouldRespectImageOrientation);
516
517    if (useLowQualityScale)
518        setImageInterpolationQuality(previousInterpolationQuality);
519}
520
521void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale, BlendMode blendMode)
522{
523    if (paintingDisabled() || !image)
524        return;
525
526    if (useLowQualityScale) {
527        InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
528        setImageInterpolationQuality(InterpolationLow);
529        image->drawTiled(this, destRect, srcPoint, tileSize, styleColorSpace, op, blendMode);
530        setImageInterpolationQuality(previousInterpolationQuality);
531    } else
532        image->drawTiled(this, destRect, srcPoint, tileSize, styleColorSpace, op, blendMode);
533}
534
535void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect,
536    const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale)
537{
538    if (paintingDisabled() || !image)
539        return;
540
541    if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
542        // Just do a scale.
543        drawImage(image, styleColorSpace, dest, srcRect, op);
544        return;
545    }
546
547    if (useLowQualityScale) {
548        InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
549        setImageInterpolationQuality(InterpolationLow);
550        image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, styleColorSpace, op);
551        setImageInterpolationQuality(previousInterpolationQuality);
552    } else
553        image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, styleColorSpace, op);
554}
555
556void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op, BlendMode blendMode)
557{
558    if (!image)
559        return;
560    drawImageBuffer(image, styleColorSpace, FloatRect(IntRect(p, image->logicalSize())), FloatRect(FloatPoint(), FloatSize(image->logicalSize())), op, blendMode);
561}
562
563void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
564{
565    if (!image)
566        return;
567    drawImageBuffer(image, styleColorSpace, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->logicalSize())), op, blendMode, useLowQualityScale);
568}
569
570void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op, BlendMode blendMode)
571{
572    drawImageBuffer(image, styleColorSpace, FloatRect(IntRect(dest, srcRect.size())), FloatRect(srcRect), op, blendMode);
573}
574
575void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
576{
577    drawImageBuffer(image, styleColorSpace, FloatRect(dest), FloatRect(srcRect), op, blendMode, useLowQualityScale);
578}
579
580void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest)
581{
582    if (!image)
583        return;
584    drawImageBuffer(image, styleColorSpace, dest, FloatRect(IntRect(IntPoint(), image->logicalSize())));
585}
586
587void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, BlendMode blendMode, bool useLowQualityScale)
588{
589    if (paintingDisabled() || !image)
590        return;
591
592    if (useLowQualityScale) {
593        InterpolationQuality previousInterpolationQuality = imageInterpolationQuality();
594        // FIXME (49002): Should be InterpolationLow
595        setImageInterpolationQuality(InterpolationNone);
596        image->draw(this, styleColorSpace, dest, src, op, blendMode, useLowQualityScale);
597        setImageInterpolationQuality(previousInterpolationQuality);
598    } else
599        image->draw(this, styleColorSpace, dest, src, op, blendMode, useLowQualityScale);
600}
601
602#if !PLATFORM(QT)
603void GraphicsContext::clip(const IntRect& rect)
604{
605    clip(FloatRect(rect));
606}
607#endif
608
609void GraphicsContext::clipRoundedRect(const RoundedRect& rect)
610{
611    if (paintingDisabled())
612        return;
613
614    if (!rect.isRounded()) {
615        clip(rect.rect());
616        return;
617    }
618
619    Path path;
620    path.addRoundedRect(rect);
621    clip(path);
622}
623
624void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
625{
626    if (paintingDisabled())
627        return;
628
629    if (!rect.isRounded()) {
630        clipOut(rect.rect());
631        return;
632    }
633
634    Path path;
635    path.addRoundedRect(rect);
636    clipOut(path);
637}
638
639void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect)
640{
641    if (paintingDisabled())
642        return;
643    buffer->clip(this, rect);
644}
645
646#if !USE(CG) && !PLATFORM(QT) && !USE(CAIRO)
647IntRect GraphicsContext::clipBounds() const
648{
649    ASSERT_NOT_REACHED();
650    return IntRect();
651}
652#endif
653
654TextDrawingModeFlags GraphicsContext::textDrawingMode() const
655{
656    return m_state.textDrawingMode;
657}
658
659void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
660{
661    m_state.textDrawingMode = mode;
662    if (paintingDisabled())
663        return;
664    setPlatformTextDrawingMode(mode);
665}
666
667void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
668{
669    if (paintingDisabled())
670        return;
671    gradient.fill(this, rect);
672}
673
674void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode)
675{
676    if (paintingDisabled())
677        return;
678
679    CompositeOperator previousOperator = compositeOperation();
680    setCompositeOperation(op, blendMode);
681    fillRect(rect, color, styleColorSpace);
682    setCompositeOperation(previousOperator);
683}
684
685void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color, ColorSpace colorSpace, BlendMode blendMode)
686{
687
688    if (rect.isRounded()) {
689        setCompositeOperation(compositeOperation(), blendMode);
690        fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color, colorSpace);
691        setCompositeOperation(compositeOperation());
692    } else
693        fillRect(rect.rect(), color, colorSpace, compositeOperation(), blendMode);
694}
695
696#if !USE(CG) && !PLATFORM(QT)
697void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
698{
699    if (paintingDisabled())
700        return;
701
702    Path path;
703    path.addRect(rect);
704
705    if (!roundedHoleRect.radii().isZero())
706        path.addRoundedRect(roundedHoleRect);
707    else
708        path.addRect(roundedHoleRect.rect());
709
710    WindRule oldFillRule = fillRule();
711    Color oldFillColor = fillColor();
712    ColorSpace oldFillColorSpace = fillColorSpace();
713
714    setFillRule(RULE_EVENODD);
715    setFillColor(color, colorSpace);
716
717    fillPath(path);
718
719    setFillRule(oldFillRule);
720    setFillColor(oldFillColor, oldFillColorSpace);
721}
722#endif
723
724void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
725{
726    m_state.compositeOperator = compositeOperation;
727    m_state.blendMode = blendMode;
728    setPlatformCompositeOperation(compositeOperation, blendMode);
729}
730
731CompositeOperator GraphicsContext::compositeOperation() const
732{
733    return m_state.compositeOperator;
734}
735
736BlendMode GraphicsContext::blendModeOperation() const
737{
738    return m_state.blendMode;
739}
740
741#if !USE(CG)
742// Implement this if you want to go ahead and push the drawing mode into your native context
743// immediately.
744void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
745{
746}
747#endif
748
749#if !PLATFORM(QT) && !USE(CAIRO)
750void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
751{
752}
753#endif
754
755#if !USE(CG)
756void GraphicsContext::setPlatformShouldSmoothFonts(bool)
757{
758}
759#endif
760
761#if !USE(CG) && !USE(CAIRO)
762bool GraphicsContext::isAcceleratedContext() const
763{
764    return false;
765}
766#endif
767
768void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
769{
770    // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
771    // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
772    // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
773    // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
774    if (penStyle == DottedStroke || penStyle == DashedStroke) {
775        if (p1.x() == p2.x()) {
776            p1.setY(p1.y() + strokeWidth);
777            p2.setY(p2.y() - strokeWidth);
778        } else {
779            p1.setX(p1.x() + strokeWidth);
780            p2.setX(p2.x() - strokeWidth);
781        }
782    }
783
784    if (static_cast<int>(strokeWidth) % 2) { //odd
785        if (p1.x() == p2.x()) {
786            // We're a vertical line.  Adjust our x.
787            p1.setX(p1.x() + 0.5f);
788            p2.setX(p2.x() + 0.5f);
789        } else {
790            // We're a horizontal line. Adjust our y.
791            p1.setY(p1.y() + 0.5f);
792            p2.setY(p2.y() + 0.5f);
793        }
794    }
795}
796
797static bool scalesMatch(AffineTransform a, AffineTransform b)
798{
799    return a.xScale() == b.xScale() && a.yScale() == b.yScale();
800}
801
802PassOwnPtr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& size, bool hasAlpha) const
803{
804    // Make the buffer larger if the context's transform is scaling it so we need a higher
805    // resolution than one pixel per unit. Also set up a corresponding scale factor on the
806    // graphics context.
807
808    AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale);
809    IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
810
811    OwnPtr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceDeviceRGB, this, hasAlpha);
812    if (!buffer)
813        return nullptr;
814
815    buffer->context()->scale(FloatSize(static_cast<float>(scaledSize.width()) / size.width(),
816        static_cast<float>(scaledSize.height()) / size.height()));
817
818    return buffer.release();
819}
820
821bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer* buffer) const
822{
823    GraphicsContext* bufferContext = buffer->context();
824
825    return scalesMatch(getCTM(), bufferContext->getCTM()) && isAcceleratedContext() == bufferContext->isAcceleratedContext();
826}
827
828#if !USE(CG)
829void GraphicsContext::platformApplyDeviceScaleFactor(float)
830{
831}
832#endif
833
834void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
835{
836    scale(FloatSize(deviceScaleFactor, deviceScaleFactor));
837    platformApplyDeviceScaleFactor(deviceScaleFactor);
838}
839
840void GraphicsContext::fillEllipse(const FloatRect& ellipse)
841{
842    platformFillEllipse(ellipse);
843}
844
845void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
846{
847    platformStrokeEllipse(ellipse);
848}
849
850void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse)
851{
852    Path path;
853    path.addEllipse(ellipse);
854    fillPath(path);
855}
856
857void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
858{
859    Path path;
860    path.addEllipse(ellipse);
861    strokePath(path);
862}
863
864#if !USE(CG)
865void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
866{
867    if (paintingDisabled())
868        return;
869
870    fillEllipseAsPath(ellipse);
871}
872
873void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
874{
875    if (paintingDisabled())
876        return;
877
878    strokeEllipseAsPath(ellipse);
879}
880#endif
881
882}
883