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