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/SkLumaColorFilter.h"
55 #include "third_party/skia/include/effects/SkMatrixImageFilter.h"
56 #include "third_party/skia/include/effects/SkPictureImageFilter.h"
57 #include "third_party/skia/include/gpu/GrRenderTarget.h"
58 #include "third_party/skia/include/gpu/GrTexture.h"
59 #include "wtf/Assertions.h"
60 #include "wtf/MathExtras.h"
64 struct GraphicsContext::CanvasSaveState {
65 CanvasSaveState(bool pendingSave, int count)
66 : m_pendingSave(pendingSave), m_restoreCount(count) { }
72 struct GraphicsContext::RecordingState {
73 RecordingState(SkPictureRecorder* recorder, SkCanvas* currentCanvas, const SkMatrix& currentMatrix, bool currentShouldSmoothFonts,
74 PassRefPtr<DisplayList> displayList, RegionTrackingMode trackingMode)
75 : m_displayList(displayList)
76 , m_recorder(recorder)
77 , m_savedCanvas(currentCanvas)
78 , m_savedMatrix(currentMatrix)
79 , m_savedShouldSmoothFonts(currentShouldSmoothFonts)
80 , m_regionTrackingMode(trackingMode) { }
84 RefPtr<DisplayList> m_displayList;
85 SkPictureRecorder* m_recorder;
86 SkCanvas* m_savedCanvas;
87 const SkMatrix m_savedMatrix;
88 bool m_savedShouldSmoothFonts;
89 RegionTrackingMode m_regionTrackingMode;
92 GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting)
95 , m_paintStateIndex(0)
96 , m_pendingCanvasSave(false)
99 , m_annotationCount(0)
101 , m_disableDestructionChecks(false)
103 , m_disabledState(disableContextOrPainting)
104 , m_deviceScaleFactor(1.0f)
105 , m_regionTrackingMode(RegionTrackingDisabled)
106 , m_trackTextRegion(false)
107 , m_accelerated(false)
108 , m_isCertainlyOpaque(true)
110 , m_antialiasHairlineImages(false)
111 , m_shouldSmoothFonts(true)
113 // FIXME: Do some tests to determine how many states are typically used, and allocate
115 m_paintStateStack.append(GraphicsContextState::create());
116 m_paintState = m_paintStateStack.last().get();
119 GraphicsContext::~GraphicsContext()
122 if (!m_disableDestructionChecks) {
123 ASSERT(!m_paintStateIndex);
124 ASSERT(!m_paintState->saveCount());
125 ASSERT(!m_annotationCount);
126 ASSERT(!m_layerCount);
127 ASSERT(m_recordingStateStack.isEmpty());
128 ASSERT(m_canvasStateStack.isEmpty());
133 void GraphicsContext::resetCanvas(SkCanvas* canvas)
136 m_trackedRegion.reset();
139 void GraphicsContext::setRegionTrackingMode(RegionTrackingMode mode)
141 m_regionTrackingMode = mode;
142 if (mode == RegionTrackingOpaque)
143 m_trackedRegion.setTrackedRegionType(RegionTracker::Opaque);
144 else if (mode == RegionTrackingOverwrite)
145 m_trackedRegion.setTrackedRegionType(RegionTracker::Overwrite);
148 void GraphicsContext::save()
150 if (contextDisabled())
153 m_paintState->incrementSaveCount();
156 m_canvasStateStack.append(CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount()));
157 m_pendingCanvasSave = true;
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();
179 ASSERT(m_canvasStateStack.size() > 0);
180 CanvasSaveState savedState = m_canvasStateStack.last();
181 m_canvasStateStack.removeLast();
182 m_pendingCanvasSave = savedState.m_pendingSave;
183 m_canvas->restoreToCount(savedState.m_restoreCount);
187 void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint)
189 if (contextDisabled())
196 m_canvas->saveLayer(bounds, paint);
197 if (regionTrackingEnabled())
198 m_trackedRegion.pushCanvasLayer(paint);
201 void GraphicsContext::restoreLayer()
203 if (contextDisabled())
209 if (regionTrackingEnabled())
210 m_trackedRegion.popCanvasLayer(this);
213 void GraphicsContext::beginAnnotation(const AnnotationList& annotations)
215 if (contextDisabled())
220 canvas()->beginCommentGroup("GraphicsContextAnnotation");
222 AnnotationList::const_iterator end = annotations.end();
223 for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++it)
224 canvas()->addComment(it->first, it->second.ascii().data());
231 void GraphicsContext::endAnnotation()
233 if (contextDisabled())
237 ASSERT(m_annotationCount > 0);
238 canvas()->endCommentGroup();
245 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
247 if (contextDisabled())
252 setStrokeColor(Color::black);
255 mutableState()->setStrokePattern(pattern);
258 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
260 if (contextDisabled())
265 setStrokeColor(Color::black);
268 mutableState()->setStrokeGradient(gradient);
271 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
273 if (contextDisabled())
278 setFillColor(Color::black);
282 mutableState()->setFillPattern(pattern);
285 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
287 if (contextDisabled())
292 setFillColor(Color::black);
296 mutableState()->setFillGradient(gradient);
299 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color,
300 DrawLooperBuilder::ShadowTransformMode shadowTransformMode,
301 DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode)
303 if (contextDisabled())
306 if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) {
311 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
312 drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode);
313 drawLooperBuilder->addUnmodifiedContent();
314 setDrawLooper(drawLooperBuilder.release());
317 void GraphicsContext::setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder)
319 if (contextDisabled())
322 mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper());
325 void GraphicsContext::clearDrawLooper()
327 if (contextDisabled())
330 mutableState()->clearDrawLooper();
333 bool GraphicsContext::hasShadow() const
335 return !!immutableState()->drawLooper();
338 bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const
340 if (contextDisabled())
344 if (!m_canvas->getClipDeviceBounds(&skIBounds))
346 SkRect skBounds = SkRect::Make(skIBounds);
347 *bounds = FloatRect(skBounds);
351 SkMatrix GraphicsContext::getTotalMatrix() const
353 if (contextDisabled() || !m_canvas)
354 return SkMatrix::I();
359 return m_canvas->getTotalMatrix();
361 const RecordingState& recordingState = m_recordingStateStack.last();
362 SkMatrix totalMatrix = recordingState.m_savedMatrix;
363 totalMatrix.preConcat(m_canvas->getTotalMatrix());
368 void GraphicsContext::adjustTextRenderMode(SkPaint* paint) const
370 if (contextDisabled())
373 if (!paint->isLCDRenderText())
376 paint->setLCDRenderText(couldUseLCDRenderedText());
379 bool GraphicsContext::couldUseLCDRenderedText() const
382 // Our layers only have a single alpha channel. This means that subpixel
383 // rendered text cannot be composited correctly when the layer is
384 // collapsed. Therefore, subpixel text is contextDisabled when we are drawing
386 if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque())
389 return shouldSmoothFonts();
392 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode)
394 if (contextDisabled())
396 mutableState()->setCompositeOperation(compositeOperation, blendMode);
399 SkColorFilter* GraphicsContext::colorFilter() const
401 return immutableState()->colorFilter();
404 void GraphicsContext::setColorFilter(ColorFilter colorFilter)
406 GraphicsContextState* stateToSet = mutableState();
408 // We only support one active color filter at the moment. If (when) this becomes a problem,
409 // we should switch to using color filter chains (Skia work in progress).
410 ASSERT(!stateToSet->colorFilter());
411 stateToSet->setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter));
414 bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y)
416 if (contextDisabled())
420 return m_canvas->readPixels(info, pixels, rowBytes, x, y);
423 void GraphicsContext::setMatrix(const SkMatrix& matrix)
425 if (contextDisabled())
431 m_canvas->setMatrix(matrix);
434 void GraphicsContext::concat(const SkMatrix& matrix)
436 if (contextDisabled())
439 if (matrix.isIdentity())
445 m_canvas->concat(matrix);
448 void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds)
450 beginLayer(opacity, immutableState()->compositeOperator(), bounds);
453 void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilter colorFilter, ImageFilter* imageFilter)
455 if (contextDisabled())
459 layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
460 layerPaint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode()));
461 layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).get());
462 layerPaint.setImageFilter(imageFilter);
465 SkRect skBounds = WebCoreFloatRectToSKRect(*bounds);
466 saveLayer(&skBounds, &layerPaint);
468 saveLayer(0, &layerPaint);
476 void GraphicsContext::endLayer()
478 if (contextDisabled())
483 ASSERT(m_layerCount > 0);
489 void GraphicsContext::beginRecording(const FloatRect& bounds, uint32_t recordFlags)
491 RefPtr<DisplayList> displayList = DisplayList::create(bounds);
493 SkCanvas* savedCanvas = m_canvas;
494 SkMatrix savedMatrix = getTotalMatrix();
495 SkPictureRecorder* recorder = 0;
497 if (!contextDisabled()) {
498 FloatRect bounds = displayList->bounds();
499 IntSize recordingSize = enclosingIntRect(bounds).size();
500 recorder = new SkPictureRecorder;
501 m_canvas = recorder->beginRecording(recordingSize.width(), recordingSize.height(), 0, recordFlags);
503 // We want the bounds offset mapped to (0, 0), such that the display list content
504 // is fully contained within the SkPictureRecord's bounds.
505 if (!toFloatSize(bounds.location()).isZero()) {
506 m_canvas->translate(-bounds.x(), -bounds.y());
507 // To avoid applying the offset repeatedly in getTotalMatrix(), we pre-apply it here.
508 savedMatrix.preTranslate(bounds.x(), bounds.y());
512 m_recordingStateStack.append(RecordingState(recorder, savedCanvas, savedMatrix, m_shouldSmoothFonts, displayList,
513 static_cast<RegionTrackingMode>(m_regionTrackingMode)));
515 // Disable region tracking during recording.
516 setRegionTrackingMode(RegionTrackingDisabled);
519 PassRefPtr<DisplayList> GraphicsContext::endRecording()
521 ASSERT(!m_recordingStateStack.isEmpty());
523 RecordingState recording = m_recordingStateStack.last();
524 if (!contextDisabled())
525 recording.m_displayList->setPicture(recording.m_recorder->endRecording());
527 m_canvas = recording.m_savedCanvas;
528 setRegionTrackingMode(recording.m_regionTrackingMode);
529 setShouldSmoothFonts(recording.m_savedShouldSmoothFonts);
530 delete recording.m_recorder;
531 m_recordingStateStack.removeLast();
533 return recording.m_displayList;
536 bool GraphicsContext::isRecording() const
538 return !m_recordingStateStack.isEmpty();
541 void GraphicsContext::drawDisplayList(DisplayList* displayList)
546 if (contextDisabled() || displayList->bounds().isEmpty())
549 bool performClip = !displayList->clip().isEmpty();
550 bool performTransform = !displayList->transform().isIdentity();
551 if (performClip || performTransform) {
553 if (performTransform)
554 concat(displayList->transform());
556 clipRect(displayList->clip());
561 const FloatPoint& location = displayList->bounds().location();
562 if (location.x() || location.y()) {
564 m.setTranslate(location.x(), location.y());
565 m_canvas->drawPicture(displayList->picture(), &m, 0);
567 m_canvas->drawPicture(displayList->picture());
570 if (regionTrackingEnabled()) {
571 // Since we don't track regions within display lists, conservatively
572 // mark the bounds as non-opaque.
574 paint.setXfermodeMode(SkXfermode::kClear_Mode);
575 m_trackedRegion.didDrawBounded(this, displayList->bounds(), paint);
578 if (performClip || performTransform)
582 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
584 if (contextDisabled())
591 setPathFromConvexPoints(&path, numPoints, points);
593 SkPaint paint(immutableState()->fillPaint());
594 paint.setAntiAlias(shouldAntialias);
595 drawPath(path, paint);
597 if (strokeStyle() != NoStroke)
598 drawPath(path, immutableState()->strokePaint());
601 float GraphicsContext::prepareFocusRingPaint(SkPaint& paint, const Color& color, int width) const
603 paint.setAntiAlias(true);
604 paint.setStyle(SkPaint::kStroke_Style);
605 paint.setColor(color.rgb());
606 paint.setStrokeWidth(focusRingWidth(width));
610 return (width - 1) * 0.5f;
616 void GraphicsContext::drawFocusRingPath(const SkPath& path, const Color& color, int width)
619 float cornerRadius = prepareFocusRingPaint(paint, color, width);
621 paint.setPathEffect(SkCornerPathEffect::Create(SkFloatToScalar(cornerRadius)))->unref();
624 drawPath(path, paint);
629 paint.setStrokeWidth(paint.getStrokeWidth() * 0.5f);
630 drawPath(path, paint);
634 void GraphicsContext::drawFocusRingRect(const SkRect& rect, const Color& color, int width)
637 float cornerRadius = prepareFocusRingPaint(paint, color, width);
640 rrect.setRectXY(rect, SkFloatToScalar(cornerRadius), SkFloatToScalar(cornerRadius));
643 drawRRect(rrect, paint);
648 paint.setStrokeWidth(paint.getStrokeWidth() * 0.5f);
649 drawRRect(rrect, paint);
653 void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int offset, const Color& color)
655 // FIXME: Implement support for offset.
656 if (contextDisabled())
659 drawFocusRingPath(focusRingPath.skPath(), color, width);
662 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
664 if (contextDisabled())
667 unsigned rectCount = rects.size();
671 SkRegion focusRingRegion;
672 const int outset = focusRingOutset(offset);
673 for (unsigned i = 0; i < rectCount; i++) {
674 SkIRect r = rects[i];
675 r.inset(-outset, -outset);
676 focusRingRegion.op(r, SkRegion::kUnion_Op);
679 if (focusRingRegion.isRect()) {
680 drawFocusRingRect(SkRect::MakeFromIRect(focusRingRegion.getBounds()), color, width);
683 if (focusRingRegion.getBoundaryPath(&path))
684 drawFocusRingPath(path, color, width);
688 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
690 IntRect bounds(holeRect);
692 bounds.inflate(shadowBlur);
694 if (shadowSpread < 0)
695 bounds.inflate(-shadowSpread);
697 IntRect offsetBounds = bounds;
698 offsetBounds.move(-shadowOffset);
699 return unionRect(bounds, offsetBounds);
702 void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges)
704 if (contextDisabled())
707 IntRect holeRect(rect.rect());
708 holeRect.inflate(-shadowSpread);
710 if (holeRect.isEmpty()) {
711 if (rect.isRounded())
712 fillRoundedRect(rect, shadowColor);
714 fillRect(rect.rect(), shadowColor);
718 if (clippedEdges & LeftEdge) {
719 holeRect.move(-std::max(shadowOffset.width(), 0) - shadowBlur, 0);
720 holeRect.setWidth(holeRect.width() + std::max(shadowOffset.width(), 0) + shadowBlur);
722 if (clippedEdges & TopEdge) {
723 holeRect.move(0, -std::max(shadowOffset.height(), 0) - shadowBlur);
724 holeRect.setHeight(holeRect.height() + std::max(shadowOffset.height(), 0) + shadowBlur);
726 if (clippedEdges & RightEdge)
727 holeRect.setWidth(holeRect.width() - std::min(shadowOffset.width(), 0) + shadowBlur);
728 if (clippedEdges & BottomEdge)
729 holeRect.setHeight(holeRect.height() - std::min(shadowOffset.height(), 0) + shadowBlur);
731 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
733 IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset);
734 RoundedRect roundedHole(holeRect, rect.radii());
737 if (rect.isRounded()) {
739 path.addRoundedRect(rect);
741 roundedHole.shrinkRadii(shadowSpread);
746 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
747 drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor,
748 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
749 setDrawLooper(drawLooperBuilder.release());
750 fillRectWithRoundedHole(outerRect, roundedHole, fillColor);
755 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
758 if (contextDisabled())
761 StrokeStyle penStyle = strokeStyle();
762 if (penStyle == NoStroke)
765 FloatPoint p1 = point1;
766 FloatPoint p2 = point2;
767 bool isVerticalLine = (p1.x() == p2.x());
768 int width = roundf(strokeThickness());
770 // We know these are vertical or horizontal lines, so the length will just
771 // be the sum of the displacement component vectors give or take 1 -
772 // probably worth the speed up of no square root, which also won't be exact.
773 FloatSize disp = p2 - p1;
774 int length = SkScalarRoundToInt(disp.width() + disp.height());
775 SkPaint paint(immutableState()->strokePaint(length));
777 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
778 // Do a rect fill of our endpoints. This ensures we always have the
779 // appearance of being a border. We then draw the actual dotted/dashed line.
781 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
782 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
784 if (isVerticalLine) {
785 r1.offset(-width / 2, 0);
786 r2.offset(-width / 2, -width);
788 r1.offset(0, -width / 2);
789 r2.offset(-width, -width / 2);
792 fillPaint.setColor(paint.getColor());
793 drawRect(r1, fillPaint);
794 drawRect(r2, fillPaint);
797 adjustLineToPixelBoundaries(p1, p2, width, penStyle);
798 SkPoint pts[2] = { p1.data(), p2.data() };
800 m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
802 if (regionTrackingEnabled())
803 m_trackedRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint);
806 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
808 if (contextDisabled())
811 // Use 2x resources for a device scale factor of 1.5 or above.
812 int deviceScaleFactor = m_deviceScaleFactor > 1.5f ? 2 : 1;
814 // Create the pattern we'll use to draw the underline.
815 int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0;
816 static SkBitmap* misspellBitmap1x[2] = { 0, 0 };
817 static SkBitmap* misspellBitmap2x[2] = { 0, 0 };
818 SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x;
819 if (!misspellBitmap[index]) {
821 // Match the artwork used by the Mac.
822 const int rowPixels = 4 * deviceScaleFactor;
823 const int colPixels = 3 * deviceScaleFactor;
825 if (!bitmap.tryAllocN32Pixels(rowPixels, colPixels))
828 bitmap.eraseARGB(0, 0, 0, 0);
829 const uint32_t transparentColor = 0x00000000;
831 if (deviceScaleFactor == 1) {
832 const uint32_t colors[2][6] = {
833 { 0x2a2a0600, 0x57571000, 0xa8a81b00, 0xbfbf1f00, 0x70701200, 0xe0e02400 },
834 { 0x2a0f0f0f, 0x571e1e1e, 0xa83d3d3d, 0xbf454545, 0x70282828, 0xe0515151 }
837 // Pattern: a b a a b a
840 for (int x = 0; x < colPixels; ++x) {
841 uint32_t* row = bitmap.getAddr32(0, x);
842 row[0] = colors[index][x * 2];
843 row[1] = colors[index][x * 2 + 1];
844 row[2] = colors[index][x * 2];
845 row[3] = transparentColor;
847 } else if (deviceScaleFactor == 2) {
848 const uint32_t colors[2][18] = {
849 { 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810,
850 0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 },
851 { 0x0a020202, 0x33141414, 0x55232323, 0x37161616, 0x6e2e2e2e, 0x6e2e2e2e, 0x76313131, 0x8d3a3a3a, 0x8d3a3a3a,
852 0x963e3e3e, 0xb34b4b4b, 0xb34b4b4b, 0x6d2d2d2d, 0xd95b5b5b, 0xd95b5b5b, 0x19090909, 0x7c343434, 0xce575757 }
855 // Pattern: a b c c b a
861 for (int x = 0; x < colPixels; ++x) {
862 uint32_t* row = bitmap.getAddr32(0, x);
863 row[0] = colors[index][x * 3];
864 row[1] = colors[index][x * 3 + 1];
865 row[2] = colors[index][x * 3 + 2];
866 row[3] = colors[index][x * 3 + 2];
867 row[4] = colors[index][x * 3 + 1];
868 row[5] = colors[index][x * 3];
869 row[6] = transparentColor;
870 row[7] = transparentColor;
873 ASSERT_NOT_REACHED();
875 misspellBitmap[index] = new SkBitmap(bitmap);
877 // We use a 2-pixel-high misspelling indicator because that seems to be
878 // what WebKit is designed for, and how much room there is in a typical
880 const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below.
881 const int colPixels = 2 * deviceScaleFactor;
883 if (!bitmap.tryAllocN32Pixels(rowPixels, colPixels))
886 bitmap.eraseARGB(0, 0, 0, 0);
887 if (deviceScaleFactor == 1)
888 draw1xMarker(&bitmap, index);
889 else if (deviceScaleFactor == 2)
890 draw2xMarker(&bitmap, index);
892 ASSERT_NOT_REACHED();
894 misspellBitmap[index] = new SkBitmap(bitmap);
899 SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
900 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
902 // Make sure to draw only complete dots.
903 int rowPixels = misspellBitmap[index]->width();
904 float widthMod = fmodf(width * deviceScaleFactor, rowPixels);
905 if (rowPixels - widthMod > deviceScaleFactor)
906 width -= widthMod / deviceScaleFactor;
908 SkScalar originX = WebCoreFloatToSkScalar(pt.x());
910 // Offset it vertically by 1 so that there's some space under the text.
911 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
912 originX *= deviceScaleFactor;
913 originY *= deviceScaleFactor;
916 SkMatrix localMatrix;
917 localMatrix.setTranslate(originX, originY);
918 RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(
919 *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
922 paint.setShader(shader.get());
925 rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
927 if (deviceScaleFactor == 2) {
931 drawRect(rect, paint);
932 if (deviceScaleFactor == 2)
936 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool printing)
938 if (contextDisabled())
945 switch (strokeStyle()) {
950 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
952 r.fLeft = WebCoreFloatToSkScalar(pt.x());
953 // Avoid anti-aliasing lines. Currently, these are always horizontal.
954 // Round to nearest pixel to match text and other content.
955 r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f));
956 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
957 r.fBottom = r.fTop + SkIntToScalar(thickness);
958 paint = immutableState()->fillPaint();
959 // Text lines are drawn using the stroke color.
960 paint.setColor(effectiveStrokeColor());
966 int y = floorf(pt.y() + std::max<float>(strokeThickness() / 2.0f, 0.5f));
967 drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y));
972 ASSERT_NOT_REACHED();
975 // Draws a filled rectangle with a stroked border.
976 void GraphicsContext::drawRect(const IntRect& rect)
978 if (contextDisabled())
981 ASSERT(!rect.isEmpty());
985 SkRect skRect = rect;
986 int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000;
987 if (fillcolorNotTransparent)
988 drawRect(skRect, immutableState()->fillPaint());
990 if (immutableState()->strokeData().style() != NoStroke
991 && immutableState()->strokeColor().alpha()) {
992 // Stroke a width: 1 inset border
993 SkPaint paint(immutableState()->fillPaint());
994 paint.setColor(effectiveStrokeColor());
995 paint.setStyle(SkPaint::kStroke_Style);
996 paint.setStrokeWidth(1);
998 skRect.inset(0.5f, 0.5f);
999 drawRect(skRect, paint);
1003 void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point)
1005 if (contextDisabled())
1008 font.drawText(this, runInfo, point);
1011 void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point)
1013 if (contextDisabled())
1016 font.drawEmphasisMarks(this, runInfo, mark, point);
1019 void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
1021 if (contextDisabled())
1024 // sub-run painting is not supported for Bidi text.
1025 const TextRun& run = runInfo.run;
1026 ASSERT((runInfo.from == 0) && (runInfo.to == run.length()));
1027 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
1028 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
1029 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
1031 // FIXME: This ownership should be reversed. We should pass BidiRunList
1032 // to BidiResolver in createBidiRunsForLine.
1033 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
1034 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
1035 if (!bidiRuns.runCount())
1038 FloatPoint currPoint = point;
1039 BidiCharacterRun* bidiRun = bidiRuns.firstRun();
1042 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
1043 bool isRTL = bidiRun->level() % 2;
1044 subrun.setDirection(isRTL ? RTL : LTR);
1045 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
1047 TextRunPaintInfo subrunInfo(subrun);
1048 subrunInfo.bounds = runInfo.bounds;
1049 width = font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction);
1052 bidiRun = bidiRun->next();
1054 currPoint.move(width, 0);
1057 bidiRuns.deleteRuns();
1060 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to)
1062 if (contextDisabled())
1065 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor);
1068 void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1072 drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
1075 void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1079 drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
1082 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1084 drawImage(image, dest, src, op, WebBlendModeNormal, shouldRespectImageOrientation);
1087 void GraphicsContext::drawImage(Image* image, const FloatRect& dest)
1091 drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size())));
1094 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation)
1096 if (contextDisabled() || !image)
1098 image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation);
1101 void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing)
1103 if (contextDisabled() || !image)
1105 image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing);
1108 void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect,
1109 const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op)
1111 if (contextDisabled() || !image)
1114 if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
1116 drawImage(image, dest, srcRect, op);
1120 image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op);
1123 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest,
1124 const FloatRect* src, CompositeOperator op, WebBlendMode blendMode)
1126 if (contextDisabled() || !image)
1129 image->draw(this, dest, src, op, blendMode);
1132 void GraphicsContext::drawPicture(PassRefPtr<SkPicture> picture, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode)
1135 if (contextDisabled() || !picture)
1138 SkMatrix ctm = m_canvas->getTotalMatrix();
1140 ctm.mapRect(&deviceDest, dest);
1141 SkRect sourceBounds = WebCoreFloatRectToSKRect(src);
1143 RefPtr<SkPictureImageFilter> pictureFilter = adoptRef(SkPictureImageFilter::Create(picture.get(), sourceBounds));
1144 SkMatrix layerScale;
1145 layerScale.setScale(deviceDest.width() / src.width(), deviceDest.height() / src.height());
1146 RefPtr<SkMatrixImageFilter> matrixFilter = adoptRef(SkMatrixImageFilter::Create(layerScale, SkPaint::kLow_FilterLevel, pictureFilter.get()));
1147 SkPaint picturePaint;
1148 picturePaint.setXfermodeMode(WebCoreCompositeToSkiaComposite(op, blendMode));
1149 picturePaint.setImageFilter(matrixFilter.get());
1150 SkRect layerBounds = SkRect::MakeWH(std::max(deviceDest.width(), sourceBounds.width()), std::max(deviceDest.height(), sourceBounds.height()));
1152 m_canvas->resetMatrix();
1153 m_canvas->translate(deviceDest.x(), deviceDest.y());
1154 m_canvas->saveLayer(&layerBounds, &picturePaint);
1155 m_canvas->restore();
1156 m_canvas->restore();
1159 void GraphicsContext::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y)
1162 if (contextDisabled())
1165 m_canvas->writePixels(info, pixels, rowBytes, x, y);
1167 if (regionTrackingEnabled()) {
1168 SkRect rect = SkRect::MakeXYWH(x, y, info.width(), info.height());
1171 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1172 if (kOpaque_SkAlphaType != info.alphaType())
1173 paint.setAlpha(0x80); // signal to m_trackedRegion that we are not fully opaque
1175 m_trackedRegion.didDrawRect(this, rect, paint, 0);
1176 // more efficient would be to call markRectAsOpaque or MarkRectAsNonOpaque directly,
1177 // rather than cons-ing up a paint with an xfermode and alpha
1181 void GraphicsContext::writePixels(const SkBitmap& bitmap, int x, int y)
1183 if (contextDisabled())
1186 if (!bitmap.getTexture()) {
1187 SkAutoLockPixels alp(bitmap);
1188 if (bitmap.getPixels())
1189 writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), x, y);
1193 void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint)
1196 if (contextDisabled())
1199 m_canvas->drawBitmap(bitmap, left, top, paint);
1201 if (regionTrackingEnabled()) {
1202 SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height());
1203 m_trackedRegion.didDrawRect(this, rect, *paint, &bitmap);
1207 void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
1208 const SkRect& dst, const SkPaint* paint)
1211 if (contextDisabled())
1214 SkCanvas::DrawBitmapRectFlags flags =
1215 immutableState()->shouldClampToSourceRect() ? SkCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag;
1217 m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags);
1219 if (regionTrackingEnabled())
1220 m_trackedRegion.didDrawRect(this, dst, *paint, &bitmap);
1223 void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint)
1226 if (contextDisabled())
1229 m_canvas->drawOval(oval, paint);
1231 if (regionTrackingEnabled())
1232 m_trackedRegion.didDrawBounded(this, oval, paint);
1235 void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint)
1238 if (contextDisabled())
1241 m_canvas->drawPath(path, paint);
1243 if (regionTrackingEnabled())
1244 m_trackedRegion.didDrawPath(this, path, paint);
1247 void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint)
1250 if (contextDisabled())
1253 m_canvas->drawRect(rect, paint);
1255 if (regionTrackingEnabled())
1256 m_trackedRegion.didDrawRect(this, rect, paint, 0);
1259 void GraphicsContext::drawRRect(const SkRRect& rrect, const SkPaint& paint)
1262 if (contextDisabled())
1265 m_canvas->drawRRect(rrect, paint);
1267 if (regionTrackingEnabled())
1268 m_trackedRegion.didDrawBounded(this, rrect.rect(), paint);
1271 void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap)
1273 if (contextDisabled())
1276 if (regionTrackingEnabled())
1277 m_trackedRegion.didDrawRect(this, rect, paint, bitmap);
1280 void GraphicsContext::drawPosText(const void* text, size_t byteLength,
1281 const SkPoint pos[], const SkRect& textRect, const SkPaint& paint)
1284 if (contextDisabled())
1287 m_canvas->drawPosText(text, byteLength, pos, paint);
1288 didDrawTextInRect(textRect);
1290 // FIXME: compute bounds for positioned text.
1291 if (regionTrackingEnabled())
1292 m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke);
1295 void GraphicsContext::drawPosTextH(const void* text, size_t byteLength,
1296 const SkScalar xpos[], SkScalar constY, const SkRect& textRect, const SkPaint& paint)
1299 if (contextDisabled())
1302 m_canvas->drawPosTextH(text, byteLength, xpos, constY, paint);
1303 didDrawTextInRect(textRect);
1305 // FIXME: compute bounds for positioned text.
1306 if (regionTrackingEnabled())
1307 m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke);
1310 void GraphicsContext::drawTextBlob(const SkTextBlob* blob, const SkPoint& origin, const SkPaint& paint)
1313 if (contextDisabled())
1316 m_canvas->drawTextBlob(blob, origin.x(), origin.y(), paint);
1318 SkRect bounds = blob->bounds();
1319 bounds.offset(origin);
1320 didDrawTextInRect(bounds);
1322 // FIXME: use bounds here if it helps performance.
1323 if (regionTrackingEnabled())
1324 m_trackedRegion.didDrawUnbounded(this, paint, RegionTracker::FillOrStroke);
1327 void GraphicsContext::fillPath(const Path& pathToFill)
1329 if (contextDisabled() || pathToFill.isEmpty())
1332 // Use const_cast and temporarily modify the fill type instead of copying the path.
1333 SkPath& path = const_cast<SkPath&>(pathToFill.skPath());
1334 SkPath::FillType previousFillType = path.getFillType();
1336 SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(immutableState()->fillRule());
1337 path.setFillType(temporaryFillType);
1339 drawPath(path, immutableState()->fillPaint());
1341 path.setFillType(previousFillType);
1344 void GraphicsContext::fillRect(const FloatRect& rect)
1346 if (contextDisabled())
1351 drawRect(r, immutableState()->fillPaint());
1354 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
1356 if (contextDisabled())
1360 SkPaint paint = immutableState()->fillPaint();
1361 paint.setColor(color.rgb());
1365 void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight,
1366 const IntRect& inner, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color& color)
1369 if (contextDisabled())
1372 SkVector outerRadii[4];
1373 SkVector innerRadii[4];
1374 setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft);
1375 setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft);
1379 rrOuter.setRectRadii(outer, outerRadii);
1380 rrInner.setRectRadii(inner, innerRadii);
1382 SkPaint paint(immutableState()->fillPaint());
1383 paint.setColor(color.rgb());
1385 m_canvas->drawDRRect(rrOuter, rrInner, paint);
1387 if (regionTrackingEnabled())
1388 m_trackedRegion.didDrawBounded(this, rrOuter.getBounds(), paint);
1391 void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color)
1393 fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(),
1394 inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color);
1397 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
1398 const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
1401 if (contextDisabled())
1404 if (topLeft.width() + topRight.width() > rect.width()
1405 || bottomLeft.width() + bottomRight.width() > rect.width()
1406 || topLeft.height() + bottomLeft.height() > rect.height()
1407 || topRight.height() + bottomRight.height() > rect.height()) {
1408 // Not all the radii fit, return a rect. This matches the behavior of
1409 // Path::createRoundedRectangle. Without this we attempt to draw a round
1410 // shadow for a square box.
1411 fillRect(rect, color);
1416 setRadii(radii, topLeft, topRight, bottomRight, bottomLeft);
1419 rr.setRectRadii(rect, radii);
1421 SkPaint paint(immutableState()->fillPaint());
1422 paint.setColor(color.rgb());
1424 m_canvas->drawRRect(rr, paint);
1426 if (regionTrackingEnabled())
1427 m_trackedRegion.didDrawBounded(this, rr.getBounds(), paint);
1430 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
1432 if (contextDisabled())
1435 SkRect rect = ellipse;
1436 drawOval(rect, immutableState()->fillPaint());
1439 void GraphicsContext::strokePath(const Path& pathToStroke)
1441 if (contextDisabled() || pathToStroke.isEmpty())
1444 const SkPath& path = pathToStroke.skPath();
1445 drawPath(path, immutableState()->strokePaint());
1448 void GraphicsContext::strokeRect(const FloatRect& rect)
1450 strokeRect(rect, strokeThickness());
1453 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1455 if (contextDisabled())
1458 SkPaint paint(immutableState()->strokePaint());
1459 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1460 // Reset the dash effect to account for the width
1461 immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0);
1462 // strokerect has special rules for CSS when the rect is degenerate:
1463 // if width==0 && height==0, do nothing
1464 // if width==0 || height==0, then just draw line for the other dimension
1466 bool validW = r.width() > 0;
1467 bool validH = r.height() > 0;
1468 if (validW && validH) {
1470 } else if (validW || validH) {
1471 // we are expected to respect the lineJoin, so we can't just call
1472 // drawLine -- we have to create a path that doubles back on itself.
1474 path.moveTo(r.fLeft, r.fTop);
1475 path.lineTo(r.fRight, r.fBottom);
1477 drawPath(path, paint);
1481 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
1483 if (contextDisabled())
1486 drawOval(ellipse, immutableState()->strokePaint());
1489 void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkRegion::Op regionOp)
1491 if (contextDisabled())
1494 if (!rect.isRounded()) {
1495 clipRect(rect.rect(), NotAntiAliased, regionOp);
1500 RoundedRect::Radii wkRadii = rect.radii();
1501 setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft());
1504 r.setRectRadii(rect.rect(), radii);
1506 clipRRect(r, AntiAliased, regionOp);
1509 void GraphicsContext::clipOut(const Path& pathToClip)
1511 if (contextDisabled())
1514 // Use const_cast and temporarily toggle the inverse fill type instead of copying the path.
1515 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1516 path.toggleInverseFillType();
1517 clipPath(path, AntiAliased);
1518 path.toggleInverseFillType();
1521 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
1523 if (contextDisabled() || pathToClip.isEmpty())
1526 // Use const_cast and temporarily modify the fill type instead of copying the path.
1527 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1528 SkPath::FillType previousFillType = path.getFillType();
1530 SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule);
1531 path.setFillType(temporaryFillType);
1532 clipPath(path, AntiAliased);
1534 path.setFillType(previousFillType);
1537 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
1539 if (contextDisabled())
1546 setPathFromConvexPoints(&path, numPoints, points);
1547 clipPath(path, antialiased ? AntiAliased : NotAntiAliased);
1550 void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
1552 if (contextDisabled())
1555 clipRoundedRect(rect, SkRegion::kDifference_Op);
1558 void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule)
1560 if (contextDisabled())
1563 // Use const_cast and temporarily modify the fill type instead of copying the path.
1564 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1565 SkPath::FillType previousFillType = path.getFillType();
1567 SkPath::FillType temporaryFillType = WebCoreWindRuleToSkFillType(clipRule);
1568 path.setFillType(temporaryFillType);
1571 path.setFillType(previousFillType);
1574 void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1577 if (contextDisabled())
1580 realizeCanvasSave();
1582 m_canvas->clipRect(rect, op, aa == AntiAliased);
1585 void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion::Op op)
1588 if (contextDisabled())
1591 realizeCanvasSave();
1593 m_canvas->clipPath(path, op, aa == AntiAliased);
1596 void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1599 if (contextDisabled())
1602 realizeCanvasSave();
1604 m_canvas->clipRRect(rect, op, aa == AntiAliased);
1607 void GraphicsContext::beginCull(const FloatRect& rect)
1610 if (contextDisabled())
1613 realizeCanvasSave();
1614 m_canvas->pushCull(rect);
1617 void GraphicsContext::endCull()
1620 if (contextDisabled())
1623 realizeCanvasSave();
1625 m_canvas->popCull();
1628 void GraphicsContext::rotate(float angleInRadians)
1631 if (contextDisabled())
1634 realizeCanvasSave();
1636 m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f)));
1639 void GraphicsContext::translate(float x, float y)
1642 if (contextDisabled())
1648 realizeCanvasSave();
1650 m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1653 void GraphicsContext::scale(float x, float y)
1656 if (contextDisabled())
1659 if (x == 1.0f && y == 1.0f)
1662 realizeCanvasSave();
1664 m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1667 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1670 if (contextDisabled())
1673 SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data()));
1674 SkAnnotateRectWithURL(m_canvas, destRect, url.get());
1677 void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRect& rect)
1680 if (contextDisabled())
1683 SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data()));
1684 SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get());
1687 void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& pos)
1690 if (contextDisabled())
1693 SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data()));
1694 SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameData);
1697 AffineTransform GraphicsContext::getCTM() const
1699 if (contextDisabled())
1700 return AffineTransform();
1702 SkMatrix m = getTotalMatrix();
1703 return AffineTransform(SkScalarToDouble(m.getScaleX()),
1704 SkScalarToDouble(m.getSkewY()),
1705 SkScalarToDouble(m.getSkewX()),
1706 SkScalarToDouble(m.getScaleY()),
1707 SkScalarToDouble(m.getTranslateX()),
1708 SkScalarToDouble(m.getTranslateY()));
1711 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op)
1713 if (contextDisabled())
1716 CompositeOperator previousOperator = compositeOperation();
1717 setCompositeOperation(op);
1718 fillRect(rect, color);
1719 setCompositeOperation(previousOperator);
1722 void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color)
1724 if (contextDisabled())
1727 if (rect.isRounded())
1728 fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color);
1730 fillRect(rect.rect(), color);
1733 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color)
1735 if (contextDisabled())
1741 if (!roundedHoleRect.radii().isZero())
1742 path.addRoundedRect(roundedHoleRect);
1744 path.addRect(roundedHoleRect.rect());
1746 WindRule oldFillRule = fillRule();
1747 Color oldFillColor = fillColor();
1749 setFillRule(RULE_EVENODD);
1750 setFillColor(color);
1754 setFillRule(oldFillRule);
1755 setFillColor(oldFillColor);
1758 void GraphicsContext::clearRect(const FloatRect& rect)
1760 if (contextDisabled())
1764 SkPaint paint(immutableState()->fillPaint());
1765 paint.setXfermodeMode(SkXfermode::kClear_Mode);
1769 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
1771 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
1772 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
1773 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
1774 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
1775 if (penStyle == DottedStroke || penStyle == DashedStroke) {
1776 if (p1.x() == p2.x()) {
1777 p1.setY(p1.y() + strokeWidth);
1778 p2.setY(p2.y() - strokeWidth);
1780 p1.setX(p1.x() + strokeWidth);
1781 p2.setX(p2.x() - strokeWidth);
1785 if (static_cast<int>(strokeWidth) % 2) { //odd
1786 if (p1.x() == p2.x()) {
1787 // We're a vertical line. Adjust our x.
1788 p1.setX(p1.x() + 0.5f);
1789 p2.setX(p2.x() + 0.5f);
1791 // We're a horizontal line. Adjust our y.
1792 p1.setY(p1.y() + 0.5f);
1793 p2.setY(p2.y() + 0.5f);
1798 PassOwnPtr<ImageBuffer> GraphicsContext::createRasterBuffer(const IntSize& size, OpacityMode opacityMode) const
1800 // Make the buffer larger if the context's transform is scaling it so we need a higher
1801 // resolution than one pixel per unit. Also set up a corresponding scale factor on the
1802 // graphics context.
1804 AffineTransform transform = getCTM();
1805 IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
1807 OwnPtr<ImageBufferSurface> surface = adoptPtr(new UnacceleratedImageBufferSurface(scaledSize, opacityMode));
1808 if (!surface->isValid())
1810 OwnPtr<ImageBuffer> buffer = adoptPtr(new ImageBuffer(surface.release()));
1812 buffer->context()->scale(static_cast<float>(scaledSize.width()) / size.width(),
1813 static_cast<float>(scaledSize.height()) / size.height());
1815 return buffer.release();
1818 void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points)
1820 path->incReserve(numPoints);
1821 path->moveTo(WebCoreFloatToSkScalar(points[0].x()),
1822 WebCoreFloatToSkScalar(points[0].y()));
1823 for (size_t i = 1; i < numPoints; ++i) {
1824 path->lineTo(WebCoreFloatToSkScalar(points[i].x()),
1825 WebCoreFloatToSkScalar(points[i].y()));
1828 /* The code used to just blindly call this
1829 path->setIsConvex(true);
1830 But webkit can sometimes send us non-convex 4-point values, so we mark the path's
1831 convexity as unknown, so it will get computed by skia at draw time.
1832 See crbug.com 108605
1834 SkPath::Convexity convexity = SkPath::kConvex_Convexity;
1836 convexity = SkPath::kUnknown_Convexity;
1837 path->setConvexity(convexity);
1840 void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft)
1842 radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()),
1843 SkIntToScalar(topLeft.height()));
1844 radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()),
1845 SkIntToScalar(topRight.height()));
1846 radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()),
1847 SkIntToScalar(bottomRight.height()));
1848 radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()),
1849 SkIntToScalar(bottomLeft.height()));
1852 PassRefPtr<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter)
1854 switch (colorFilter) {
1855 case ColorFilterLuminanceToAlpha:
1856 return adoptRef(SkLumaColorFilter::Create());
1857 case ColorFilterLinearRGBToSRGB:
1858 return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
1859 case ColorFilterSRGBToLinearRGB:
1860 return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
1861 case ColorFilterNone:
1864 ASSERT_NOT_REACHED();
1872 void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index)
1874 const SkPMColor lineColor = lineColors(index);
1875 const SkPMColor antiColor1 = antiColors1(index);
1876 const SkPMColor antiColor2 = antiColors2(index);
1878 uint32_t* row1 = bitmap->getAddr32(0, 0);
1879 uint32_t* row2 = bitmap->getAddr32(0, 1);
1880 uint32_t* row3 = bitmap->getAddr32(0, 2);
1881 uint32_t* row4 = bitmap->getAddr32(0, 3);
1883 // Pattern: X0o o0X0o o0
1887 const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 };
1888 const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor };
1889 const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 };
1890 const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 };
1892 for (int x = 0; x < bitmap->width() + 8; x += 8) {
1893 int count = std::min(bitmap->width() - x, 8);
1895 memcpy(row1 + x, row1Color, count * sizeof(SkPMColor));
1896 memcpy(row2 + x, row2Color, count * sizeof(SkPMColor));
1897 memcpy(row3 + x, row3Color, count * sizeof(SkPMColor));
1898 memcpy(row4 + x, row4Color, count * sizeof(SkPMColor));
1903 void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index)
1905 const uint32_t lineColor = lineColors(index);
1906 const uint32_t antiColor = antiColors2(index);
1908 // Pattern: X o o X o o X
1910 uint32_t* row1 = bitmap->getAddr32(0, 0);
1911 uint32_t* row2 = bitmap->getAddr32(0, 1);
1912 for (int x = 0; x < bitmap->width(); x++) {
1915 row1[x] = lineColor;
1918 row1[x] = antiColor;
1919 row2[x] = antiColor;
1922 row2[x] = lineColor;
1925 row1[x] = antiColor;
1926 row2[x] = antiColor;
1932 SkPMColor GraphicsContext::lineColors(int index)
1934 static const SkPMColor colors[] = {
1935 SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
1936 SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray.
1939 return colors[index];
1942 SkPMColor GraphicsContext::antiColors1(int index)
1944 static const SkPMColor colors[] = {
1945 SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red.
1946 SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray.
1949 return colors[index];
1952 SkPMColor GraphicsContext::antiColors2(int index)
1954 static const SkPMColor colors[] = {
1955 SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
1956 SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray
1959 return colors[index];
1963 void GraphicsContext::didDrawTextInRect(const SkRect& textRect)
1965 if (m_trackTextRegion) {
1966 TRACE_EVENT0("skia", "GraphicsContext::didDrawTextInRect");
1967 m_textRegion.join(textRect);
1971 void GraphicsContext::preparePaintForDrawRectToRect(
1973 const SkRect& srcRect,
1974 const SkRect& destRect,
1975 CompositeOperator compositeOp,
1976 WebBlendMode blendMode,
1978 bool isDataComplete) const
1980 paint->setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp, blendMode));
1981 paint->setColorFilter(this->colorFilter());
1982 paint->setAlpha(this->getNormalizedAlpha());
1983 paint->setLooper(this->drawLooper());
1984 paint->setAntiAlias(shouldDrawAntiAliased(this, destRect));
1986 InterpolationQuality resampling;
1987 if (this->isAccelerated()) {
1988 resampling = InterpolationLow;
1989 } else if (this->printing()) {
1990 resampling = InterpolationNone;
1991 } else if (isLazyDecoded) {
1992 resampling = InterpolationHigh;
1994 // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale).
1995 SkRect destRectTarget = destRect;
1996 SkMatrix totalMatrix = this->getTotalMatrix();
1997 if (!(totalMatrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)))
1998 totalMatrix.mapRect(&destRectTarget, destRect);
2000 resampling = computeInterpolationQuality(totalMatrix,
2001 SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()),
2002 SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height()),
2006 if (resampling == InterpolationNone) {
2007 // FIXME: This is to not break tests (it results in the filter bitmap flag
2008 // being set to true). We need to decide if we respect InterpolationNone
2009 // being returned from computeInterpolationQuality.
2010 resampling = InterpolationLow;
2012 resampling = limitInterpolationQuality(this, resampling);
2013 paint->setFilterLevel(static_cast<SkPaint::FilterLevel>(resampling));
2016 } // namespace blink