Replace WTF::move with WTFMove
[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 = WTFMove(pattern);
278 }
279
280 void GraphicsContext::setFillPattern(Ref<Pattern>&& pattern)
281 {
282     m_state.fillGradient = nullptr;
283     m_state.fillPattern = WTFMove(pattern);
284 }
285
286 void GraphicsContext::setStrokeGradient(Ref<Gradient>&& gradient)
287 {
288     m_state.strokeGradient = WTFMove(gradient);
289     m_state.strokePattern = nullptr;
290 }
291
292 void GraphicsContext::setFillGradient(Ref<Gradient>&& gradient)
293 {
294     m_state.fillGradient = WTFMove(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::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
447 {
448     if (!image)
449         return;
450     IntSize imageLogicalSize = image->logicalSize();
451     drawConsumingImageBuffer(WTFMove(image), FloatRect(destination, imageLogicalSize), FloatRect(FloatPoint(), imageLogicalSize), imagePaintingOptions);
452 }
453
454 void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
455 {
456     if (!image)
457         return;
458     IntSize imageLogicalSize = image->logicalSize();
459     drawConsumingImageBuffer(WTFMove(image), destination, FloatRect(FloatPoint(), FloatSize(imageLogicalSize)), imagePaintingOptions);
460 }
461
462 void GraphicsContext::drawConsumingImageBuffer(std::unique_ptr<ImageBuffer> image, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
463 {
464     if (paintingDisabled() || !image)
465         return;
466     
467     // FIXME (49002): Should be InterpolationLow
468     InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality());
469
470     ImageBuffer::drawConsuming(WTFMove(image), *this, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_useLowQualityScale);
471 }
472
473 void GraphicsContext::clip(const IntRect& rect)
474 {
475     clip(FloatRect(rect));
476 }
477
478 void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect)
479 {
480     if (paintingDisabled())
481         return;
482
483     Path path;
484     path.addRoundedRect(rect);
485     clipPath(path);
486 }
487
488 void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect)
489 {
490     if (paintingDisabled())
491         return;
492
493     if (!rect.isRounded()) {
494         clipOut(rect.rect());
495         return;
496     }
497
498     Path path;
499     path.addRoundedRect(rect);
500     clipOut(path);
501 }
502
503 void GraphicsContext::clipToImageBuffer(ImageBuffer& buffer, const FloatRect& rect)
504 {
505     if (paintingDisabled())
506         return;
507     buffer.clip(*this, rect);
508 }
509
510 #if !USE(CG) && !USE(CAIRO)
511 IntRect GraphicsContext::clipBounds() const
512 {
513     ASSERT_NOT_REACHED();
514     return IntRect();
515 }
516 #endif
517
518 void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
519 {
520     m_state.textDrawingMode = mode;
521     if (paintingDisabled())
522         return;
523     setPlatformTextDrawingMode(mode);
524 }
525
526 void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
527 {
528     if (paintingDisabled())
529         return;
530     gradient.fill(this, rect);
531 }
532
533 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op, BlendMode blendMode)
534 {
535     if (paintingDisabled())
536         return;
537
538     CompositeOperator previousOperator = compositeOperation();
539     setCompositeOperation(op, blendMode);
540     fillRect(rect, color);
541     setCompositeOperation(previousOperator);
542 }
543
544 void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, BlendMode blendMode)
545 {
546     if (rect.isRounded()) {
547         setCompositeOperation(compositeOperation(), blendMode);
548         platformFillRoundedRect(rect, color);
549         setCompositeOperation(compositeOperation());
550     } else
551         fillRect(rect.rect(), color, compositeOperation(), blendMode);
552 }
553
554 #if !USE(CG) && !USE(CAIRO)
555 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color)
556 {
557     if (paintingDisabled())
558         return;
559
560     Path path;
561     path.addRect(rect);
562
563     if (!roundedHoleRect.radii().isZero())
564         path.addRoundedRect(roundedHoleRect);
565     else
566         path.addRect(roundedHoleRect.rect());
567
568     WindRule oldFillRule = fillRule();
569     Color oldFillColor = fillColor();
570     
571     setFillRule(RULE_EVENODD);
572     setFillColor(color);
573
574     fillPath(path);
575     
576     setFillRule(oldFillRule);
577     setFillColor(oldFillColor);
578 }
579 #endif
580
581 void GraphicsContext::setAlpha(float alpha)
582 {
583     m_state.alpha = alpha;
584     setPlatformAlpha(alpha);
585 }
586
587 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
588 {
589     m_state.compositeOperator = compositeOperation;
590     m_state.blendMode = blendMode;
591     setPlatformCompositeOperation(compositeOperation, blendMode);
592 }
593
594 #if !USE(CG)
595 // Implement this if you want to go push the drawing mode into your native context immediately.
596 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
597 {
598 }
599 #endif
600
601 #if !USE(CAIRO)
602 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
603 {
604 }
605 #endif
606
607 #if !USE(CG)
608 void GraphicsContext::setPlatformShouldSmoothFonts(bool)
609 {
610 }
611 #endif
612
613 #if !USE(CG) && !USE(CAIRO)
614 bool GraphicsContext::isAcceleratedContext() const
615 {
616     return false;
617 }
618 #endif
619
620 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
621 {
622     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
623     // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
624     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
625     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
626     if (penStyle == DottedStroke || penStyle == DashedStroke) {
627         if (p1.x() == p2.x()) {
628             p1.setY(p1.y() + strokeWidth);
629             p2.setY(p2.y() - strokeWidth);
630         } else {
631             p1.setX(p1.x() + strokeWidth);
632             p2.setX(p2.x() - strokeWidth);
633         }
634     }
635
636     if (static_cast<int>(strokeWidth) % 2) { //odd
637         if (p1.x() == p2.x()) {
638             // We're a vertical line.  Adjust our x.
639             p1.setX(p1.x() + 0.5f);
640             p2.setX(p2.x() + 0.5f);
641         } else {
642             // We're a horizontal line. Adjust our y.
643             p1.setY(p1.y() + 0.5f);
644             p2.setY(p2.y() + 0.5f);
645         }
646     }
647 }
648
649 static bool scalesMatch(AffineTransform a, AffineTransform b)
650 {
651     return a.xScale() == b.xScale() && a.yScale() == b.yScale();
652 }
653
654 std::unique_ptr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const FloatSize& size, bool hasAlpha) const
655 {
656     // Make the buffer larger if the context's transform is scaling it so we need a higher
657     // resolution than one pixel per unit. Also set up a corresponding scale factor on the
658     // graphics context.
659
660     AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale);
661     FloatSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
662
663     std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceSRGB, *this, hasAlpha);
664     if (!buffer)
665         return nullptr;
666
667     buffer->context().scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height()));
668
669     return buffer;
670 }
671
672 bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer& buffer) const
673 {
674     GraphicsContext& bufferContext = buffer.context();
675
676     return scalesMatch(getCTM(), bufferContext.getCTM()) && isAcceleratedContext() == bufferContext.isAcceleratedContext();
677 }
678
679 #if !USE(CG)
680 void GraphicsContext::platformApplyDeviceScaleFactor(float)
681 {
682 }
683 #endif
684
685 void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
686 {
687     scale(FloatSize(deviceScaleFactor, deviceScaleFactor));
688     platformApplyDeviceScaleFactor(deviceScaleFactor);
689 }
690
691 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
692 {
693     platformFillEllipse(ellipse);
694 }
695
696 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
697 {
698     platformStrokeEllipse(ellipse);
699 }
700
701 void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse)
702 {
703     Path path;
704     path.addEllipse(ellipse);
705     fillPath(path);
706 }
707
708 void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
709 {
710     Path path;
711     path.addEllipse(ellipse);
712     strokePath(path);
713 }
714
715 #if !USE(CG)
716 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
717 {
718     if (paintingDisabled())
719         return;
720
721     fillEllipseAsPath(ellipse);
722 }
723
724 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
725 {
726     if (paintingDisabled())
727         return;
728
729     strokeEllipseAsPath(ellipse);
730 }
731 #endif
732
733 FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatPoint& point, float width, bool printing, bool& shouldAntialias, Color& color)
734 {
735     FloatPoint origin = point;
736     float thickness = std::max(strokeThickness(), 0.5f);
737
738     shouldAntialias = true;
739     if (!printing) {
740         AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
741         if (transform.preservesAxisAlignment())
742             shouldAntialias = false;
743
744         // This code always draws a line that is at least one-pixel line high,
745         // which tends to visually overwhelm text at small scales. To counter this
746         // effect, an alpha is applied to the underline color when text is at small scales.
747
748         // Just compute scale in x dimension, assuming x and y scales are equal.
749         float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a();
750         if (scale < 1.0) {
751             static const float minimumUnderlineAlpha = 0.4f;
752             float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha;
753             int alpha = color.alpha() * shade;
754             color = Color(color.red(), color.green(), color.blue(), alpha);
755         }
756
757         FloatPoint devicePoint = transform.mapPoint(point);
758         FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y()));
759         if (auto inverse = transform.inverse())
760             origin = inverse.value().mapPoint(deviceOrigin);
761     }
762     return FloatRect(origin.x(), origin.y(), width, thickness);
763 }
764
765 }