2b4a2f799f9f50525d9e98b5610c1c45d54aaa3e
[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::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace)
146 {
147     if (paintingDisabled())
148         return;
149
150     save();
151
152     setStrokeColor(shadowColor, shadowColorSpace);
153     setFillColor(shadowColor, shadowColorSpace);
154
155     drawEllipse(FloatRect(rect.x(), rect.y() + 1, rect.width(), rect.height()));
156
157     setStrokeColor(ellipseColor, ellipseColorSpace);
158     setFillColor(ellipseColor, ellipseColorSpace);
159
160     drawEllipse(rect);  
161
162     restore();
163 }
164
165 void GraphicsContext::setStrokeThickness(float thickness)
166 {
167     m_state.strokeThickness = thickness;
168     setPlatformStrokeThickness(thickness);
169 }
170
171 void GraphicsContext::setStrokeStyle(StrokeStyle style)
172 {
173     m_state.strokeStyle = style;
174     setPlatformStrokeStyle(style);
175 }
176
177 void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
178 {
179     m_state.strokeColor = color;
180     m_state.strokeColorSpace = colorSpace;
181     m_state.strokeGradient = nullptr;
182     m_state.strokePattern = nullptr;
183     setPlatformStrokeColor(color, colorSpace);
184 }
185
186 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
187 {
188     m_state.shadowOffset = offset;
189     m_state.shadowBlur = blur;
190     m_state.shadowColor = color;
191     m_state.shadowColorSpace = colorSpace;
192     setPlatformShadow(offset, blur, color, colorSpace);
193 }
194
195 void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
196 {
197     m_state.shadowOffset = offset;
198     m_state.shadowBlur = blur;
199     m_state.shadowColor = color;
200     m_state.shadowColorSpace = colorSpace;
201 #if USE(CG)
202     m_state.shadowsUseLegacyRadius = true;
203 #endif
204     setPlatformShadow(offset, blur, color, colorSpace);
205 }
206
207 void GraphicsContext::clearShadow()
208 {
209     m_state.shadowOffset = FloatSize();
210     m_state.shadowBlur = 0;
211     m_state.shadowColor = Color();
212     m_state.shadowColorSpace = ColorSpaceDeviceRGB;
213     clearPlatformShadow();
214 }
215
216 bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const
217 {
218     offset = m_state.shadowOffset;
219     blur = m_state.shadowBlur;
220     color = m_state.shadowColor;
221     colorSpace = m_state.shadowColorSpace;
222
223     return hasShadow();
224 }
225
226 #if USE(CAIRO)
227 bool GraphicsContext::mustUseShadowBlur() const
228 {
229     // We can't avoid ShadowBlur if the shadow has blur.
230     if (hasBlurredShadow())
231         return true;
232     // We can avoid ShadowBlur and optimize, since we're not drawing on a
233     // canvas and box shadows are affected by the transformation matrix.
234     if (!m_state.shadowsIgnoreTransforms)
235         return false;
236     // We can avoid ShadowBlur, since there are no transformations to apply to the canvas.
237     if (getCTM().isIdentity())
238         return false;
239     // Otherwise, no chance avoiding ShadowBlur.
240     return true;
241 }
242 #endif
243
244 void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
245 {
246     m_state.fillColor = color;
247     m_state.fillColorSpace = colorSpace;
248     m_state.fillGradient = nullptr;
249     m_state.fillPattern = nullptr;
250     setPlatformFillColor(color, colorSpace);
251 }
252
253 void GraphicsContext::setShouldAntialias(bool shouldAntialias)
254 {
255     m_state.shouldAntialias = shouldAntialias;
256     setPlatformShouldAntialias(shouldAntialias);
257 }
258
259 void GraphicsContext::setShouldSmoothFonts(bool shouldSmoothFonts)
260 {
261     m_state.shouldSmoothFonts = shouldSmoothFonts;
262     setPlatformShouldSmoothFonts(shouldSmoothFonts);
263 }
264
265 void GraphicsContext::setAntialiasedFontDilationEnabled(bool antialiasedFontDilationEnabled)
266 {
267     m_state.antialiasedFontDilationEnabled = antialiasedFontDilationEnabled;
268 }
269
270 void GraphicsContext::setStrokePattern(Ref<Pattern>&& pattern)
271 {
272     m_state.strokeGradient = nullptr;
273     m_state.strokePattern = WTF::move(pattern);
274 }
275
276 void GraphicsContext::setFillPattern(Ref<Pattern>&& pattern)
277 {
278     m_state.fillGradient = nullptr;
279     m_state.fillPattern = WTF::move(pattern);
280 }
281
282 void GraphicsContext::setStrokeGradient(Ref<Gradient>&& gradient)
283 {
284     m_state.strokeGradient = WTF::move(gradient);
285     m_state.strokePattern = nullptr;
286 }
287
288 void GraphicsContext::setFillGradient(Ref<Gradient>&& gradient)
289 {
290     m_state.fillGradient = WTF::move(gradient);
291     m_state.fillPattern = nullptr;
292 }
293
294 void GraphicsContext::beginTransparencyLayer(float opacity)
295 {
296     beginPlatformTransparencyLayer(opacity);
297     ++m_transparencyCount;
298 }
299
300 void GraphicsContext::endTransparencyLayer()
301 {
302     endPlatformTransparencyLayer();
303     ASSERT(m_transparencyCount > 0);
304     --m_transparencyCount;
305 }
306
307 void GraphicsContext::setUpdatingControlTints(bool b)
308 {
309     setPaintingDisabled(b);
310     m_updatingControlTints = b;
311 }
312
313 float GraphicsContext::drawText(const FontCascade& font, const TextRun& run, const FloatPoint& point, int from, int to)
314 {
315     if (paintingDisabled())
316         return 0;
317
318     return font.drawText(*this, run, point, from, to);
319 }
320
321 void GraphicsContext::drawGlyphs(const FontCascade& fontCascade, const Font& font, const GlyphBuffer& buffer, int from, int numGlyphs, const FloatPoint& point)
322 {
323     if (paintingDisabled())
324         return;
325
326     fontCascade.drawGlyphs(*this, font, buffer, from, numGlyphs, point);
327 }
328
329 void GraphicsContext::drawEmphasisMarks(const FontCascade& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to)
330 {
331     if (paintingDisabled())
332         return;
333
334     font.drawEmphasisMarks(*this, run, mark, point, from, to);
335 }
336
337 void GraphicsContext::drawBidiText(const FontCascade& font, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction customFontNotReadyAction)
338 {
339     if (paintingDisabled())
340         return;
341
342     BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
343     bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
344     bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
345
346     // FIXME: This ownership should be reversed. We should pass BidiRunList
347     // to BidiResolver in createBidiRunsForLine.
348     BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
349     bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
350
351     if (!bidiRuns.runCount())
352         return;
353
354     FloatPoint currPoint = point;
355     BidiCharacterRun* bidiRun = bidiRuns.firstRun();
356     while (bidiRun) {
357         TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
358         bool isRTL = bidiRun->level() % 2;
359         subrun.setDirection(isRTL ? RTL : LTR);
360         subrun.setDirectionalOverride(bidiRun->dirOverride(false));
361
362         float width = font.drawText(*this, subrun, currPoint, 0, -1, customFontNotReadyAction);
363         currPoint.move(width, 0);
364
365         bidiRun = bidiRun->next();
366     }
367
368     bidiRuns.deleteRuns();
369 }
370
371 void GraphicsContext::drawImage(Image& image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
372 {
373     drawImage(image, colorSpace, FloatRect(destination, image.size()), FloatRect(FloatPoint(), image.size()), imagePaintingOptions);
374 }
375
376 void GraphicsContext::drawImage(Image& image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
377 {
378 #if PLATFORM(IOS)
379     FloatRect srcRect(FloatPoint(), image.originalSize());
380 #else
381     FloatRect srcRect(FloatPoint(), image.size());
382 #endif
383         
384     drawImage(image, colorSpace, destination, srcRect, imagePaintingOptions);
385 }
386
387 void GraphicsContext::drawImage(Image& image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
388 {
389     if (paintingDisabled())
390         return;
391
392     // FIXME (49002): Should be InterpolationLow
393     InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality());
394     image.draw(*this, destination, source, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_orientationDescription);
395 }
396
397 void GraphicsContext::drawTiledImage(Image& image, ColorSpace colorSpace, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize, const FloatSize& spacing, const ImagePaintingOptions& imagePaintingOptions)
398 {
399     if (paintingDisabled())
400         return;
401
402     InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality());
403     image.drawTiled(*this, destination, source, tileSize, spacing, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
404 }
405
406 void GraphicsContext::drawTiledImage(Image& image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor,
407     Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
408 {
409     if (paintingDisabled())
410         return;
411
412     if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
413         // Just do a scale.
414         drawImage(image, colorSpace, destination, source, imagePaintingOptions);
415         return;
416     }
417
418     InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality());
419     image.drawTiled(*this, destination, source, tileScaleFactor, hRule, vRule, colorSpace, imagePaintingOptions.m_compositeOperator);
420 }
421
422 void GraphicsContext::drawImageBuffer(ImageBuffer& image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
423 {
424     drawImageBuffer(image, colorSpace, FloatRect(destination, image.logicalSize()), FloatRect(FloatPoint(), image.logicalSize()), imagePaintingOptions);
425 }
426
427 void GraphicsContext::drawImageBuffer(ImageBuffer& image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
428 {
429     drawImageBuffer(image, colorSpace, destination, FloatRect(FloatPoint(), FloatSize(image.logicalSize())), imagePaintingOptions);
430 }
431
432 void GraphicsContext::drawImageBuffer(ImageBuffer& image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
433 {
434     if (paintingDisabled())
435         return;
436
437     // FIXME (49002): Should be InterpolationLow
438     InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality());
439     image.draw(*this, colorSpace, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_useLowQualityScale);
440 }
441
442 void GraphicsContext::clip(const IntRect& rect)
443 {
444     clip(FloatRect(rect));
445 }
446
447 void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect)
448 {
449     if (paintingDisabled())
450         return;
451
452     Path path;
453     path.addRoundedRect(rect);
454     clip(path);
455 }
456
457 void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect)
458 {
459     if (paintingDisabled())
460         return;
461
462     if (!rect.isRounded()) {
463         clipOut(rect.rect());
464         return;
465     }
466
467     Path path;
468     path.addRoundedRect(rect);
469     clipOut(path);
470 }
471
472 void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& rect)
473 {
474     if (paintingDisabled())
475         return;
476     buffer.clip(*this, rect);
477 }
478
479 #if !USE(CG) && !USE(CAIRO)
480 IntRect GraphicsContext::clipBounds() const
481 {
482     ASSERT_NOT_REACHED();
483     return IntRect();
484 }
485 #endif
486
487 void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
488 {
489     m_state.textDrawingMode = mode;
490     if (paintingDisabled())
491         return;
492     setPlatformTextDrawingMode(mode);
493 }
494
495 void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
496 {
497     if (paintingDisabled())
498         return;
499     gradient.fill(this, rect);
500 }
501
502 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode)
503 {
504     if (paintingDisabled())
505         return;
506
507     CompositeOperator previousOperator = compositeOperation();
508     setCompositeOperation(op, blendMode);
509     fillRect(rect, color, styleColorSpace);
510     setCompositeOperation(previousOperator);
511 }
512
513 void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace colorSpace, BlendMode blendMode)
514 {
515     if (rect.isRounded()) {
516         setCompositeOperation(compositeOperation(), blendMode);
517         platformFillRoundedRect(rect, color, colorSpace);
518         setCompositeOperation(compositeOperation());
519     } else
520         fillRect(rect.rect(), color, colorSpace, compositeOperation(), blendMode);
521 }
522
523 #if !USE(CG) && !USE(CAIRO)
524 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
525 {
526     if (paintingDisabled())
527         return;
528
529     Path path;
530     path.addRect(rect);
531
532     if (!roundedHoleRect.radii().isZero())
533         path.addRoundedRect(roundedHoleRect);
534     else
535         path.addRect(roundedHoleRect.rect());
536
537     WindRule oldFillRule = fillRule();
538     Color oldFillColor = fillColor();
539     ColorSpace oldFillColorSpace = fillColorSpace();
540     
541     setFillRule(RULE_EVENODD);
542     setFillColor(color, colorSpace);
543
544     fillPath(path);
545     
546     setFillRule(oldFillRule);
547     setFillColor(oldFillColor, oldFillColorSpace);
548 }
549 #endif
550
551 void GraphicsContext::setAlpha(float alpha)
552 {
553     m_state.alpha = alpha;
554     setPlatformAlpha(alpha);
555 }
556
557 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
558 {
559     m_state.compositeOperator = compositeOperation;
560     m_state.blendMode = blendMode;
561     setPlatformCompositeOperation(compositeOperation, blendMode);
562 }
563
564 #if !USE(CG)
565 // Implement this if you want to go push the drawing mode into your native context immediately.
566 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
567 {
568 }
569 #endif
570
571 #if !USE(CAIRO)
572 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
573 {
574 }
575 #endif
576
577 #if !USE(CG)
578 void GraphicsContext::setPlatformShouldSmoothFonts(bool)
579 {
580 }
581 #endif
582
583 #if !USE(CG) && !USE(CAIRO)
584 bool GraphicsContext::isAcceleratedContext() const
585 {
586     return false;
587 }
588 #endif
589
590 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
591 {
592     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
593     // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
594     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
595     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
596     if (penStyle == DottedStroke || penStyle == DashedStroke) {
597         if (p1.x() == p2.x()) {
598             p1.setY(p1.y() + strokeWidth);
599             p2.setY(p2.y() - strokeWidth);
600         } else {
601             p1.setX(p1.x() + strokeWidth);
602             p2.setX(p2.x() - strokeWidth);
603         }
604     }
605
606     if (static_cast<int>(strokeWidth) % 2) { //odd
607         if (p1.x() == p2.x()) {
608             // We're a vertical line.  Adjust our x.
609             p1.setX(p1.x() + 0.5f);
610             p2.setX(p2.x() + 0.5f);
611         } else {
612             // We're a horizontal line. Adjust our y.
613             p1.setY(p1.y() + 0.5f);
614             p2.setY(p2.y() + 0.5f);
615         }
616     }
617 }
618
619 static bool scalesMatch(AffineTransform a, AffineTransform b)
620 {
621     return a.xScale() == b.xScale() && a.yScale() == b.yScale();
622 }
623
624 std::unique_ptr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const FloatSize& size, bool hasAlpha) const
625 {
626     // Make the buffer larger if the context's transform is scaling it so we need a higher
627     // resolution than one pixel per unit. Also set up a corresponding scale factor on the
628     // graphics context.
629
630     AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale);
631     FloatSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
632
633     std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceDeviceRGB, *this, hasAlpha);
634     if (!buffer)
635         return nullptr;
636
637     buffer->context().scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height()));
638
639     return buffer;
640 }
641
642 bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer& buffer) const
643 {
644     GraphicsContext& bufferContext = buffer.context();
645
646     return scalesMatch(getCTM(), bufferContext.getCTM()) && isAcceleratedContext() == bufferContext.isAcceleratedContext();
647 }
648
649 #if !USE(CG)
650 void GraphicsContext::platformApplyDeviceScaleFactor(float)
651 {
652 }
653 #endif
654
655 void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
656 {
657     scale(FloatSize(deviceScaleFactor, deviceScaleFactor));
658     platformApplyDeviceScaleFactor(deviceScaleFactor);
659 }
660
661 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
662 {
663     platformFillEllipse(ellipse);
664 }
665
666 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
667 {
668     platformStrokeEllipse(ellipse);
669 }
670
671 void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse)
672 {
673     Path path;
674     path.addEllipse(ellipse);
675     fillPath(path);
676 }
677
678 void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
679 {
680     Path path;
681     path.addEllipse(ellipse);
682     strokePath(path);
683 }
684
685 #if !USE(CG)
686 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
687 {
688     if (paintingDisabled())
689         return;
690
691     fillEllipseAsPath(ellipse);
692 }
693
694 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
695 {
696     if (paintingDisabled())
697         return;
698
699     strokeEllipseAsPath(ellipse);
700 }
701 #endif
702
703 FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatPoint& point, float width, bool printing, bool& shouldAntialias, Color& color)
704 {
705     FloatPoint origin;
706     float thickness = std::max(strokeThickness(), 0.5f);
707
708     shouldAntialias = true;
709     if (printing)
710         origin = point;
711     else {
712         AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
713         if (transform.preservesAxisAlignment())
714             shouldAntialias = false;
715
716         // This code always draws a line that is at least one-pixel line high,
717         // which tends to visually overwhelm text at small scales. To counter this
718         // effect, an alpha is applied to the underline color when text is at small scales.
719
720         // Just compute scale in x dimension, assuming x and y scales are equal.
721         float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a();
722         if (scale < 1.0) {
723             static const float minimumUnderlineAlpha = 0.4f;
724             float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha;
725             int alpha = color.alpha() * shade;
726             color = Color(color.red(), color.green(), color.blue(), alpha);
727         }
728
729         FloatPoint devicePoint = transform.mapPoint(point);
730         FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y()));
731         origin = transform.inverse().mapPoint(deviceOrigin);
732     }
733     return FloatRect(origin.x(), origin.y(), width, thickness);
734 }
735
736 }