2 * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Google Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24 * THE POSSIBILITY OF SUCH DAMAGE.
28 #include "platform/graphics/GraphicsContext.h"
30 #include "platform/TraceEvent.h"
31 #include "platform/geometry/IntRect.h"
32 #include "platform/geometry/RoundedRect.h"
33 #include "platform/graphics/BitmapImage.h"
34 #include "platform/graphics/DisplayList.h"
35 #include "platform/graphics/Gradient.h"
36 #include "platform/graphics/ImageBuffer.h"
37 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
38 #include "platform/graphics/skia/SkiaUtils.h"
39 #include "platform/text/BidiResolver.h"
40 #include "platform/text/TextRunIterator.h"
41 #include "platform/weborigin/KURL.h"
42 #include "third_party/skia/include/core/SkAnnotation.h"
43 #include "third_party/skia/include/core/SkClipStack.h"
44 #include "third_party/skia/include/core/SkColorFilter.h"
45 #include "third_party/skia/include/core/SkData.h"
46 #include "third_party/skia/include/core/SkDevice.h"
47 #include "third_party/skia/include/core/SkPicture.h"
48 #include "third_party/skia/include/core/SkPictureRecorder.h"
49 #include "third_party/skia/include/core/SkRRect.h"
50 #include "third_party/skia/include/core/SkRefCnt.h"
51 #include "third_party/skia/include/core/SkSurface.h"
52 #include "third_party/skia/include/effects/SkBlurMaskFilter.h"
53 #include "third_party/skia/include/effects/SkCornerPathEffect.h"
54 #include "third_party/skia/include/effects/SkDropShadowImageFilter.h"
55 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
56 #include "third_party/skia/include/effects/SkMatrixImageFilter.h"
57 #include "third_party/skia/include/effects/SkPictureImageFilter.h"
58 #include "third_party/skia/include/gpu/GrRenderTarget.h"
59 #include "third_party/skia/include/gpu/GrTexture.h"
60 #include "wtf/Assertions.h"
61 #include "wtf/MathExtras.h"
65 // Tolerance value use for comparing scale factor to 1..
66 // Numerical error should not reach 6th decimal except for highly degenerate cases,
67 // and effect of 6th decimal on scale is negligible over max span of a skia canvas
68 // which is 32k pixels.
69 const float cPictureScaleEpsilon = 0.000001;
75 struct GraphicsContext::RecordingState {
76 RecordingState(SkPictureRecorder* recorder, SkCanvas* currentCanvas, const SkMatrix& currentMatrix, bool currentShouldSmoothFonts,
77 PassRefPtr<DisplayList> displayList, RegionTrackingMode trackingMode)
78 : m_displayList(displayList)
79 , m_recorder(recorder)
80 , m_savedCanvas(currentCanvas)
81 , m_savedMatrix(currentMatrix)
82 , m_savedShouldSmoothFonts(currentShouldSmoothFonts)
83 , m_regionTrackingMode(trackingMode) { }
87 RefPtr<DisplayList> m_displayList;
88 SkPictureRecorder* m_recorder;
89 SkCanvas* m_savedCanvas;
90 const SkMatrix m_savedMatrix;
91 bool m_savedShouldSmoothFonts;
92 RegionTrackingMode m_regionTrackingMode;
95 GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting)
98 , m_paintStateIndex(0)
101 , m_annotationCount(0)
103 , m_disableDestructionChecks(false)
105 , m_disabledState(disableContextOrPainting)
106 , m_deviceScaleFactor(1.0f)
107 , m_regionTrackingMode(RegionTrackingDisabled)
108 , m_trackTextRegion(false)
109 , m_accelerated(false)
110 , m_isCertainlyOpaque(true)
112 , m_antialiasHairlineImages(false)
113 , m_shouldSmoothFonts(true)
115 // FIXME: Do some tests to determine how many states are typically used, and allocate
117 m_paintStateStack.append(GraphicsContextState::create());
118 m_paintState = m_paintStateStack.last().get();
121 GraphicsContext::~GraphicsContext()
124 if (!m_disableDestructionChecks) {
125 ASSERT(!m_paintStateIndex);
126 ASSERT(!m_paintState->saveCount());
127 ASSERT(!m_annotationCount);
128 ASSERT(!m_layerCount);
129 ASSERT(m_recordingStateStack.isEmpty());
130 ASSERT(!saveCount());
135 void GraphicsContext::resetCanvas(SkCanvas* canvas)
138 m_trackedRegion.reset();
141 void GraphicsContext::setRegionTrackingMode(RegionTrackingMode mode)
143 m_regionTrackingMode = mode;
144 if (mode == RegionTrackingOpaque)
145 m_trackedRegion.setTrackedRegionType(RegionTracker::Opaque);
146 else if (mode == RegionTrackingOverwrite)
147 m_trackedRegion.setTrackedRegionType(RegionTracker::Overwrite);
150 void GraphicsContext::save()
152 if (contextDisabled())
155 m_paintState->incrementSaveCount();
161 void GraphicsContext::restore()
163 if (contextDisabled())
166 if (!m_paintStateIndex && !m_paintState->saveCount()) {
167 WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
171 if (m_paintState->saveCount()) {
172 m_paintState->decrementSaveCount();
175 m_paintState = m_paintStateStack[m_paintStateIndex].get();
183 unsigned GraphicsContext::saveCount() const
185 // Each m_paintStateStack entry implies an additional save op
186 // (on top of its own saveCount), except for the first frame.
187 unsigned count = m_paintStateIndex;
188 ASSERT(m_paintStateStack.size() > m_paintStateIndex);
189 for (unsigned i = 0; i <= m_paintStateIndex; ++i)
190 count += m_paintStateStack[i]->saveCount();
196 void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint)
198 if (contextDisabled())
203 m_canvas->saveLayer(bounds, paint);
204 if (regionTrackingEnabled())
205 m_trackedRegion.pushCanvasLayer(paint);
208 void GraphicsContext::restoreLayer()
210 if (contextDisabled())
216 if (regionTrackingEnabled())
217 m_trackedRegion.popCanvasLayer(this);
220 void GraphicsContext::beginAnnotation(const AnnotationList& annotations)
222 if (contextDisabled())
227 canvas()->beginCommentGroup("GraphicsContextAnnotation");
229 AnnotationList::const_iterator end = annotations.end();
230 for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++it)
231 canvas()->addComment(it->first, it->second.ascii().data());
238 void GraphicsContext::endAnnotation()
240 if (contextDisabled())
244 ASSERT(m_annotationCount > 0);
245 canvas()->endCommentGroup();
252 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
254 if (contextDisabled())
259 setStrokeColor(Color::black);
263 mutableState()->setStrokePattern(pattern);
266 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
268 if (contextDisabled())
273 setStrokeColor(Color::black);
276 mutableState()->setStrokeGradient(gradient);
279 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
281 if (contextDisabled())
286 setFillColor(Color::black);
290 mutableState()->setFillPattern(pattern);
293 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
295 if (contextDisabled())
300 setFillColor(Color::black);
304 mutableState()->setFillGradient(gradient);
307 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color,
308 DrawLooperBuilder::ShadowTransformMode shadowTransformMode,
309 DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode, ShadowMode shadowMode)
311 if (contextDisabled())
314 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
315 if (!color.alpha()) {
316 if (shadowMode == DrawShadowOnly) {
317 // shadow only, but there is no shadow: use an empty draw looper to disable rendering of the source primitive
318 setDrawLooper(drawLooperBuilder.release());
325 drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode);
326 if (shadowMode == DrawShadowAndForeground) {
327 drawLooperBuilder->addUnmodifiedContent();
329 setDrawLooper(drawLooperBuilder.release());
331 if (shadowTransformMode == DrawLooperBuilder::ShadowIgnoresTransforms
332 && shadowAlphaMode == DrawLooperBuilder::ShadowRespectsAlpha) {
333 // This image filter will be used in place of the drawLooper created above but only for drawing non-opaque bitmaps;
334 // see preparePaintForDrawRectToRect().
335 SkColor skColor = color.rgb();
336 // These constants are from RadiusToSigma() from DrawLooperBuilder.cpp.
337 const SkScalar sigma = 0.288675f * blur + 0.5f;
338 SkDropShadowImageFilter::ShadowMode dropShadowMode = shadowMode == DrawShadowAndForeground ? SkDropShadowImageFilter::kDrawShadowAndForeground_ShadowMode : SkDropShadowImageFilter::kDrawShadowOnly_ShadowMode;
339 RefPtr<SkImageFilter> filter = adoptRef(SkDropShadowImageFilter::Create(offset.width(), offset.height(), sigma, sigma, skColor, dropShadowMode));
340 setDropShadowImageFilter(filter);
344 void GraphicsContext::setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder)
346 if (contextDisabled())
349 mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper());
352 void GraphicsContext::clearDrawLooper()
354 if (contextDisabled())
357 mutableState()->clearDrawLooper();
360 void GraphicsContext::setDropShadowImageFilter(PassRefPtr<SkImageFilter> imageFilter)
362 if (contextDisabled())
365 mutableState()->setDropShadowImageFilter(imageFilter);
368 void GraphicsContext::clearDropShadowImageFilter()
370 if (contextDisabled())
373 mutableState()->clearDropShadowImageFilter();
376 bool GraphicsContext::hasShadow() const
378 return !!immutableState()->drawLooper() || !!immutableState()->dropShadowImageFilter();
381 bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const
383 if (contextDisabled())
387 if (!m_canvas->getClipDeviceBounds(&skIBounds))
389 SkRect skBounds = SkRect::Make(skIBounds);
390 *bounds = FloatRect(skBounds);
394 SkMatrix GraphicsContext::getTotalMatrix() const
396 if (contextDisabled() || !m_canvas)
397 return SkMatrix::I();
402 return m_canvas->getTotalMatrix();
404 const RecordingState& recordingState = m_recordingStateStack.last();
405 SkMatrix totalMatrix = recordingState.m_savedMatrix;
406 totalMatrix.preConcat(m_canvas->getTotalMatrix());
411 void GraphicsContext::adjustTextRenderMode(SkPaint* paint) const
413 if (contextDisabled())
416 if (!paint->isLCDRenderText())
419 paint->setLCDRenderText(couldUseLCDRenderedText());
422 bool GraphicsContext::couldUseLCDRenderedText() const
425 // Our layers only have a single alpha channel. This means that subpixel
426 // rendered text cannot be composited correctly when the layer is
427 // collapsed. Therefore, subpixel text is contextDisabled when we are drawing
429 if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque())
432 return shouldSmoothFonts();
435 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode)
437 if (contextDisabled())
439 mutableState()->setCompositeOperation(compositeOperation, blendMode);
442 SkColorFilter* GraphicsContext::colorFilter() const
444 return immutableState()->colorFilter();
447 void GraphicsContext::setColorFilter(ColorFilter colorFilter)
449 GraphicsContextState* stateToSet = mutableState();
451 // We only support one active color filter at the moment. If (when) this becomes a problem,
452 // we should switch to using color filter chains (Skia work in progress).
453 ASSERT(!stateToSet->colorFilter());
454 stateToSet->setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter));
457 bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y)
459 if (contextDisabled())
463 return m_canvas->readPixels(info, pixels, rowBytes, x, y);
466 void GraphicsContext::setMatrix(const SkMatrix& matrix)
468 if (contextDisabled())
473 m_canvas->setMatrix(matrix);
476 void GraphicsContext::concat(const SkMatrix& matrix)
478 if (contextDisabled())
481 if (matrix.isIdentity())
486 m_canvas->concat(matrix);
489 void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds)
491 beginLayer(opacity, immutableState()->compositeOperator(), bounds);
494 void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilter colorFilter, ImageFilter* imageFilter)
496 if (contextDisabled())
500 layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
501 layerPaint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode()));
502 layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).get());
503 layerPaint.setImageFilter(imageFilter);
506 SkRect skBounds = WebCoreFloatRectToSKRect(*bounds);
507 saveLayer(&skBounds, &layerPaint);
509 saveLayer(0, &layerPaint);
517 void GraphicsContext::endLayer()
519 if (contextDisabled())
524 ASSERT(m_layerCount > 0);
530 void GraphicsContext::beginRecording(const FloatRect& bounds, uint32_t recordFlags)
532 RefPtr<DisplayList> displayList = DisplayList::create(bounds);
534 SkCanvas* savedCanvas = m_canvas;
535 SkMatrix savedMatrix = getTotalMatrix();
536 SkPictureRecorder* recorder = 0;
538 if (!contextDisabled()) {
539 FloatRect bounds = displayList->bounds();
540 recorder = new SkPictureRecorder;
541 m_canvas = recorder->beginRecording(bounds.width(), bounds.height(), 0, recordFlags);
543 // We want the bounds offset mapped to (0, 0), such that the display list content
544 // is fully contained within the SkPictureRecord's bounds.
545 if (!toFloatSize(bounds.location()).isZero()) {
546 m_canvas->translate(-bounds.x(), -bounds.y());
547 // To avoid applying the offset repeatedly in getTotalMatrix(), we pre-apply it here.
548 savedMatrix.preTranslate(bounds.x(), bounds.y());
552 m_recordingStateStack.append(RecordingState(recorder, savedCanvas, savedMatrix, m_shouldSmoothFonts, displayList,
553 static_cast<RegionTrackingMode>(m_regionTrackingMode)));
555 // Disable region tracking during recording.
556 setRegionTrackingMode(RegionTrackingDisabled);
559 PassRefPtr<DisplayList> GraphicsContext::endRecording()
561 ASSERT(!m_recordingStateStack.isEmpty());
563 RecordingState recording = m_recordingStateStack.last();
564 if (!contextDisabled())
565 recording.m_displayList->setPicture(recording.m_recorder->endRecording());
567 m_canvas = recording.m_savedCanvas;
568 setRegionTrackingMode(recording.m_regionTrackingMode);
569 setShouldSmoothFonts(recording.m_savedShouldSmoothFonts);
570 delete recording.m_recorder;
571 m_recordingStateStack.removeLast();
573 return recording.m_displayList;
576 bool GraphicsContext::isRecording() const
578 return !m_recordingStateStack.isEmpty();
581 void GraphicsContext::drawDisplayList(DisplayList* displayList)
586 if (contextDisabled() || displayList->bounds().isEmpty())
589 bool performClip = !displayList->clip().isEmpty();
590 bool performTransform = !displayList->transform().isIdentity();
591 if (performClip || performTransform) {
593 if (performTransform)
594 concat(displayList->transform());
596 clipRect(displayList->clip());
599 const FloatPoint& location = displayList->bounds().location();
600 if (location.x() || location.y()) {
602 m.setTranslate(location.x(), location.y());
603 m_canvas->drawPicture(displayList->picture().get(), &m, 0);
605 m_canvas->drawPicture(displayList->picture().get());
608 if (regionTrackingEnabled()) {
609 // Since we don't track regions within display lists, conservatively
610 // mark the bounds as non-opaque.
612 paint.setXfermodeMode(SkXfermode::kClear_Mode);
613 m_trackedRegion.didDrawBounded(this, displayList->bounds(), paint);
616 if (performClip || performTransform)
620 void GraphicsContext::drawPicture(SkPicture* picture, const FloatPoint& location)
625 if (contextDisabled())
628 if (location.x() || location.y()) {
630 m.setTranslate(location.x(), location.y());
631 m_canvas->drawPicture(picture, &m, 0);
633 m_canvas->drawPicture(picture);
636 if (regionTrackingEnabled()) {
637 // Since we don't track regions within pictures, conservatively
638 // mark the bounds as non-opaque.
640 paint.setXfermodeMode(SkXfermode::kClear_Mode);
641 FloatRect bound = picture->cullRect();
642 bound.moveBy(location);
643 m_trackedRegion.didDrawBounded(this, bound, paint);
647 static inline bool pictureScaleIsApproximatelyOne(float x)
649 return fabsf(x - 1.0f) < cPictureScaleEpsilon;
652 void GraphicsContext::drawPicture(SkPicture* picture, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode)
655 if (contextDisabled() || !picture)
658 SkMatrix ctm = m_canvas->getTotalMatrix();
660 ctm.mapRect(&deviceDest, dest);
661 float scaleX = deviceDest.width() / src.width();
662 float scaleY = deviceDest.height() / src.height();
664 SkPaint picturePaint;
665 picturePaint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op, blendMode));
666 SkRect sourceBounds = WebCoreFloatRectToSKRect(src);
667 if (pictureScaleIsApproximatelyOne(scaleX * m_deviceScaleFactor) && pictureScaleIsApproximatelyOne(scaleY * m_deviceScaleFactor)) {
668 // Fast path for canvases that are rasterized at screen resolution
669 SkRect skBounds = WebCoreFloatRectToSKRect(dest);
670 m_canvas->saveLayer(&skBounds, &picturePaint);
671 SkMatrix pictureTransform;
672 pictureTransform.setRectToRect(sourceBounds, skBounds, SkMatrix::kFill_ScaleToFit);
673 m_canvas->concat(pictureTransform);
674 m_canvas->drawPicture(picture);
677 RefPtr<SkPictureImageFilter> pictureFilter = adoptRef(SkPictureImageFilter::Create(picture, sourceBounds));
679 layerScale.setScale(scaleX, scaleY);
680 RefPtr<SkMatrixImageFilter> matrixFilter = adoptRef(SkMatrixImageFilter::Create(layerScale, SkPaint::kLow_FilterLevel, pictureFilter.get()));
681 picturePaint.setImageFilter(matrixFilter.get());
682 SkRect layerBounds = SkRect::MakeWH(std::max(deviceDest.width(), sourceBounds.width()), std::max(deviceDest.height(), sourceBounds.height()));
684 m_canvas->resetMatrix();
685 m_canvas->translate(deviceDest.x(), deviceDest.y());
686 m_canvas->saveLayer(&layerBounds, &picturePaint);
692 void GraphicsContext::fillPolygon(size_t numPoints, const FloatPoint* points, const Color& color,
693 bool shouldAntialias)
695 if (contextDisabled())
698 ASSERT(numPoints > 2);
701 setPathFromPoints(&path, numPoints, points);
703 SkPaint paint(immutableState()->fillPaint());
704 paint.setAntiAlias(shouldAntialias);
705 paint.setColor(color.rgb());
707 drawPath(path, paint);
710 float GraphicsContext::prepareFocusRingPaint(SkPaint& paint, const Color& color, int width) const
712 paint.setAntiAlias(true);
713 paint.setStyle(SkPaint::kStroke_Style);
714 paint.setColor(color.rgb());
715 paint.setStrokeWidth(focusRingWidth(width));
719 return (width - 1) * 0.5f;
725 void GraphicsContext::drawFocusRingPath(const SkPath& path, const Color& color, int width)
728 float cornerRadius = prepareFocusRingPaint(paint, color, width);
730 paint.setPathEffect(SkCornerPathEffect::Create(SkFloatToScalar(cornerRadius)))->unref();
733 drawPath(path, paint);
738 paint.setStrokeWidth(paint.getStrokeWidth() * 0.5f);
739 drawPath(path, paint);
743 void GraphicsContext::drawFocusRingRect(const SkRect& rect, const Color& color, int width)
746 float cornerRadius = prepareFocusRingPaint(paint, color, width);
749 rrect.setRectXY(rect, SkFloatToScalar(cornerRadius), SkFloatToScalar(cornerRadius));
752 drawRRect(rrect, paint);
757 paint.setStrokeWidth(paint.getStrokeWidth() * 0.5f);
758 drawRRect(rrect, paint);
762 void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int offset, const Color& color)
764 // FIXME: Implement support for offset.
765 if (contextDisabled())
768 drawFocusRingPath(focusRingPath.skPath(), color, width);
771 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
773 if (contextDisabled())
776 unsigned rectCount = rects.size();
780 SkRegion focusRingRegion;
781 const int outset = focusRingOutset(offset);
782 for (unsigned i = 0; i < rectCount; i++) {
783 SkIRect r = rects[i];
784 r.inset(-outset, -outset);
785 focusRingRegion.op(r, SkRegion::kUnion_Op);
788 if (focusRingRegion.isRect()) {
789 drawFocusRingRect(SkRect::MakeFromIRect(focusRingRegion.getBounds()), color, width);
792 if (focusRingRegion.getBoundaryPath(&path))
793 drawFocusRingPath(path, color, width);
797 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
799 IntRect bounds(holeRect);
801 bounds.inflate(shadowBlur);
803 if (shadowSpread < 0)
804 bounds.inflate(-shadowSpread);
806 IntRect offsetBounds = bounds;
807 offsetBounds.move(-shadowOffset);
808 return unionRect(bounds, offsetBounds);
811 void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges)
813 if (contextDisabled())
816 IntRect holeRect(rect.rect());
817 holeRect.inflate(-shadowSpread);
819 if (holeRect.isEmpty()) {
820 if (rect.isRounded())
821 fillRoundedRect(rect, shadowColor);
823 fillRect(rect.rect(), shadowColor);
827 if (clippedEdges & LeftEdge) {
828 holeRect.move(-std::max(shadowOffset.width(), 0) - shadowBlur, 0);
829 holeRect.setWidth(holeRect.width() + std::max(shadowOffset.width(), 0) + shadowBlur);
831 if (clippedEdges & TopEdge) {
832 holeRect.move(0, -std::max(shadowOffset.height(), 0) - shadowBlur);
833 holeRect.setHeight(holeRect.height() + std::max(shadowOffset.height(), 0) + shadowBlur);
835 if (clippedEdges & RightEdge)
836 holeRect.setWidth(holeRect.width() - std::min(shadowOffset.width(), 0) + shadowBlur);
837 if (clippedEdges & BottomEdge)
838 holeRect.setHeight(holeRect.height() - std::min(shadowOffset.height(), 0) + shadowBlur);
840 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
842 IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset);
843 RoundedRect roundedHole(holeRect, rect.radii());
846 if (rect.isRounded()) {
848 path.addRoundedRect(rect);
850 roundedHole.shrinkRadii(shadowSpread);
855 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
856 drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor,
857 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
858 setDrawLooper(drawLooperBuilder.release());
859 fillRectWithRoundedHole(outerRect, roundedHole, fillColor);
864 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
867 if (contextDisabled())
870 StrokeStyle penStyle = strokeStyle();
871 if (penStyle == NoStroke)
874 FloatPoint p1 = point1;
875 FloatPoint p2 = point2;
876 bool isVerticalLine = (p1.x() == p2.x());
877 int width = roundf(strokeThickness());
879 // We know these are vertical or horizontal lines, so the length will just
880 // be the sum of the displacement component vectors give or take 1 -
881 // probably worth the speed up of no square root, which also won't be exact.
882 FloatSize disp = p2 - p1;
883 int length = SkScalarRoundToInt(disp.width() + disp.height());
884 SkPaint paint(immutableState()->strokePaint(length));
886 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
887 // Do a rect fill of our endpoints. This ensures we always have the
888 // appearance of being a border. We then draw the actual dotted/dashed line.
890 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
891 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
893 if (isVerticalLine) {
894 r1.offset(-width / 2, 0);
895 r2.offset(-width / 2, -width);
897 r1.offset(0, -width / 2);
898 r2.offset(-width, -width / 2);
901 fillPaint.setColor(paint.getColor());
902 drawRect(r1, fillPaint);
903 drawRect(r2, fillPaint);
906 adjustLineToPixelBoundaries(p1, p2, width, penStyle);
907 SkPoint pts[2] = { p1.data(), p2.data() };
909 m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
911 if (regionTrackingEnabled())
912 m_trackedRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint);
915 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
917 if (contextDisabled())
920 // Use 2x resources for a device scale factor of 1.5 or above.
921 int deviceScaleFactor = m_deviceScaleFactor > 1.5f ? 2 : 1;
923 // Create the pattern we'll use to draw the underline.
924 int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0;
925 static SkBitmap* misspellBitmap1x[2] = { 0, 0 };
926 static SkBitmap* misspellBitmap2x[2] = { 0, 0 };
927 SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x;
928 if (!misspellBitmap[index]) {
930 // Match the artwork used by the Mac.
931 const int rowPixels = 4 * deviceScaleFactor;
932 const int colPixels = 3 * deviceScaleFactor;
934 if (!bitmap.tryAllocN32Pixels(rowPixels, colPixels))
937 bitmap.eraseARGB(0, 0, 0, 0);
938 const uint32_t transparentColor = 0x00000000;
940 if (deviceScaleFactor == 1) {
941 const uint32_t colors[2][6] = {
942 { 0x2a2a0600, 0x57571000, 0xa8a81b00, 0xbfbf1f00, 0x70701200, 0xe0e02400 },
943 { 0x2a0f0f0f, 0x571e1e1e, 0xa83d3d3d, 0xbf454545, 0x70282828, 0xe0515151 }
946 // Pattern: a b a a b a
949 for (int x = 0; x < colPixels; ++x) {
950 uint32_t* row = bitmap.getAddr32(0, x);
951 row[0] = colors[index][x * 2];
952 row[1] = colors[index][x * 2 + 1];
953 row[2] = colors[index][x * 2];
954 row[3] = transparentColor;
956 } else if (deviceScaleFactor == 2) {
957 const uint32_t colors[2][18] = {
958 { 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810,
959 0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 },
960 { 0x0a020202, 0x33141414, 0x55232323, 0x37161616, 0x6e2e2e2e, 0x6e2e2e2e, 0x76313131, 0x8d3a3a3a, 0x8d3a3a3a,
961 0x963e3e3e, 0xb34b4b4b, 0xb34b4b4b, 0x6d2d2d2d, 0xd95b5b5b, 0xd95b5b5b, 0x19090909, 0x7c343434, 0xce575757 }
964 // Pattern: a b c c b a
970 for (int x = 0; x < colPixels; ++x) {
971 uint32_t* row = bitmap.getAddr32(0, x);
972 row[0] = colors[index][x * 3];
973 row[1] = colors[index][x * 3 + 1];
974 row[2] = colors[index][x * 3 + 2];
975 row[3] = colors[index][x * 3 + 2];
976 row[4] = colors[index][x * 3 + 1];
977 row[5] = colors[index][x * 3];
978 row[6] = transparentColor;
979 row[7] = transparentColor;
982 ASSERT_NOT_REACHED();
984 misspellBitmap[index] = new SkBitmap(bitmap);
986 // We use a 2-pixel-high misspelling indicator because that seems to be
987 // what WebKit is designed for, and how much room there is in a typical
989 const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below.
990 const int colPixels = 2 * deviceScaleFactor;
992 if (!bitmap.tryAllocN32Pixels(rowPixels, colPixels))
995 bitmap.eraseARGB(0, 0, 0, 0);
996 if (deviceScaleFactor == 1)
997 draw1xMarker(&bitmap, index);
998 else if (deviceScaleFactor == 2)
999 draw2xMarker(&bitmap, index);
1001 ASSERT_NOT_REACHED();
1003 misspellBitmap[index] = new SkBitmap(bitmap);
1008 SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
1009 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
1011 // Make sure to draw only complete dots.
1012 int rowPixels = misspellBitmap[index]->width();
1013 float widthMod = fmodf(width * deviceScaleFactor, rowPixels);
1014 if (rowPixels - widthMod > deviceScaleFactor)
1015 width -= widthMod / deviceScaleFactor;
1017 SkScalar originX = WebCoreFloatToSkScalar(pt.x());
1019 // Offset it vertically by 1 so that there's some space under the text.
1020 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
1021 originX *= deviceScaleFactor;
1022 originY *= deviceScaleFactor;
1025 SkMatrix localMatrix;
1026 localMatrix.setTranslate(originX, originY);
1027 RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(
1028 *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
1031 paint.setShader(shader.get());
1034 rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
1036 if (deviceScaleFactor == 2) {
1040 drawRect(rect, paint);
1041 if (deviceScaleFactor == 2)
1045 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool printing)
1047 if (contextDisabled())
1054 switch (strokeStyle()) {
1059 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
1061 r.fLeft = WebCoreFloatToSkScalar(pt.x());
1062 // Avoid anti-aliasing lines. Currently, these are always horizontal.
1063 // Round to nearest pixel to match text and other content.
1064 r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f));
1065 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
1066 r.fBottom = r.fTop + SkIntToScalar(thickness);
1067 paint = immutableState()->fillPaint();
1068 // Text lines are drawn using the stroke color.
1069 paint.setColor(effectiveStrokeColor());
1074 case DashedStroke: {
1075 int y = floorf(pt.y() + std::max<float>(strokeThickness() / 2.0f, 0.5f));
1076 drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y));
1081 ASSERT_NOT_REACHED();
1084 // Draws a filled rectangle with a stroked border.
1085 void GraphicsContext::drawRect(const IntRect& rect)
1087 if (contextDisabled())
1090 ASSERT(!rect.isEmpty());
1094 SkRect skRect = rect;
1095 int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000;
1096 if (fillcolorNotTransparent)
1097 drawRect(skRect, immutableState()->fillPaint());
1099 if (immutableState()->strokeData().style() != NoStroke
1100 && immutableState()->strokeColor().alpha()) {
1101 // Stroke a width: 1 inset border
1102 SkPaint paint(immutableState()->fillPaint());
1103 paint.setColor(effectiveStrokeColor());
1104 paint.setStyle(SkPaint::kStroke_Style);
1105 paint.setStrokeWidth(1);
1107 skRect.inset(0.5f, 0.5f);
1108 drawRect(skRect, paint);
1112 void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point)
1114 if (contextDisabled())
1117 font.drawText(this, runInfo, point);
1120 void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point)
1122 if (contextDisabled())
1125 font.drawEmphasisMarks(this, runInfo, mark, point);
1128 void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
1130 if (contextDisabled())
1133 // sub-run painting is not supported for Bidi text.
1134 const TextRun& run = runInfo.run;
1135 ASSERT((runInfo.from == 0) && (runInfo.to == run.length()));
1136 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
1137 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
1138 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
1140 // FIXME: This ownership should be reversed. We should pass BidiRunList
1141 // to BidiResolver in createBidiRunsForLine.
1142 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
1143 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
1144 if (!bidiRuns.runCount())
1147 FloatPoint currPoint = point;
1148 BidiCharacterRun* bidiRun = bidiRuns.firstRun();
1150 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
1151 bool isRTL = bidiRun->level() % 2;
1152 subrun.setDirection(isRTL ? RTL : LTR);
1153 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
1155 TextRunPaintInfo subrunInfo(subrun);
1156 subrunInfo.bounds = runInfo.bounds;
1157 float runWidth = font.drawUncachedText(this, subrunInfo, currPoint, customFontNotReadyAction);
1159 bidiRun = bidiRun->next();
1160 currPoint.move(runWidth, 0);
1163 bidiRuns.deleteRuns();
1166 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to)
1168 if (contextDisabled())
1171 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor);
1174 void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1178 drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
1181 void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1185 drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
1188 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1190 drawImage(image, dest, src, op, WebBlendModeNormal, shouldRespectImageOrientation);
1193 void GraphicsContext::drawImage(Image* image, const FloatRect& dest)
1197 drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size())));
1200 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation)
1202 if (contextDisabled() || !image)
1204 image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation);
1207 void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing)
1209 if (contextDisabled() || !image)
1211 image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing);
1214 void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect,
1215 const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op)
1217 if (contextDisabled() || !image)
1220 if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
1222 drawImage(image, dest, srcRect, op);
1226 image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op);
1229 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest,
1230 const FloatRect* src, CompositeOperator op, WebBlendMode blendMode)
1232 if (contextDisabled() || !image)
1235 image->draw(this, dest, src, op, blendMode);
1238 void GraphicsContext::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y)
1241 if (contextDisabled())
1244 m_canvas->writePixels(info, pixels, rowBytes, x, y);
1246 if (regionTrackingEnabled()) {
1247 SkRect rect = SkRect::MakeXYWH(x, y, info.width(), info.height());
1250 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1251 if (kOpaque_SkAlphaType != info.alphaType())
1252 paint.setAlpha(0x80); // signal to m_trackedRegion that we are not fully opaque
1254 m_trackedRegion.didDrawRect(this, rect, paint, 0);
1255 // more efficient would be to call markRectAsOpaque or MarkRectAsNonOpaque directly,
1256 // rather than cons-ing up a paint with an xfermode and alpha
1260 void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint)
1263 // Textures are bound to the blink main-thread GrContext, which can not be
1264 // used on the compositor raster thread.
1265 // FIXME: Mailbox support would make this possible in the GPU-raster case.
1266 ASSERT(!isRecording() || !bitmap.getTexture());
1267 if (contextDisabled())
1270 m_canvas->drawBitmap(bitmap, left, top, paint);
1272 if (regionTrackingEnabled()) {
1273 SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height());
1274 m_trackedRegion.didDrawRect(this, rect, *paint, &bitmap);
1278 void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
1279 const SkRect& dst, const SkPaint* paint)
1282 // Textures are bound to the blink main-thread GrContext, which can not be
1283 // used on the compositor raster thread.
1284 // FIXME: Mailbox support would make this possible in the GPU-raster case.
1285 ASSERT(!isRecording() || !bitmap.getTexture());
1286 if (contextDisabled())
1289 SkCanvas::DrawBitmapRectFlags flags =
1290 immutableState()->shouldClampToSourceRect() ? SkCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag;
1292 m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags);
1294 if (regionTrackingEnabled())
1295 m_trackedRegion.didDrawRect(this, dst, *paint, &bitmap);
1298 void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint)
1301 if (contextDisabled())
1304 m_canvas->drawOval(oval, paint);
1306 if (regionTrackingEnabled())
1307 m_trackedRegion.didDrawBounded(this, oval, paint);
1310 void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint)
1313 if (contextDisabled())
1316 m_canvas->drawPath(path, paint);
1318 if (regionTrackingEnabled())
1319 m_trackedRegion.didDrawPath(this, path, paint);
1322 void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint)
1325 if (contextDisabled())
1328 m_canvas->drawRect(rect, paint);
1330 if (regionTrackingEnabled())
1331 m_trackedRegion.didDrawRect(this, rect, paint, 0);
1334 void GraphicsContext::drawRRect(const SkRRect& rrect, const SkPaint& paint)
1337 if (contextDisabled())
1340 m_canvas->drawRRect(rrect, paint);
1342 if (regionTrackingEnabled())
1343 m_trackedRegion.didDrawBounded(this, rrect.rect(), paint);
1346 void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap)
1348 if (contextDisabled())
1351 if (regionTrackingEnabled())
1352 m_trackedRegion.didDrawRect(this, rect, paint, bitmap);
1355 void GraphicsContext::drawPosText(const void* text, size_t byteLength,
1356 const SkPoint pos[], const SkRect& textRect, const SkPaint& paint)
1359 if (contextDisabled())
1362 m_canvas->drawPosText(text, byteLength, pos, paint);
1363 didDrawTextInRect(textRect);
1365 // FIXME: compute bounds for positioned text.
1366 if (regionTrackingEnabled())
1367 m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke);
1370 void GraphicsContext::drawPosTextH(const void* text, size_t byteLength,
1371 const SkScalar xpos[], SkScalar constY, const SkRect& textRect, const SkPaint& paint)
1374 if (contextDisabled())
1377 m_canvas->drawPosTextH(text, byteLength, xpos, constY, paint);
1378 didDrawTextInRect(textRect);
1380 // FIXME: compute bounds for positioned text.
1381 if (regionTrackingEnabled())
1382 m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke);
1385 void GraphicsContext::drawTextBlob(const SkTextBlob* blob, const SkPoint& origin, const SkPaint& paint)
1388 if (contextDisabled())
1391 m_canvas->drawTextBlob(blob, origin.x(), origin.y(), paint);
1393 SkRect bounds = blob->bounds();
1394 bounds.offset(origin);
1395 didDrawTextInRect(bounds);
1397 // FIXME: use bounds here if it helps performance.
1398 if (regionTrackingEnabled())
1399 m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke);
1402 void GraphicsContext::fillPath(const Path& pathToFill)
1404 if (contextDisabled() || pathToFill.isEmpty())
1407 // Use const_cast and temporarily modify the fill type instead of copying the path.
1408 SkPath& path = const_cast<SkPath&>(pathToFill.skPath());
1409 SkPath::FillType previousFillType = path.getFillType();
1411 SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(immutableState()->fillRule());
1412 path.setFillType(temporaryFillType);
1414 drawPath(path, immutableState()->fillPaint());
1416 path.setFillType(previousFillType);
1419 void GraphicsContext::fillRect(const FloatRect& rect)
1421 if (contextDisabled())
1426 drawRect(r, immutableState()->fillPaint());
1429 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
1431 if (contextDisabled())
1435 SkPaint paint = immutableState()->fillPaint();
1436 paint.setColor(color.rgb());
1440 void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight,
1441 const IntRect& inner, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color& color)
1444 if (contextDisabled())
1447 SkVector outerRadii[4];
1448 SkVector innerRadii[4];
1449 setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft);
1450 setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft);
1454 rrOuter.setRectRadii(outer, outerRadii);
1455 rrInner.setRectRadii(inner, innerRadii);
1457 SkPaint paint(immutableState()->fillPaint());
1458 paint.setColor(color.rgb());
1460 m_canvas->drawDRRect(rrOuter, rrInner, paint);
1462 if (regionTrackingEnabled())
1463 m_trackedRegion.didDrawBounded(this, rrOuter.getBounds(), paint);
1466 void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color)
1468 fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(),
1469 inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color);
1472 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
1473 const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
1476 if (contextDisabled())
1479 if (topLeft.width() + topRight.width() > rect.width()
1480 || bottomLeft.width() + bottomRight.width() > rect.width()
1481 || topLeft.height() + bottomLeft.height() > rect.height()
1482 || topRight.height() + bottomRight.height() > rect.height()) {
1483 // Not all the radii fit, return a rect. This matches the behavior of
1484 // Path::createRoundedRectangle. Without this we attempt to draw a round
1485 // shadow for a square box.
1486 fillRect(rect, color);
1491 setRadii(radii, topLeft, topRight, bottomRight, bottomLeft);
1494 rr.setRectRadii(rect, radii);
1496 SkPaint paint(immutableState()->fillPaint());
1497 paint.setColor(color.rgb());
1499 m_canvas->drawRRect(rr, paint);
1501 if (regionTrackingEnabled())
1502 m_trackedRegion.didDrawBounded(this, rr.getBounds(), paint);
1505 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
1507 if (contextDisabled())
1510 SkRect rect = ellipse;
1511 drawOval(rect, immutableState()->fillPaint());
1514 void GraphicsContext::strokePath(const Path& pathToStroke)
1516 if (contextDisabled() || pathToStroke.isEmpty())
1519 const SkPath& path = pathToStroke.skPath();
1520 drawPath(path, immutableState()->strokePaint());
1523 void GraphicsContext::strokeRect(const FloatRect& rect)
1525 strokeRect(rect, strokeThickness());
1528 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1530 if (contextDisabled())
1533 SkPaint paint(immutableState()->strokePaint());
1534 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1535 // Reset the dash effect to account for the width
1536 immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0);
1537 // strokerect has special rules for CSS when the rect is degenerate:
1538 // if width==0 && height==0, do nothing
1539 // if width==0 || height==0, then just draw line for the other dimension
1541 bool validW = r.width() > 0;
1542 bool validH = r.height() > 0;
1543 if (validW && validH) {
1545 } else if (validW || validH) {
1546 // we are expected to respect the lineJoin, so we can't just call
1547 // drawLine -- we have to create a path that doubles back on itself.
1549 path.moveTo(r.fLeft, r.fTop);
1550 path.lineTo(r.fRight, r.fBottom);
1552 drawPath(path, paint);
1556 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
1558 if (contextDisabled())
1561 drawOval(ellipse, immutableState()->strokePaint());
1564 void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkRegion::Op regionOp)
1566 if (contextDisabled())
1569 if (!rect.isRounded()) {
1570 clipRect(rect.rect(), NotAntiAliased, regionOp);
1575 RoundedRect::Radii wkRadii = rect.radii();
1576 setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft());
1579 r.setRectRadii(rect.rect(), radii);
1581 clipRRect(r, AntiAliased, regionOp);
1584 void GraphicsContext::clipOut(const Path& pathToClip)
1586 if (contextDisabled())
1589 // Use const_cast and temporarily toggle the inverse fill type instead of copying the path.
1590 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1591 path.toggleInverseFillType();
1592 clipPath(path, AntiAliased);
1593 path.toggleInverseFillType();
1596 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
1598 if (contextDisabled() || pathToClip.isEmpty())
1601 // Use const_cast and temporarily modify the fill type instead of copying the path.
1602 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1603 SkPath::FillType previousFillType = path.getFillType();
1605 SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule);
1606 path.setFillType(temporaryFillType);
1607 clipPath(path, AntiAliased);
1609 path.setFillType(previousFillType);
1612 void GraphicsContext::clipPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
1614 if (contextDisabled())
1617 ASSERT(numPoints > 2);
1620 setPathFromPoints(&path, numPoints, points);
1621 clipPath(path, antialiased ? AntiAliased : NotAntiAliased);
1624 void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
1626 if (contextDisabled())
1629 clipRoundedRect(rect, SkRegion::kDifference_Op);
1632 void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule, AntiAliasingMode aa)
1634 if (contextDisabled())
1637 // Use const_cast and temporarily modify the fill type instead of copying the path.
1638 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1639 SkPath::FillType previousFillType = path.getFillType();
1641 SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule);
1642 path.setFillType(temporaryFillType);
1645 path.setFillType(previousFillType);
1648 void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1651 if (contextDisabled())
1654 m_canvas->clipRect(rect, op, aa == AntiAliased);
1657 void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion::Op op)
1660 if (contextDisabled())
1663 m_canvas->clipPath(path, op, aa == AntiAliased);
1666 void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1669 if (contextDisabled())
1672 m_canvas->clipRRect(rect, op, aa == AntiAliased);
1675 void GraphicsContext::beginCull(const FloatRect& rect)
1678 if (contextDisabled())
1681 m_canvas->pushCull(rect);
1684 void GraphicsContext::endCull()
1687 if (contextDisabled())
1690 m_canvas->popCull();
1693 void GraphicsContext::rotate(float angleInRadians)
1696 if (contextDisabled())
1699 m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f)));
1702 void GraphicsContext::translate(float x, float y)
1705 if (contextDisabled())
1711 m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1714 void GraphicsContext::scale(float x, float y)
1717 if (contextDisabled())
1720 if (x == 1.0f && y == 1.0f)
1723 m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1726 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1729 if (contextDisabled())
1732 SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data()));
1733 SkAnnotateRectWithURL(m_canvas, destRect, url.get());
1736 void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRect& rect)
1739 if (contextDisabled())
1742 SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data()));
1743 SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get());
1746 void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& pos)
1749 if (contextDisabled())
1752 SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data()));
1753 SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameData);
1756 AffineTransform GraphicsContext::getCTM() const
1758 if (contextDisabled())
1759 return AffineTransform();
1761 SkMatrix m = getTotalMatrix();
1762 return AffineTransform(SkScalarToDouble(m.getScaleX()),
1763 SkScalarToDouble(m.getSkewY()),
1764 SkScalarToDouble(m.getSkewX()),
1765 SkScalarToDouble(m.getScaleY()),
1766 SkScalarToDouble(m.getTranslateX()),
1767 SkScalarToDouble(m.getTranslateY()));
1770 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op)
1772 if (contextDisabled())
1775 CompositeOperator previousOperator = compositeOperation();
1776 setCompositeOperation(op);
1777 fillRect(rect, color);
1778 setCompositeOperation(previousOperator);
1781 void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color)
1783 if (contextDisabled())
1786 if (rect.isRounded())
1787 fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color);
1789 fillRect(rect.rect(), color);
1792 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color)
1794 if (contextDisabled())
1800 if (!roundedHoleRect.radii().isZero())
1801 path.addRoundedRect(roundedHoleRect);
1803 path.addRect(roundedHoleRect.rect());
1805 WindRule oldFillRule = fillRule();
1806 Color oldFillColor = fillColor();
1808 setFillRule(RULE_EVENODD);
1809 setFillColor(color);
1813 setFillRule(oldFillRule);
1814 setFillColor(oldFillColor);
1817 void GraphicsContext::clearRect(const FloatRect& rect)
1819 if (contextDisabled())
1823 SkPaint paint(immutableState()->fillPaint());
1824 paint.setXfermodeMode(SkXfermode::kClear_Mode);
1828 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
1830 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
1831 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
1832 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
1833 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
1834 if (penStyle == DottedStroke || penStyle == DashedStroke) {
1835 if (p1.x() == p2.x()) {
1836 p1.setY(p1.y() + strokeWidth);
1837 p2.setY(p2.y() - strokeWidth);
1839 p1.setX(p1.x() + strokeWidth);
1840 p2.setX(p2.x() - strokeWidth);
1844 if (static_cast<int>(strokeWidth) % 2) { //odd
1845 if (p1.x() == p2.x()) {
1846 // We're a vertical line. Adjust our x.
1847 p1.setX(p1.x() + 0.5f);
1848 p2.setX(p2.x() + 0.5f);
1850 // We're a horizontal line. Adjust our y.
1851 p1.setY(p1.y() + 0.5f);
1852 p2.setY(p2.y() + 0.5f);
1857 PassOwnPtr<ImageBuffer> GraphicsContext::createRasterBuffer(const IntSize& size, OpacityMode opacityMode) const
1859 // Make the buffer larger if the context's transform is scaling it so we need a higher
1860 // resolution than one pixel per unit. Also set up a corresponding scale factor on the
1861 // graphics context.
1863 AffineTransform transform = getCTM();
1864 IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
1866 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(scaledSize, opacityMode));
1867 if (!surface->isValid())
1869 OwnPtr<ImageBuffer> buffer = adoptPtr(new ImageBuffer(surface.release()));
1871 buffer->context()->scale(static_cast<float>(scaledSize.width()) / size.width(),
1872 static_cast<float>(scaledSize.height()) / size.height());
1874 return buffer.release();
1877 void GraphicsContext::setPathFromPoints(SkPath* path, size_t numPoints, const FloatPoint* points)
1879 path->incReserve(numPoints);
1880 path->moveTo(WebCoreFloatToSkScalar(points[0].x()),
1881 WebCoreFloatToSkScalar(points[0].y()));
1882 for (size_t i = 1; i < numPoints; ++i) {
1883 path->lineTo(WebCoreFloatToSkScalar(points[i].x()),
1884 WebCoreFloatToSkScalar(points[i].y()));
1888 void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft)
1890 radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()),
1891 SkIntToScalar(topLeft.height()));
1892 radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()),
1893 SkIntToScalar(topRight.height()));
1894 radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()),
1895 SkIntToScalar(bottomRight.height()));
1896 radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()),
1897 SkIntToScalar(bottomLeft.height()));
1900 PassRefPtr<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter)
1902 switch (colorFilter) {
1903 case ColorFilterLuminanceToAlpha:
1904 return adoptRef(SkLumaColorFilter::Create());
1905 case ColorFilterLinearRGBToSRGB:
1906 return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
1907 case ColorFilterSRGBToLinearRGB:
1908 return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
1909 case ColorFilterNone:
1912 ASSERT_NOT_REACHED();
1920 void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index)
1922 const SkPMColor lineColor = lineColors(index);
1923 const SkPMColor antiColor1 = antiColors1(index);
1924 const SkPMColor antiColor2 = antiColors2(index);
1926 uint32_t* row1 = bitmap->getAddr32(0, 0);
1927 uint32_t* row2 = bitmap->getAddr32(0, 1);
1928 uint32_t* row3 = bitmap->getAddr32(0, 2);
1929 uint32_t* row4 = bitmap->getAddr32(0, 3);
1931 // Pattern: X0o o0X0o o0
1935 const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 };
1936 const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor };
1937 const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 };
1938 const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 };
1940 for (int x = 0; x < bitmap->width() + 8; x += 8) {
1941 int count = std::min(bitmap->width() - x, 8);
1943 memcpy(row1 + x, row1Color, count * sizeof(SkPMColor));
1944 memcpy(row2 + x, row2Color, count * sizeof(SkPMColor));
1945 memcpy(row3 + x, row3Color, count * sizeof(SkPMColor));
1946 memcpy(row4 + x, row4Color, count * sizeof(SkPMColor));
1951 void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index)
1953 const uint32_t lineColor = lineColors(index);
1954 const uint32_t antiColor = antiColors2(index);
1956 // Pattern: X o o X o o X
1958 uint32_t* row1 = bitmap->getAddr32(0, 0);
1959 uint32_t* row2 = bitmap->getAddr32(0, 1);
1960 for (int x = 0; x < bitmap->width(); x++) {
1963 row1[x] = lineColor;
1966 row1[x] = antiColor;
1967 row2[x] = antiColor;
1970 row2[x] = lineColor;
1973 row1[x] = antiColor;
1974 row2[x] = antiColor;
1980 SkPMColor GraphicsContext::lineColors(int index)
1982 static const SkPMColor colors[] = {
1983 SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
1984 SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray.
1987 return colors[index];
1990 SkPMColor GraphicsContext::antiColors1(int index)
1992 static const SkPMColor colors[] = {
1993 SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red.
1994 SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray.
1997 return colors[index];
2000 SkPMColor GraphicsContext::antiColors2(int index)
2002 static const SkPMColor colors[] = {
2003 SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
2004 SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray
2007 return colors[index];
2011 void GraphicsContext::didDrawTextInRect(const SkRect& textRect)
2013 if (m_trackTextRegion) {
2014 TRACE_EVENT0("skia", "GraphicsContext::didDrawTextInRect");
2015 m_textRegion.join(textRect);
2019 void GraphicsContext::preparePaintForDrawRectToRect(
2021 const SkRect& srcRect,
2022 const SkRect& destRect,
2023 CompositeOperator compositeOp,
2024 WebBlendMode blendMode,
2025 bool isBitmapWithAlpha,
2027 bool isDataComplete) const
2029 paint->setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode));
2030 paint->setColorFilter(this->colorFilter());
2031 paint->setAlpha(this->getNormalizedAlpha());
2032 if (this->dropShadowImageFilter() && isBitmapWithAlpha) {
2033 paint->setImageFilter(this->dropShadowImageFilter());
2035 paint->setLooper(this->drawLooper());
2037 paint->setAntiAlias(shouldDrawAntiAliased(this, destRect));
2039 InterpolationQuality resampling;
2040 if (this->isAccelerated()) {
2041 resampling = InterpolationLow;
2042 } else if (this->printing()) {
2043 resampling = InterpolationNone;
2044 } else if (isLazyDecoded) {
2045 resampling = InterpolationHigh;
2047 // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
2048 SkRect destRectTarget = destRect;
2049 SkMatrix totalMatrix = this->getTotalMatrix();
2050 if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
2051 totalMatrix.mapRect(&destRectTarget, destRect);
2053 resampling = computeInterpolationQuality(totalMatrix,
2054 SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
2055 SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()),
2059 if (resampling == InterpolationNone) {
2060 // FIXME: This is to not break tests (it results in the filter bitmap flag
2061 // being set to true). We need to decide if we respect InterpolationNone
2062 // being returned from computeInterpolationQuality.
2063 resampling = InterpolationLow;
2065 resampling = limitInterpolationQuality(this, resampling);
2066 paint->setFilterLevel(static_cast<SkPaint::FilterLevel>(resampling));
2069 } // namespace blink