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 "FloatRoundedRect.h"
32#include "Gradient.h"
33#include "ImageBuffer.h"
34#include "IntRect.h"
35#include "RoundedRect.h"
36#include "TextRun.h"
37
38#include "stdio.h"
39
40namespace WebCore {
41
42class TextRunIterator {
43public:
44    TextRunIterator()
45        : m_textRun(0)
46        , m_offset(0)
47    {
48    }
49
50    TextRunIterator(const TextRun* textRun, unsigned offset)
51        : m_textRun(textRun)
52        , m_offset(offset)
53    {
54    }
55
56    TextRunIterator(const TextRunIterator& other)
57        : m_textRun(other.m_textRun)
58        , m_offset(other.m_offset)
59    {
60    }
61
62    unsigned offset() const { return m_offset; }
63    void increment() { m_offset++; }
64    bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
65    UChar current() const { return (*m_textRun)[m_offset]; }
66    UCharDirection direction() const { return atEnd() ? U_OTHER_NEUTRAL : u_charDirection(current()); }
67
68    bool operator==(const TextRunIterator& other)
69    {
70        return m_offset == other.m_offset && m_textRun == other.m_textRun;
71    }
72
73    bool operator!=(const TextRunIterator& other) { return !operator==(other); }
74
75private:
76    const TextRun* m_textRun;
77    int m_offset;
78};
79
80class InterpolationQualityMaintainer {
81public:
82    explicit InterpolationQualityMaintainer(GraphicsContext& graphicsContext, InterpolationQuality interpolationQualityToUse)
83        : m_graphicsContext(graphicsContext)
84        , m_currentInterpolationQuality(graphicsContext.imageInterpolationQuality())
85        , m_interpolationQualityChanged(m_currentInterpolationQuality != interpolationQualityToUse)
86    {
87        if (m_interpolationQualityChanged)
88            m_graphicsContext.setImageInterpolationQuality(interpolationQualityToUse);
89    }
90
91    ~InterpolationQualityMaintainer()
92    {
93        if (m_interpolationQualityChanged)
94            m_graphicsContext.setImageInterpolationQuality(m_currentInterpolationQuality);
95    }
96
97private:
98    GraphicsContext& m_graphicsContext;
99    InterpolationQuality m_currentInterpolationQuality;
100    bool m_interpolationQualityChanged;
101};
102
103#if !PLATFORM(IOS)
104GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
105    : m_updatingControlTints(false)
106    , m_transparencyCount(0)
107{
108    platformInit(platformGraphicsContext);
109}
110#else
111GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext, bool shouldUseContextColors)
112    : m_updatingControlTints(false)
113    , m_transparencyCount(0)
114{
115    platformInit(platformGraphicsContext, shouldUseContextColors);
116}
117#endif
118
119GraphicsContext::~GraphicsContext()
120{
121    ASSERT(m_stack.isEmpty());
122    ASSERT(!m_transparencyCount);
123    platformDestroy();
124}
125
126void GraphicsContext::save()
127{
128    if (paintingDisabled())
129        return;
130
131    m_stack.append(m_state);
132
133    savePlatformState();
134}
135
136void GraphicsContext::restore()
137{
138    if (paintingDisabled())
139        return;
140
141    if (m_stack.isEmpty()) {
142        LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
143        return;
144    }
145    m_state = m_stack.last();
146    m_stack.removeLast();
147
148    restorePlatformState();
149}
150
151#if PLATFORM(IOS)
152void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace)
153{
154    if (paintingDisabled())
155        return;
156
157    save();
158
159    setStrokeColor(shadowColor, shadowColorSpace);
160    setFillColor(shadowColor, shadowColorSpace);
161
162    drawEllipse(FloatRect(rect.x(), rect.y() + 1, rect.width(), rect.height()));
163
164    setStrokeColor(ellipseColor, ellipseColorSpace);
165    setFillColor(ellipseColor, ellipseColorSpace);
166
167    drawEllipse(rect);
168
169    restore();
170}
171#endif
172
173void GraphicsContext::setStrokeThickness(float thickness)
174{
175    m_state.strokeThickness = thickness;
176    setPlatformStrokeThickness(thickness);
177}
178
179void GraphicsContext::setStrokeStyle(StrokeStyle style)
180{
181    m_state.strokeStyle = style;
182    setPlatformStrokeStyle(style);
183}
184
185void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
186{
187    m_state.strokeColor = color;
188    m_state.strokeColorSpace = colorSpace;
189    m_state.strokeGradient.clear();
190    m_state.strokePattern.clear();
191    setPlatformStrokeColor(color, colorSpace);
192}
193
194void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
195{
196    m_state.shadowOffset = offset;
197    m_state.shadowBlur = blur;
198    m_state.shadowColor = color;
199    m_state.shadowColorSpace = colorSpace;
200    setPlatformShadow(offset, blur, color, colorSpace);
201}
202
203void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
204{
205    m_state.shadowOffset = offset;
206    m_state.shadowBlur = blur;
207    m_state.shadowColor = color;
208    m_state.shadowColorSpace = colorSpace;
209#if USE(CG)
210    m_state.shadowsUseLegacyRadius = true;
211#endif
212    setPlatformShadow(offset, blur, color, colorSpace);
213}
214
215void GraphicsContext::clearShadow()
216{
217    m_state.shadowOffset = FloatSize();
218    m_state.shadowBlur = 0;
219    m_state.shadowColor = Color();
220    m_state.shadowColorSpace = ColorSpaceDeviceRGB;
221    clearPlatformShadow();
222}
223
224bool GraphicsContext::hasShadow() const
225{
226    return m_state.shadowColor.isValid() && m_state.shadowColor.alpha()
227           && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height());
228}
229
230bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const
231{
232    offset = m_state.shadowOffset;
233    blur = m_state.shadowBlur;
234    color = m_state.shadowColor;
235    colorSpace = m_state.shadowColorSpace;
236
237    return hasShadow();
238}
239
240bool GraphicsContext::hasBlurredShadow() const
241{
242    return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() && m_state.shadowBlur;
243}
244
245#if USE(CAIRO)
246bool GraphicsContext::mustUseShadowBlur() const
247{
248    // We can't avoid ShadowBlur if the shadow has blur.
249    if (hasBlurredShadow())
250        return true;
251    // We can avoid ShadowBlur and optimize, since we're not drawing on a
252    // canvas and box shadows are affected by the transformation matrix.
253    if (!m_state.shadowsIgnoreTransforms)
254        return false;
255    // We can avoid ShadowBlur, since there are no transformations to apply to the canvas.
256    if (getCTM().isIdentity())
257        return false;
258    // Otherwise, no chance avoiding ShadowBlur.
259    return true;
260}
261#endif
262
263float GraphicsContext::strokeThickness() const
264{
265    return m_state.strokeThickness;
266}
267
268StrokeStyle GraphicsContext::strokeStyle() const
269{
270    return m_state.strokeStyle;
271}
272
273Color GraphicsContext::strokeColor() const
274{
275    return m_state.strokeColor;
276}
277
278ColorSpace GraphicsContext::strokeColorSpace() const
279{
280    return m_state.strokeColorSpace;
281}
282
283WindRule GraphicsContext::fillRule() const
284{
285    return m_state.fillRule;
286}
287
288void GraphicsContext::setFillRule(WindRule fillRule)
289{
290    m_state.fillRule = fillRule;
291}
292
293void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
294{
295    m_state.fillColor = color;
296    m_state.fillColorSpace = colorSpace;
297    m_state.fillGradient.clear();
298    m_state.fillPattern.clear();
299    setPlatformFillColor(color, colorSpace);
300}
301
302Color GraphicsContext::fillColor() const
303{
304    return m_state.fillColor;
305}
306
307ColorSpace GraphicsContext::fillColorSpace() const
308{
309    return m_state.fillColorSpace;
310}
311
312void GraphicsContext::setShouldAntialias(bool b)
313{
314    m_state.shouldAntialias = b;
315    setPlatformShouldAntialias(b);
316}
317
318bool GraphicsContext::shouldAntialias() const
319{
320    return m_state.shouldAntialias;
321}
322
323void GraphicsContext::setShouldSmoothFonts(bool b)
324{
325    m_state.shouldSmoothFonts = b;
326    setPlatformShouldSmoothFonts(b);
327}
328
329bool GraphicsContext::shouldSmoothFonts() const
330{
331    return m_state.shouldSmoothFonts;
332}
333
334void GraphicsContext::setShouldSubpixelQuantizeFonts(bool b)
335{
336    m_state.shouldSubpixelQuantizeFonts = b;
337}
338
339bool GraphicsContext::shouldSubpixelQuantizeFonts() const
340{
341    return m_state.shouldSubpixelQuantizeFonts;
342}
343
344const GraphicsContextState& GraphicsContext::state() const
345{
346    return m_state;
347}
348
349void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
350{
351    ASSERT(pattern);
352    if (!pattern) {
353        setStrokeColor(Color::black, ColorSpaceDeviceRGB);
354        return;
355    }
356    m_state.strokeGradient.clear();
357    m_state.strokePattern = pattern;
358}
359
360void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
361{
362    ASSERT(pattern);
363    if (!pattern) {
364        setFillColor(Color::black, ColorSpaceDeviceRGB);
365        return;
366    }
367    m_state.fillGradient.clear();
368    m_state.fillPattern = pattern;
369}
370
371void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
372{
373    ASSERT(gradient);
374    if (!gradient) {
375        setStrokeColor(Color::black, ColorSpaceDeviceRGB);
376        return;
377    }
378    m_state.strokeGradient = gradient;
379    m_state.strokePattern.clear();
380}
381
382void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
383{
384    ASSERT(gradient);
385    if (!gradient) {
386        setFillColor(Color::black, ColorSpaceDeviceRGB);
387        return;
388    }
389    m_state.fillGradient = gradient;
390    m_state.fillPattern.clear();
391}
392
393Gradient* GraphicsContext::fillGradient() const
394{
395    return m_state.fillGradient.get();
396}
397
398Gradient* GraphicsContext::strokeGradient() const
399{
400    return m_state.strokeGradient.get();
401}
402
403Pattern* GraphicsContext::fillPattern() const
404{
405    return m_state.fillPattern.get();
406}
407
408Pattern* GraphicsContext::strokePattern() const
409{
410    return m_state.strokePattern.get();
411}
412
413void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
414{
415    m_state.shadowsIgnoreTransforms = ignoreTransforms;
416}
417
418bool GraphicsContext::shadowsIgnoreTransforms() const
419{
420    return m_state.shadowsIgnoreTransforms;
421}
422
423void GraphicsContext::beginTransparencyLayer(float opacity)
424{
425    beginPlatformTransparencyLayer(opacity);
426    ++m_transparencyCount;
427}
428
429void GraphicsContext::endTransparencyLayer()
430{
431    endPlatformTransparencyLayer();
432    ASSERT(m_transparencyCount > 0);
433    --m_transparencyCount;
434}
435
436bool GraphicsContext::isInTransparencyLayer() const
437{
438    return (m_transparencyCount > 0) && supportsTransparencyLayers();
439}
440
441bool GraphicsContext::updatingControlTints() const
442{
443    return m_updatingControlTints;
444}
445
446void GraphicsContext::setUpdatingControlTints(bool b)
447{
448    setPaintingDisabled(b);
449    m_updatingControlTints = b;
450}
451
452void GraphicsContext::setPaintingDisabled(bool f)
453{
454    m_state.paintingDisabled = f;
455}
456
457bool GraphicsContext::paintingDisabled() const
458{
459    return m_state.paintingDisabled;
460}
461
462// FIXME: Replace the non-iOS implementation with the iOS implementation since the latter computes returns
463// the width of the drawn text. Ensure that there aren't noticeable differences in layout.
464#if !PLATFORM(IOS)
465#if !USE(WINGDI)
466void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
467{
468    if (paintingDisabled())
469        return;
470
471    font.drawText(this, run, point, from, to);
472}
473#endif
474#else
475float GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
476{
477    if (paintingDisabled())
478        return 0;
479
480    return font.drawText(this, run, point, from, to);
481}
482#endif // !PLATFORM(IOS)
483
484void GraphicsContext::drawGlyphs(const Font& font, const SimpleFontData& fontData, const GlyphBuffer& buffer, int from, int numGlyphs, const FloatPoint& point)
485{
486    if (paintingDisabled())
487        return;
488
489    font.drawGlyphs(this, &fontData, buffer, from, numGlyphs, point);
490}
491
492void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to)
493{
494    if (paintingDisabled())
495        return;
496
497    font.drawEmphasisMarks(this, run, mark, point, from, to);
498}
499
500// FIXME: Better merge the iOS and non-iOS differences. In particular, make this method use the
501// returned width of the drawn text, Font::drawText(), instead of computing it. Ensure that there
502// aren't noticeable differences in layout with such a change.
503#if !PLATFORM(IOS)
504void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
505#else
506float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction, BidiStatus* status, int length)
507#endif
508{
509    if (paintingDisabled())
510#if !PLATFORM(IOS)
511        return;
512#else
513        return 0;
514#endif
515
516    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
517#if !PLATFORM(IOS)
518    bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
519#else
520    bidiResolver.setStatus(status ? *status : BidiStatus(run.direction(), run.directionalOverride()));
521#endif
522    bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
523
524    // FIXME: This ownership should be reversed. We should pass BidiRunList
525    // to BidiResolver in createBidiRunsForLine.
526    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
527#if !PLATFORM(IOS)
528    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
529#else
530    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, length < 0 ? run.length() : length));
531#endif
532    if (!bidiRuns.runCount())
533#if !PLATFORM(IOS)
534        return;
535#else
536        return 0;
537#endif
538
539    FloatPoint currPoint = point;
540    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
541    while (bidiRun) {
542        TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
543        bool isRTL = bidiRun->level() % 2;
544        subrun.setDirection(isRTL ? RTL : LTR);
545        subrun.setDirectionalOverride(bidiRun->dirOverride(false));
546
547#if !PLATFORM(IOS)
548        font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
549
550        bidiRun = bidiRun->next();
551        // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
552        if (bidiRun)
553            currPoint.move(font.width(subrun), 0);
554#else
555        float width = font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
556        currPoint.move(width, 0);
557
558        bidiRun = bidiRun->next();
559#endif
560    }
561
562#if PLATFORM(IOS)
563    if (status)
564        *status = bidiResolver.status();
565#endif
566    bidiRuns.deleteRuns();
567
568#if PLATFORM(IOS)
569    return currPoint.x() - static_cast<float>(point.x());
570#endif
571}
572
573void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
574{
575    if (!image)
576        return;
577    drawImage(image, colorSpace, FloatRect(destination, image->size()), FloatRect(FloatPoint(), image->size()), imagePaintingOptions);
578}
579
580void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
581{
582    if (!image)
583        return;
584
585#if PLATFORM(IOS)
586    FloatRect srcRect(FloatPoint(), image->originalSize());
587#else
588    FloatRect srcRect(FloatPoint(), image->size());
589#endif
590
591    drawImage(image, colorSpace, destination, srcRect, imagePaintingOptions);
592}
593
594void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
595{
596    if (paintingDisabled() || !image)
597        return;
598
599    // FIXME (49002): Should be InterpolationLow
600    InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality());
601    image->draw(this, destination, source, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_orientationDescription);
602}
603
604void GraphicsContext::drawTiledImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize,
605    const ImagePaintingOptions& imagePaintingOptions)
606{
607    if (paintingDisabled() || !image)
608        return;
609
610    InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality());
611    image->drawTiled(this, destination, source, tileSize, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
612}
613
614void GraphicsContext::drawTiledImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor,
615    Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
616{
617    if (paintingDisabled() || !image)
618        return;
619
620    if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
621        // Just do a scale.
622        drawImage(image, colorSpace, destination, source, imagePaintingOptions);
623        return;
624    }
625
626    InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality());
627    image->drawTiled(this, destination, source, tileScaleFactor, hRule, vRule, colorSpace, imagePaintingOptions.m_compositeOperator);
628}
629
630void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
631{
632    if (!image)
633        return;
634    drawImageBuffer(image, colorSpace, FloatRect(destination, image->logicalSize()), FloatRect(FloatPoint(), image->logicalSize()), imagePaintingOptions);
635}
636
637void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
638{
639    if (!image)
640        return;
641    drawImageBuffer(image, colorSpace, destination, FloatRect(FloatPoint(), FloatSize(image->logicalSize())), imagePaintingOptions);
642}
643
644void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
645{
646    if (paintingDisabled() || !image)
647        return;
648
649    // FIXME (49002): Should be InterpolationLow
650    InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality());
651    image->draw(this, colorSpace, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_useLowQualityScale);
652}
653
654void GraphicsContext::clip(const IntRect& rect)
655{
656    clip(FloatRect(rect));
657}
658
659void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect)
660{
661    if (paintingDisabled())
662        return;
663
664    Path path;
665    path.addRoundedRect(rect);
666    clip(path);
667}
668
669void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect)
670{
671    if (paintingDisabled())
672        return;
673
674    if (!rect.isRounded()) {
675        clipOut(rect.rect());
676        return;
677    }
678
679    Path path;
680    path.addRoundedRect(rect);
681    clipOut(path);
682}
683
684void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect)
685{
686    if (paintingDisabled())
687        return;
688    buffer->clip(this, rect);
689}
690
691#if !USE(CG) && !USE(CAIRO)
692IntRect GraphicsContext::clipBounds() const
693{
694    ASSERT_NOT_REACHED();
695    return IntRect();
696}
697#endif
698
699TextDrawingModeFlags GraphicsContext::textDrawingMode() const
700{
701    return m_state.textDrawingMode;
702}
703
704void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
705{
706    m_state.textDrawingMode = mode;
707    if (paintingDisabled())
708        return;
709    setPlatformTextDrawingMode(mode);
710}
711
712void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
713{
714    if (paintingDisabled())
715        return;
716    gradient.fill(this, rect);
717}
718
719void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode)
720{
721    if (paintingDisabled())
722        return;
723
724    CompositeOperator previousOperator = compositeOperation();
725    setCompositeOperation(op, blendMode);
726    fillRect(rect, color, styleColorSpace);
727    setCompositeOperation(previousOperator);
728}
729
730void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace colorSpace, BlendMode blendMode)
731{
732    if (rect.isRounded()) {
733        setCompositeOperation(compositeOperation(), blendMode);
734        platformFillRoundedRect(rect, color, colorSpace);
735        setCompositeOperation(compositeOperation());
736    } else
737        fillRect(rect.rect(), color, colorSpace, compositeOperation(), blendMode);
738}
739
740#if !USE(CG) && !USE(CAIRO)
741void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
742{
743    if (paintingDisabled())
744        return;
745
746    Path path;
747    path.addRect(rect);
748
749    if (!roundedHoleRect.radii().isZero())
750        path.addRoundedRect(roundedHoleRect);
751    else
752        path.addRect(roundedHoleRect.rect());
753
754    WindRule oldFillRule = fillRule();
755    Color oldFillColor = fillColor();
756    ColorSpace oldFillColorSpace = fillColorSpace();
757
758    setFillRule(RULE_EVENODD);
759    setFillColor(color, colorSpace);
760
761    fillPath(path);
762
763    setFillRule(oldFillRule);
764    setFillColor(oldFillColor, oldFillColorSpace);
765}
766#endif
767
768void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
769{
770    m_state.compositeOperator = compositeOperation;
771    m_state.blendMode = blendMode;
772    setPlatformCompositeOperation(compositeOperation, blendMode);
773}
774
775CompositeOperator GraphicsContext::compositeOperation() const
776{
777    return m_state.compositeOperator;
778}
779
780BlendMode GraphicsContext::blendModeOperation() const
781{
782    return m_state.blendMode;
783}
784
785#if PLATFORM(IOS)
786bool GraphicsContext::emojiDrawingEnabled()
787{
788    return m_state.emojiDrawingEnabled;
789}
790
791void GraphicsContext::setEmojiDrawingEnabled(bool emojiDrawingEnabled)
792{
793    m_state.emojiDrawingEnabled = emojiDrawingEnabled;
794}
795#endif
796
797void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask)
798{
799    m_state.drawLuminanceMask = drawLuminanceMask;
800}
801
802bool GraphicsContext::drawLuminanceMask() const
803{
804    return m_state.drawLuminanceMask;
805}
806
807#if !USE(CG)
808// Implement this if you want to go ahead and push the drawing mode into your native context
809// immediately.
810void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
811{
812}
813#endif
814
815#if !USE(CAIRO)
816void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
817{
818}
819#endif
820
821#if !USE(CG)
822void GraphicsContext::setPlatformShouldSmoothFonts(bool)
823{
824}
825#endif
826
827#if !USE(CG) && !USE(CAIRO)
828bool GraphicsContext::isAcceleratedContext() const
829{
830    return false;
831}
832#endif
833
834void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
835{
836    // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
837    // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
838    // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
839    // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
840    if (penStyle == DottedStroke || penStyle == DashedStroke) {
841        if (p1.x() == p2.x()) {
842            p1.setY(p1.y() + strokeWidth);
843            p2.setY(p2.y() - strokeWidth);
844        } else {
845            p1.setX(p1.x() + strokeWidth);
846            p2.setX(p2.x() - strokeWidth);
847        }
848    }
849
850    if (static_cast<int>(strokeWidth) % 2) { //odd
851        if (p1.x() == p2.x()) {
852            // We're a vertical line.  Adjust our x.
853            p1.setX(p1.x() + 0.5f);
854            p2.setX(p2.x() + 0.5f);
855        } else {
856            // We're a horizontal line. Adjust our y.
857            p1.setY(p1.y() + 0.5f);
858            p2.setY(p2.y() + 0.5f);
859        }
860    }
861}
862
863static bool scalesMatch(AffineTransform a, AffineTransform b)
864{
865    return a.xScale() == b.xScale() && a.yScale() == b.yScale();
866}
867
868std::unique_ptr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const FloatSize& size, bool hasAlpha) const
869{
870    // Make the buffer larger if the context's transform is scaling it so we need a higher
871    // resolution than one pixel per unit. Also set up a corresponding scale factor on the
872    // graphics context.
873
874    AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale);
875    FloatSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
876
877    std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceDeviceRGB, this, hasAlpha);
878    if (!buffer)
879        return nullptr;
880
881    buffer->context()->scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height()));
882
883    return buffer;
884}
885
886bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer* buffer) const
887{
888    GraphicsContext* bufferContext = buffer->context();
889
890    return scalesMatch(getCTM(), bufferContext->getCTM()) && isAcceleratedContext() == bufferContext->isAcceleratedContext();
891}
892
893#if !USE(CG)
894void GraphicsContext::platformApplyDeviceScaleFactor(float)
895{
896}
897#endif
898
899void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
900{
901    scale(FloatSize(deviceScaleFactor, deviceScaleFactor));
902    platformApplyDeviceScaleFactor(deviceScaleFactor);
903}
904
905void GraphicsContext::fillEllipse(const FloatRect& ellipse)
906{
907    platformFillEllipse(ellipse);
908}
909
910void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
911{
912    platformStrokeEllipse(ellipse);
913}
914
915void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse)
916{
917    Path path;
918    path.addEllipse(ellipse);
919    fillPath(path);
920}
921
922void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
923{
924    Path path;
925    path.addEllipse(ellipse);
926    strokePath(path);
927}
928
929#if !USE(CG)
930void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
931{
932    if (paintingDisabled())
933        return;
934
935    fillEllipseAsPath(ellipse);
936}
937
938void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
939{
940    if (paintingDisabled())
941        return;
942
943    strokeEllipseAsPath(ellipse);
944}
945#endif
946
947FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatPoint& point, float width, bool printing, bool& shouldAntialias, Color& color)
948{
949    FloatPoint origin;
950    float thickness = std::max(strokeThickness(), 0.5f);
951
952    shouldAntialias = true;
953    if (printing)
954        origin = point;
955    else {
956        AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
957        if (transform.preservesAxisAlignment())
958            shouldAntialias = false;
959
960        // This code always draws a line that is at least one-pixel line high,
961        // which tends to visually overwhelm text at small scales. To counter this
962        // effect, an alpha is applied to the underline color when text is at small scales.
963
964        // Just compute scale in x dimension, assuming x and y scales are equal.
965        float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a();
966        if (scale < 1.0) {
967            static const float minimumUnderlineAlpha = 0.4f;
968            float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha;
969            int alpha = color.alpha() * shade;
970            color = Color(color.red(), color.green(), color.blue(), alpha);
971        }
972
973        FloatPoint devicePoint = transform.mapPoint(point);
974        FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y()));
975        origin = transform.inverse().mapPoint(deviceOrigin);
976    }
977    return FloatRect(origin.x(), origin.y(), width, thickness);
978}
979
980}
981