2 * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "GraphicsContext.h"
29 #include "BidiResolver.h"
30 #include "BitmapImage.h"
31 #include "FloatRoundedRect.h"
33 #include "ImageBuffer.h"
35 #include "RoundedRect.h"
42 class TextRunIterator {
50 TextRunIterator(const TextRun* textRun, unsigned offset)
56 TextRunIterator(const TextRunIterator& other)
57 : m_textRun(other.m_textRun)
58 , m_offset(other.m_offset)
62 unsigned offset() const { return m_offset; }
63 void increment() { m_offset++; }
64 bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
65 UChar current() const { return (*m_textRun)[m_offset]; }
66 UCharDirection direction() const { return atEnd() ? U_OTHER_NEUTRAL : u_charDirection(current()); }
68 bool operator==(const TextRunIterator& other)
70 return m_offset == other.m_offset && m_textRun == other.m_textRun;
73 bool operator!=(const TextRunIterator& other) { return !operator==(other); }
76 const TextRun* m_textRun;
80 class InterpolationQualityMaintainer {
82 explicit InterpolationQualityMaintainer(GraphicsContext& graphicsContext, InterpolationQuality interpolationQualityToUse)
83 : m_graphicsContext(graphicsContext)
84 , m_currentInterpolationQuality(graphicsContext.imageInterpolationQuality())
85 , m_interpolationQualityChanged(m_currentInterpolationQuality != interpolationQualityToUse)
87 if (m_interpolationQualityChanged)
88 m_graphicsContext.setImageInterpolationQuality(interpolationQualityToUse);
91 ~InterpolationQualityMaintainer()
93 if (m_interpolationQualityChanged)
94 m_graphicsContext.setImageInterpolationQuality(m_currentInterpolationQuality);
98 GraphicsContext& m_graphicsContext;
99 InterpolationQuality m_currentInterpolationQuality;
100 bool m_interpolationQualityChanged;
104 GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext)
105 : m_updatingControlTints(false)
106 , m_transparencyCount(0)
108 platformInit(platformGraphicsContext);
111 GraphicsContext::GraphicsContext(PlatformGraphicsContext* platformGraphicsContext, bool shouldUseContextColors)
112 : m_updatingControlTints(false)
113 , m_transparencyCount(0)
115 platformInit(platformGraphicsContext, shouldUseContextColors);
119 GraphicsContext::~GraphicsContext()
121 ASSERT(m_stack.isEmpty());
122 ASSERT(!m_transparencyCount);
126 void GraphicsContext::save()
128 if (paintingDisabled())
131 m_stack.append(m_state);
136 void GraphicsContext::restore()
138 if (paintingDisabled())
141 if (m_stack.isEmpty()) {
142 LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
145 m_state = m_stack.last();
146 m_stack.removeLast();
148 restorePlatformState();
151 void GraphicsContext::drawRaisedEllipse(const FloatRect& rect, const Color& ellipseColor, ColorSpace ellipseColorSpace, const Color& shadowColor, ColorSpace shadowColorSpace)
153 if (paintingDisabled())
158 setStrokeColor(shadowColor, shadowColorSpace);
159 setFillColor(shadowColor, shadowColorSpace);
161 drawEllipse(FloatRect(rect.x(), rect.y() + 1, rect.width(), rect.height()));
163 setStrokeColor(ellipseColor, ellipseColorSpace);
164 setFillColor(ellipseColor, ellipseColorSpace);
171 void GraphicsContext::setStrokeThickness(float thickness)
173 m_state.strokeThickness = thickness;
174 setPlatformStrokeThickness(thickness);
177 void GraphicsContext::setStrokeStyle(StrokeStyle style)
179 m_state.strokeStyle = style;
180 setPlatformStrokeStyle(style);
183 void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
185 m_state.strokeColor = color;
186 m_state.strokeColorSpace = colorSpace;
187 m_state.strokeGradient.clear();
188 m_state.strokePattern.clear();
189 setPlatformStrokeColor(color, colorSpace);
192 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
194 m_state.shadowOffset = offset;
195 m_state.shadowBlur = blur;
196 m_state.shadowColor = color;
197 m_state.shadowColorSpace = colorSpace;
198 setPlatformShadow(offset, blur, color, colorSpace);
201 void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace)
203 m_state.shadowOffset = offset;
204 m_state.shadowBlur = blur;
205 m_state.shadowColor = color;
206 m_state.shadowColorSpace = colorSpace;
208 m_state.shadowsUseLegacyRadius = true;
210 setPlatformShadow(offset, blur, color, colorSpace);
213 void GraphicsContext::clearShadow()
215 m_state.shadowOffset = FloatSize();
216 m_state.shadowBlur = 0;
217 m_state.shadowColor = Color();
218 m_state.shadowColorSpace = ColorSpaceDeviceRGB;
219 clearPlatformShadow();
222 bool GraphicsContext::hasShadow() const
224 return m_state.shadowColor.isValid() && m_state.shadowColor.alpha()
225 && (m_state.shadowBlur || m_state.shadowOffset.width() || m_state.shadowOffset.height());
228 bool GraphicsContext::getShadow(FloatSize& offset, float& blur, Color& color, ColorSpace& colorSpace) const
230 offset = m_state.shadowOffset;
231 blur = m_state.shadowBlur;
232 color = m_state.shadowColor;
233 colorSpace = m_state.shadowColorSpace;
238 bool GraphicsContext::hasBlurredShadow() const
240 return m_state.shadowColor.isValid() && m_state.shadowColor.alpha() && m_state.shadowBlur;
244 bool GraphicsContext::mustUseShadowBlur() const
246 // We can't avoid ShadowBlur if the shadow has blur.
247 if (hasBlurredShadow())
249 // We can avoid ShadowBlur and optimize, since we're not drawing on a
250 // canvas and box shadows are affected by the transformation matrix.
251 if (!m_state.shadowsIgnoreTransforms)
253 // We can avoid ShadowBlur, since there are no transformations to apply to the canvas.
254 if (getCTM().isIdentity())
256 // Otherwise, no chance avoiding ShadowBlur.
261 float GraphicsContext::strokeThickness() const
263 return m_state.strokeThickness;
266 StrokeStyle GraphicsContext::strokeStyle() const
268 return m_state.strokeStyle;
271 Color GraphicsContext::strokeColor() const
273 return m_state.strokeColor;
276 ColorSpace GraphicsContext::strokeColorSpace() const
278 return m_state.strokeColorSpace;
281 WindRule GraphicsContext::fillRule() const
283 return m_state.fillRule;
286 void GraphicsContext::setFillRule(WindRule fillRule)
288 m_state.fillRule = fillRule;
291 void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
293 m_state.fillColor = color;
294 m_state.fillColorSpace = colorSpace;
295 m_state.fillGradient.clear();
296 m_state.fillPattern.clear();
297 setPlatformFillColor(color, colorSpace);
300 Color GraphicsContext::fillColor() const
302 return m_state.fillColor;
305 ColorSpace GraphicsContext::fillColorSpace() const
307 return m_state.fillColorSpace;
310 void GraphicsContext::setShouldAntialias(bool b)
312 m_state.shouldAntialias = b;
313 setPlatformShouldAntialias(b);
316 bool GraphicsContext::shouldAntialias() const
318 return m_state.shouldAntialias;
321 void GraphicsContext::setShouldSmoothFonts(bool b)
323 m_state.shouldSmoothFonts = b;
324 setPlatformShouldSmoothFonts(b);
327 bool GraphicsContext::shouldSmoothFonts() const
329 return m_state.shouldSmoothFonts;
332 void GraphicsContext::setShouldSubpixelQuantizeFonts(bool b)
334 m_state.shouldSubpixelQuantizeFonts = b;
337 bool GraphicsContext::shouldSubpixelQuantizeFonts() const
339 return m_state.shouldSubpixelQuantizeFonts;
342 const GraphicsContextState& GraphicsContext::state() const
347 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
351 setStrokeColor(Color::black, ColorSpaceDeviceRGB);
354 m_state.strokeGradient.clear();
355 m_state.strokePattern = pattern;
358 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
362 setFillColor(Color::black, ColorSpaceDeviceRGB);
365 m_state.fillGradient.clear();
366 m_state.fillPattern = pattern;
369 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
373 setStrokeColor(Color::black, ColorSpaceDeviceRGB);
376 m_state.strokeGradient = gradient;
377 m_state.strokePattern.clear();
380 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
384 setFillColor(Color::black, ColorSpaceDeviceRGB);
387 m_state.fillGradient = gradient;
388 m_state.fillPattern.clear();
391 Gradient* GraphicsContext::fillGradient() const
393 return m_state.fillGradient.get();
396 Gradient* GraphicsContext::strokeGradient() const
398 return m_state.strokeGradient.get();
401 Pattern* GraphicsContext::fillPattern() const
403 return m_state.fillPattern.get();
406 Pattern* GraphicsContext::strokePattern() const
408 return m_state.strokePattern.get();
411 void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
413 m_state.shadowsIgnoreTransforms = ignoreTransforms;
416 bool GraphicsContext::shadowsIgnoreTransforms() const
418 return m_state.shadowsIgnoreTransforms;
421 void GraphicsContext::beginTransparencyLayer(float opacity)
423 beginPlatformTransparencyLayer(opacity);
424 ++m_transparencyCount;
427 void GraphicsContext::endTransparencyLayer()
429 endPlatformTransparencyLayer();
430 ASSERT(m_transparencyCount > 0);
431 --m_transparencyCount;
434 bool GraphicsContext::isInTransparencyLayer() const
436 return (m_transparencyCount > 0) && supportsTransparencyLayers();
439 bool GraphicsContext::updatingControlTints() const
441 return m_updatingControlTints;
444 void GraphicsContext::setUpdatingControlTints(bool b)
446 setPaintingDisabled(b);
447 m_updatingControlTints = b;
450 void GraphicsContext::setPaintingDisabled(bool f)
452 m_state.paintingDisabled = f;
455 bool GraphicsContext::paintingDisabled() const
457 return m_state.paintingDisabled;
460 // FIXME: Replace the non-iOS implementation with the iOS implementation since the latter computes returns
461 // the width of the drawn text. Ensure that there aren't noticeable differences in layout.
464 void GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
466 if (paintingDisabled())
469 font.drawText(this, run, point, from, to);
473 float GraphicsContext::drawText(const Font& font, const TextRun& run, const FloatPoint& point, int from, int to)
475 if (paintingDisabled())
478 return font.drawText(this, run, point, from, to);
480 #endif // !PLATFORM(IOS)
482 void GraphicsContext::drawGlyphs(const Font& font, const SimpleFontData& fontData, const GlyphBuffer& buffer, int from, int numGlyphs, const FloatPoint& point)
484 if (paintingDisabled())
487 font.drawGlyphs(this, &fontData, buffer, from, numGlyphs, point);
490 void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to)
492 if (paintingDisabled())
495 font.drawEmphasisMarks(this, run, mark, point, from, to);
498 // FIXME: Better merge the iOS and non-iOS differences. In particular, make this method use the
499 // returned width of the drawn text, Font::drawText(), instead of computing it. Ensure that there
500 // aren't noticeable differences in layout with such a change.
502 void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
504 float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction, BidiStatus* status, int length)
507 if (paintingDisabled())
514 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
516 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
518 bidiResolver.setStatus(status ? *status : BidiStatus(run.direction(), run.directionalOverride()));
520 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
522 // FIXME: This ownership should be reversed. We should pass BidiRunList
523 // to BidiResolver in createBidiRunsForLine.
524 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
526 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
528 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, length < 0 ? run.length() : length));
530 if (!bidiRuns.runCount())
537 FloatPoint currPoint = point;
538 BidiCharacterRun* bidiRun = bidiRuns.firstRun();
540 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
541 bool isRTL = bidiRun->level() % 2;
542 subrun.setDirection(isRTL ? RTL : LTR);
543 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
546 font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
548 bidiRun = bidiRun->next();
549 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
551 currPoint.move(font.width(subrun), 0);
553 float width = font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction);
554 currPoint.move(width, 0);
556 bidiRun = bidiRun->next();
562 *status = bidiResolver.status();
564 bidiRuns.deleteRuns();
567 return currPoint.x() - static_cast<float>(point.x());
571 void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
575 drawImage(image, colorSpace, FloatRect(destination, image->size()), FloatRect(FloatPoint(), image->size()), imagePaintingOptions);
578 void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
584 FloatRect srcRect(FloatPoint(), image->originalSize());
586 FloatRect srcRect(FloatPoint(), image->size());
589 drawImage(image, colorSpace, destination, srcRect, imagePaintingOptions);
592 void GraphicsContext::drawImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
594 if (paintingDisabled() || !image)
597 // FIXME (49002): Should be InterpolationLow
598 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality());
599 image->draw(this, destination, source, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_orientationDescription);
602 void GraphicsContext::drawTiledImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatPoint& source, const FloatSize& tileSize,
603 const ImagePaintingOptions& imagePaintingOptions)
605 if (paintingDisabled() || !image)
608 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality());
609 image->drawTiled(this, destination, source, tileSize, colorSpace, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode);
612 void GraphicsContext::drawTiledImage(Image* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const FloatSize& tileScaleFactor,
613 Image::TileRule hRule, Image::TileRule vRule, const ImagePaintingOptions& imagePaintingOptions)
615 if (paintingDisabled() || !image)
618 if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
620 drawImage(image, colorSpace, destination, source, imagePaintingOptions);
624 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationLow : imageInterpolationQuality());
625 image->drawTiled(this, destination, source, tileScaleFactor, hRule, vRule, colorSpace, imagePaintingOptions.m_compositeOperator);
628 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatPoint& destination, const ImagePaintingOptions& imagePaintingOptions)
632 drawImageBuffer(image, colorSpace, FloatRect(destination, image->logicalSize()), FloatRect(FloatPoint(), image->logicalSize()), imagePaintingOptions);
635 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatRect& destination, const ImagePaintingOptions& imagePaintingOptions)
639 drawImageBuffer(image, colorSpace, destination, FloatRect(FloatPoint(), FloatSize(image->logicalSize())), imagePaintingOptions);
642 void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace colorSpace, const FloatRect& destination, const FloatRect& source, const ImagePaintingOptions& imagePaintingOptions)
644 if (paintingDisabled() || !image)
647 // FIXME (49002): Should be InterpolationLow
648 InterpolationQualityMaintainer interpolationQualityForThisScope(*this, imagePaintingOptions.m_useLowQualityScale ? InterpolationNone : imageInterpolationQuality());
649 image->draw(this, colorSpace, destination, source, imagePaintingOptions.m_compositeOperator, imagePaintingOptions.m_blendMode, imagePaintingOptions.m_useLowQualityScale);
652 void GraphicsContext::clip(const IntRect& rect)
654 clip(FloatRect(rect));
657 void GraphicsContext::clipRoundedRect(const FloatRoundedRect& rect)
659 if (paintingDisabled())
663 path.addRoundedRect(rect);
667 void GraphicsContext::clipOutRoundedRect(const FloatRoundedRect& rect)
669 if (paintingDisabled())
672 if (!rect.isRounded()) {
673 clipOut(rect.rect());
678 path.addRoundedRect(rect);
682 void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect)
684 if (paintingDisabled())
686 buffer->clip(this, rect);
689 #if !USE(CG) && !USE(CAIRO)
690 IntRect GraphicsContext::clipBounds() const
692 ASSERT_NOT_REACHED();
697 TextDrawingModeFlags GraphicsContext::textDrawingMode() const
699 return m_state.textDrawingMode;
702 void GraphicsContext::setTextDrawingMode(TextDrawingModeFlags mode)
704 m_state.textDrawingMode = mode;
705 if (paintingDisabled())
707 setPlatformTextDrawingMode(mode);
710 void GraphicsContext::fillRect(const FloatRect& rect, Gradient& gradient)
712 if (paintingDisabled())
714 gradient.fill(this, rect);
717 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode)
719 if (paintingDisabled())
722 CompositeOperator previousOperator = compositeOperation();
723 setCompositeOperation(op, blendMode);
724 fillRect(rect, color, styleColorSpace);
725 setCompositeOperation(previousOperator);
728 void GraphicsContext::fillRoundedRect(const FloatRoundedRect& rect, const Color& color, ColorSpace colorSpace, BlendMode blendMode)
730 if (rect.isRounded()) {
731 setCompositeOperation(compositeOperation(), blendMode);
732 platformFillRoundedRect(rect, color, colorSpace);
733 setCompositeOperation(compositeOperation());
735 fillRect(rect.rect(), color, colorSpace, compositeOperation(), blendMode);
738 #if !USE(CG) && !USE(CAIRO)
739 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const FloatRoundedRect& roundedHoleRect, const Color& color, ColorSpace colorSpace)
741 if (paintingDisabled())
747 if (!roundedHoleRect.radii().isZero())
748 path.addRoundedRect(roundedHoleRect);
750 path.addRect(roundedHoleRect.rect());
752 WindRule oldFillRule = fillRule();
753 Color oldFillColor = fillColor();
754 ColorSpace oldFillColorSpace = fillColorSpace();
756 setFillRule(RULE_EVENODD);
757 setFillColor(color, colorSpace);
761 setFillRule(oldFillRule);
762 setFillColor(oldFillColor, oldFillColorSpace);
766 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, BlendMode blendMode)
768 m_state.compositeOperator = compositeOperation;
769 m_state.blendMode = blendMode;
770 setPlatformCompositeOperation(compositeOperation, blendMode);
773 CompositeOperator GraphicsContext::compositeOperation() const
775 return m_state.compositeOperator;
778 BlendMode GraphicsContext::blendModeOperation() const
780 return m_state.blendMode;
784 bool GraphicsContext::emojiDrawingEnabled()
786 return m_state.emojiDrawingEnabled;
789 void GraphicsContext::setEmojiDrawingEnabled(bool emojiDrawingEnabled)
791 m_state.emojiDrawingEnabled = emojiDrawingEnabled;
795 void GraphicsContext::setDrawLuminanceMask(bool drawLuminanceMask)
797 m_state.drawLuminanceMask = drawLuminanceMask;
800 bool GraphicsContext::drawLuminanceMask() const
802 return m_state.drawLuminanceMask;
806 // Implement this if you want to go ahead and push the drawing mode into your native context
808 void GraphicsContext::setPlatformTextDrawingMode(TextDrawingModeFlags)
814 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle)
820 void GraphicsContext::setPlatformShouldSmoothFonts(bool)
825 #if !USE(CG) && !USE(CAIRO)
826 bool GraphicsContext::isAcceleratedContext() const
832 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
834 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
835 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
836 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
837 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
838 if (penStyle == DottedStroke || penStyle == DashedStroke) {
839 if (p1.x() == p2.x()) {
840 p1.setY(p1.y() + strokeWidth);
841 p2.setY(p2.y() - strokeWidth);
843 p1.setX(p1.x() + strokeWidth);
844 p2.setX(p2.x() - strokeWidth);
848 if (static_cast<int>(strokeWidth) % 2) { //odd
849 if (p1.x() == p2.x()) {
850 // We're a vertical line. Adjust our x.
851 p1.setX(p1.x() + 0.5f);
852 p2.setX(p2.x() + 0.5f);
854 // We're a horizontal line. Adjust our y.
855 p1.setY(p1.y() + 0.5f);
856 p2.setY(p2.y() + 0.5f);
861 static bool scalesMatch(AffineTransform a, AffineTransform b)
863 return a.xScale() == b.xScale() && a.yScale() == b.yScale();
866 std::unique_ptr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const FloatSize& size, bool hasAlpha) const
868 // Make the buffer larger if the context's transform is scaling it so we need a higher
869 // resolution than one pixel per unit. Also set up a corresponding scale factor on the
872 AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale);
873 FloatSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
875 std::unique_ptr<ImageBuffer> buffer = ImageBuffer::createCompatibleBuffer(scaledSize, 1, ColorSpaceDeviceRGB, this, hasAlpha);
879 buffer->context()->scale(FloatSize(scaledSize.width() / size.width(), scaledSize.height() / size.height()));
884 bool GraphicsContext::isCompatibleWithBuffer(ImageBuffer* buffer) const
886 GraphicsContext* bufferContext = buffer->context();
888 return scalesMatch(getCTM(), bufferContext->getCTM()) && isAcceleratedContext() == bufferContext->isAcceleratedContext();
892 void GraphicsContext::platformApplyDeviceScaleFactor(float)
897 void GraphicsContext::applyDeviceScaleFactor(float deviceScaleFactor)
899 scale(FloatSize(deviceScaleFactor, deviceScaleFactor));
900 platformApplyDeviceScaleFactor(deviceScaleFactor);
903 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
905 platformFillEllipse(ellipse);
908 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
910 platformStrokeEllipse(ellipse);
913 void GraphicsContext::fillEllipseAsPath(const FloatRect& ellipse)
916 path.addEllipse(ellipse);
920 void GraphicsContext::strokeEllipseAsPath(const FloatRect& ellipse)
923 path.addEllipse(ellipse);
928 void GraphicsContext::platformFillEllipse(const FloatRect& ellipse)
930 if (paintingDisabled())
933 fillEllipseAsPath(ellipse);
936 void GraphicsContext::platformStrokeEllipse(const FloatRect& ellipse)
938 if (paintingDisabled())
941 strokeEllipseAsPath(ellipse);
945 FloatRect GraphicsContext::computeLineBoundsAndAntialiasingModeForText(const FloatPoint& point, float width, bool printing, bool& shouldAntialias, Color& color)
948 float thickness = std::max(strokeThickness(), 0.5f);
950 shouldAntialias = true;
954 AffineTransform transform = getCTM(GraphicsContext::DefinitelyIncludeDeviceScale);
955 if (transform.preservesAxisAlignment())
956 shouldAntialias = false;
958 // This code always draws a line that is at least one-pixel line high,
959 // which tends to visually overwhelm text at small scales. To counter this
960 // effect, an alpha is applied to the underline color when text is at small scales.
962 // Just compute scale in x dimension, assuming x and y scales are equal.
963 float scale = transform.b() ? sqrtf(transform.a() * transform.a() + transform.b() * transform.b()) : transform.a();
965 static const float minimumUnderlineAlpha = 0.4f;
966 float shade = scale > minimumUnderlineAlpha ? scale : minimumUnderlineAlpha;
967 int alpha = color.alpha() * shade;
968 color = Color(color.red(), color.green(), color.blue(), alpha);
971 FloatPoint devicePoint = transform.mapPoint(point);
972 FloatPoint deviceOrigin = FloatPoint(roundf(devicePoint.x()), ceilf(devicePoint.y()));
973 origin = transform.inverse().mapPoint(deviceOrigin);
975 return FloatRect(origin.x(), origin.y(), width, thickness);