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