0468397b2867e17b1692f359a144498760d65ed5
[WebKit-https.git] / Source / WebCore / platform / graphics / GraphicsContext.cpp
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
40 namespace WebCore {
41
42 class TextRunIterator {
43 public:
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
75 private:
76     const TextRun* m_textRun;
77     int m_offset;
78 };
79
80 class InterpolationQualityMaintainer {
81 public:
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
97 private:
98     GraphicsContext& m_graphicsContext;
99     InterpolationQuality m_currentInterpolationQuality;
100     bool m_interpolationQualityChanged;
101 };
102
103 #if !PLATFORM(IOS)
104 GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
105     : m_updatingControlTints(false)
106     , m_transparencyCount(0)
107 {
108     platformInit(platformGraphicsContext);
109 }
110 #else
111 GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext, bool shouldUseContextColors)
112     : m_updatingControlTints(false)
113     , m_transparencyCount(0)
114 {
115     platformInit(platformGraphicsContext, shouldUseContextColors);
116 }
117 #endif
118
119 GraphicsContext::~GraphicsContext()
120 {
121     ASSERT(m_stack.isEmpty());
122     ASSERT(!m_transparencyCount);
123     platformDestroy();
124 }
125
126 void GraphicsContext::save()
127 {
128     if (paintingDisabled())
129         return;
130
131     m_stack.append(m_state);
132
133     savePlatformState();
134 }
135
136 void 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)
152 void 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
173 void GraphicsContext::setStrokeThickness(float thickness)
174 {
175     m_state.strokeThickness = thickness;
176     setPlatformStrokeThickness(thickness);
177 }
178
179 void GraphicsContext::setStrokeStyle(StrokeStyle style)
180 {
181     m_state.strokeStyle = style;
182     setPlatformStrokeStyle(style);
183 }
184
185 void 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
194 void 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
203 void 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
215 void 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
224 bool 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
230 bool 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
240 bool GraphicsContext::hasBlurredShadow() const
241 {
242     return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() && m_state.shadowBlur;
243 }
244
245 #if USE(CAIRO)
246 bool 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
263 float GraphicsContext::strokeThickness() const
264 {
265     return m_state.strokeThickness;
266 }
267
268 StrokeStyle GraphicsContext::strokeStyle() const
269 {
270     return m_state.strokeStyle;
271 }
272
273 Color GraphicsContext::strokeColor() const
274 {
275     return m_state.strokeColor;
276 }
277
278 ColorSpace GraphicsContext::strokeColorSpace() const
279 {
280     return m_state.strokeColorSpace;
281 }
282
283 WindRule GraphicsContext::fillRule() const
284 {
285     return m_state.fillRule;
286 }
287
288 void GraphicsContext::setFillRule(WindRule fillRule)
289 {
290     m_state.fillRule = fillRule;
291 }
292
293 void 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
302 Color GraphicsContext::fillColor() const
303 {
304     return m_state.fillColor;
305 }
306
307 ColorSpace GraphicsContext::fillColorSpace() const
308 {
309     return m_state.fillColorSpace;
310 }
311
312 void GraphicsContext::setShouldAntialias(bool b)
313 {
314     m_state.shouldAntialias = b;
315     setPlatformShouldAntialias(b);
316 }
317
318 bool GraphicsContext::shouldAntialias() const
319 {
320     return m_state.shouldAntialias;
321 }
322
323 void GraphicsContext::setShouldSmoothFonts(bool b)
324 {
325     m_state.shouldSmoothFonts = b;
326     setPlatformShouldSmoothFonts(b);
327 }
328
329 bool GraphicsContext::shouldSmoothFonts() const
330 {
331     return m_state.shouldSmoothFonts;
332 }
333
334 void GraphicsContext::setShouldSubpixelQuantizeFonts(bool b)
335 {
336     m_state.shouldSubpixelQuantizeFonts = b;
337 }
338
339 bool GraphicsContext::shouldSubpixelQuantizeFonts() const
340 {
341     return m_state.shouldSubpixelQuantizeFonts;
342 }
343
344 const GraphicsContextState& GraphicsContext::state() const
345 {
346     return m_state;
347 }
348
349 void 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
360 void 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
371 void 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
382 void 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
393 Gradient* GraphicsContext::fillGradient() const
394 {
395     return m_state.fillGradient.get();
396 }
397
398 Gradient* GraphicsContext::strokeGradient() const
399 {
400     return m_state.strokeGradient.get();
401 }
402
403 Pattern* GraphicsContext::fillPattern() const
404 {
405     return m_state.fillPattern.get();
406 }
407
408 Pattern* GraphicsContext::strokePattern() const
409 {
410     return m_state.strokePattern.get();
411 }
412
413 void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
414 {
415     m_state.shadowsIgnoreTransforms = ignoreTransforms;
416 }
417
418 bool GraphicsContext::shadowsIgnoreTransforms() const
419 {
420     return m_state.shadowsIgnoreTransforms;
421 }
422
423 void GraphicsContext::beginTransparencyLayer(float opacity)
424 {
425     beginPlatformTransparencyLayer(opacity);
426     ++m_transparencyCount;
427 }
428
429 void GraphicsContext::endTransparencyLayer()
430 {
431     endPlatformTransparencyLayer();
432     ASSERT(m_transparencyCount > 0);
433     --m_transparencyCount;
434 }
435
436 bool GraphicsContext::isInTransparencyLayer() const
437 {
438     return (m_transparencyCount > 0) && supportsTransparencyLayers();
439 }
440
441 bool GraphicsContext::updatingControlTints() const
442 {
443     return m_updatingControlTints;
444 }
445
446 void GraphicsContext::setUpdatingControlTints(bool b)
447 {
448     setPaintingDisabled(b);
449     m_updatingControlTints = b;
450 }
451
452 void GraphicsContext::setPaintingDisabled(bool f)
453 {
454     m_state.paintingDisabled = f;
455 }
456
457 bool 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)
466 void 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
475 float 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
484 void 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
492 void 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)
504 void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
505 #else
506 float 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
573 void 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
580 void 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
594 void 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
604 void 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
614 void 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
630 void 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
637 void 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
644 void 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
654 void GraphicsContext::clip(const IntRect& rect)
655 {
656     clip(FloatRect(rect));
657 }
658
659 void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect)
660 {
661     if (paintingDisabled())
662         return;
663
664     Path path;
665     path.addRoundedRect(rect);
666     clip(path);
667 }
668
669 void 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
684 void 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)
692 IntRect GraphicsContext::clipBounds() const
693 {
694     ASSERT_NOT_REACHED();
695     return IntRect();
696 }
697 #endif
698
699 TextDrawingModeFlags GraphicsContext::textDrawingMode() const
700 {
701     return m_state.textDrawingMode;
702 }
703
704 void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
705 {
706     m_state.textDrawingMode = mode;
707     if (paintingDisabled())
708         return;
709     setPlatformTextDrawingMode(mode);
710 }
711
712 void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
713 {
714     if (paintingDisabled())
715         return;
716     gradient.fill(this, rect);
717 }
718
719 void 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
730 void 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)
741 void 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
768 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
769 {
770     m_state.compositeOperator = compositeOperation;
771     m_state.blendMode = blendMode;
772     setPlatformCompositeOperation(compositeOperation, blendMode);
773 }
774
775 CompositeOperator GraphicsContext::compositeOperation() const
776 {
777     return m_state.compositeOperator;
778 }
779
780 BlendMode GraphicsContext::blendModeOperation() const
781 {
782     return m_state.blendMode;
783 }
784
785 #if PLATFORM(IOS)
786 bool GraphicsContext::emojiDrawingEnabled()
787 {
788     return m_state.emojiDrawingEnabled;
789 }
790
791 void GraphicsContext::setEmojiDrawingEnabled(bool emojiDrawingEnabled)
792 {
793     m_state.emojiDrawingEnabled = emojiDrawingEnabled;
794 }
795 #endif
796
797 void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask)
798 {
799     m_state.drawLuminanceMask = drawLuminanceMask;
800 }
801
802 bool 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.
810 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
811 {
812 }
813 #endif
814
815 #if !USE(CAIRO)
816 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
817 {
818 }
819 #endif
820
821 #if !USE(CG)
822 void GraphicsContext::setPlatformShouldSmoothFonts(bool)
823 {
824 }
825 #endif
826
827 #if !USE(CG) && !USE(CAIRO)
828 bool GraphicsContext::isAcceleratedContext() const
829 {
830     return false;
831 }
832 #endif
833
834 void 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
863 static bool scalesMatch(AffineTransform a, AffineTransform b)
864 {
865     return a.xScale() == b.xScale() && a.yScale() == b.yScale();
866 }
867
868 std::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
886 bool 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)
894 void GraphicsContext::platformApplyDeviceScaleFactor(float)
895 {
896 }
897 #endif
898
899 void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
900 {
901     scale(FloatSize(deviceScaleFactor, deviceScaleFactor));
902     platformApplyDeviceScaleFactor(deviceScaleFactor);
903 }
904
905 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
906 {
907     platformFillEllipse(ellipse);
908 }
909
910 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
911 {
912     platformStrokeEllipse(ellipse);
913 }
914
915 void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse)
916 {
917     Path path;
918     path.addEllipse(ellipse);
919     fillPath(path);
920 }
921
922 void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
923 {
924     Path path;
925     path.addEllipse(ellipse);
926     strokePath(path);
927 }
928
929 #if !USE(CG)
930 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
931 {
932     if (paintingDisabled())
933         return;
934
935     fillEllipseAsPath(ellipse);
936 }
937
938 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
939 {
940     if (paintingDisabled())
941         return;
942
943     strokeEllipseAsPath(ellipse);
944 }
945 #endif
946
947 FloatRect 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 }