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