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/geometry/IntRect.h"
31 #include "platform/geometry/RoundedRect.h"
32 #include "platform/graphics/BitmapImage.h"
33 #include "platform/graphics/DisplayList.h"
34 #include "platform/graphics/Gradient.h"
35 #include "platform/graphics/ImageBuffer.h"
36 #include "platform/text/BidiResolver.h"
37 #include "platform/text/TextRunIterator.h"
38 #include "platform/weborigin/KURL.h"
39 #include "third_party/skia/include/core/SkAnnotation.h"
40 #include "third_party/skia/include/core/SkColorFilter.h"
41 #include "third_party/skia/include/core/SkData.h"
42 #include "third_party/skia/include/core/SkDevice.h"
43 #include "third_party/skia/include/core/SkPicture.h"
44 #include "third_party/skia/include/core/SkRRect.h"
45 #include "third_party/skia/include/core/SkRefCnt.h"
46 #include "third_party/skia/include/core/SkSurface.h"
47 #include "third_party/skia/include/effects/SkBlurMaskFilter.h"
48 #include "third_party/skia/include/effects/SkCornerPathEffect.h"
49 #include "third_party/skia/include/effects/SkLumaColorFilter.h"
50 #include "third_party/skia/include/gpu/GrRenderTarget.h"
51 #include "third_party/skia/include/gpu/GrTexture.h"
52 #include "wtf/Assertions.h"
53 #include "wtf/MathExtras.h"
56 #include <ApplicationServices/ApplicationServices.h>
60 using blink::WebBlendMode;
66 class CompatibleImageBufferSurface : public ImageBufferSurface {
67 WTF_MAKE_NONCOPYABLE(CompatibleImageBufferSurface); WTF_MAKE_FAST_ALLOCATED;
69 CompatibleImageBufferSurface(PassRefPtr<SkSurface> surface, const IntSize& size, OpacityMode opacityMode)
70 : ImageBufferSurface(size, opacityMode)
74 virtual ~CompatibleImageBufferSurface() { }
76 virtual SkCanvas* canvas() const OVERRIDE { return m_surface ? m_surface->getCanvas() : 0; }
77 virtual bool isValid() const OVERRIDE { return m_surface; }
78 virtual bool isAccelerated() const OVERRIDE { return isValid() && m_surface->getCanvas()->getTopDevice()->accessRenderTarget(); }
79 virtual Platform3DObject getBackingTexture() const OVERRIDE
81 ASSERT(isAccelerated());
82 GrRenderTarget* renderTarget = m_surface->getCanvas()->getTopDevice()->accessRenderTarget();
84 return renderTarget->asTexture()->getTextureHandle();
90 RefPtr<SkSurface> m_surface;
93 } // unnamed namespace
95 struct GraphicsContext::CanvasSaveState {
96 CanvasSaveState(bool pendingSave, int count)
97 : m_pendingSave(pendingSave), m_restoreCount(count) { }
103 struct GraphicsContext::RecordingState {
104 RecordingState(SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassRefPtr<DisplayList> displayList)
105 : m_savedCanvas(currentCanvas)
106 , m_displayList(displayList)
107 , m_savedMatrix(currentMatrix)
111 SkCanvas* m_savedCanvas;
112 RefPtr<DisplayList> m_displayList;
113 const SkMatrix m_savedMatrix;
116 GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting)
118 , m_paintStateStack()
119 , m_paintStateIndex(0)
120 , m_pendingCanvasSave(false)
121 , m_annotationMode(0)
123 , m_annotationCount(0)
126 , m_disabledState(disableContextOrPainting)
127 , m_trackOpaqueRegion(false)
128 , m_trackTextRegion(false)
129 , m_useHighResMarker(false)
130 , m_updatingControlTints(false)
131 , m_accelerated(false)
132 , m_isCertainlyOpaque(true)
134 , m_antialiasHairlineImages(false)
137 m_disabledState |= PaintingDisabled;
139 // FIXME: Do some tests to determine how many states are typically used, and allocate
141 m_paintStateStack.append(GraphicsContextState::create());
142 m_paintState = m_paintStateStack.last().get();
145 GraphicsContext::~GraphicsContext()
148 // FIXME: Oilpan: These asserts are not true for
149 // CanvasRenderingContext2D. Therefore, there is debug mode code
150 // in the CanvasRenderingContext2D that forces this to be true so
151 // that the assertions can be here for all the other cases. With
152 // Oilpan we cannot run that code in the CanvasRenderingContext2D
153 // destructor because it touches other objects that are already
154 // dead. We need to find another way of doing these asserts when
155 // Oilpan is enabled.
156 ASSERT(!m_paintStateIndex);
157 ASSERT(!m_paintState->saveCount());
158 ASSERT(!m_annotationCount);
159 ASSERT(!m_layerCount);
160 ASSERT(m_recordingStateStack.isEmpty());
164 void GraphicsContext::save()
166 if (contextDisabled())
169 m_paintState->incrementSaveCount();
171 m_canvasStateStack.append(CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount()));
172 m_pendingCanvasSave = true;
175 void GraphicsContext::restore()
177 if (contextDisabled())
180 if (!m_paintStateIndex && !m_paintState->saveCount()) {
181 WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
185 if (m_paintState->saveCount()) {
186 m_paintState->decrementSaveCount();
189 m_paintState = m_paintStateStack[m_paintStateIndex].get();
192 CanvasSaveState savedState = m_canvasStateStack.last();
193 m_canvasStateStack.removeLast();
194 m_pendingCanvasSave = savedState.m_pendingSave;
195 m_canvas->restoreToCount(savedState.m_restoreCount);
198 void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint)
200 if (contextDisabled())
205 m_canvas->saveLayer(bounds, paint);
206 if (m_trackOpaqueRegion)
207 m_opaqueRegion.pushCanvasLayer(paint);
210 void GraphicsContext::restoreLayer()
212 if (contextDisabled())
216 if (m_trackOpaqueRegion)
217 m_opaqueRegion.popCanvasLayer(this);
220 void GraphicsContext::beginAnnotation(const char* rendererName, const char* paintPhase,
221 const String& elementId, const String& elementClass, const String& elementTag)
223 if (contextDisabled())
226 canvas()->beginCommentGroup("GraphicsContextAnnotation");
228 GraphicsContextAnnotation annotation(rendererName, paintPhase, elementId, elementClass, elementTag);
229 AnnotationList annotations;
230 annotation.asAnnotationList(annotations);
232 AnnotationList::const_iterator end = annotations.end();
233 for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++it)
234 canvas()->addComment(it->first, it->second.ascii().data());
241 void GraphicsContext::endAnnotation()
243 if (contextDisabled())
246 canvas()->endCommentGroup();
248 ASSERT(m_annotationCount > 0);
254 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
256 if (contextDisabled())
261 setStrokeColor(Color::black);
264 mutableState()->setStrokePattern(pattern);
267 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
269 if (contextDisabled())
274 setStrokeColor(Color::black);
277 mutableState()->setStrokeGradient(gradient);
280 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
282 if (contextDisabled())
287 setFillColor(Color::black);
291 mutableState()->setFillPattern(pattern);
294 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
296 if (contextDisabled())
301 setFillColor(Color::black);
305 mutableState()->setFillGradient(gradient);
308 void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color,
309 DrawLooperBuilder::ShadowTransformMode shadowTransformMode,
310 DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode)
312 if (contextDisabled())
315 if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) {
320 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
321 drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode);
322 drawLooperBuilder->addUnmodifiedContent();
323 setDrawLooper(drawLooperBuilder.release());
326 void GraphicsContext::setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder)
328 if (contextDisabled())
331 mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper());
334 void GraphicsContext::clearDrawLooper()
336 if (contextDisabled())
339 mutableState()->clearDrawLooper();
342 bool GraphicsContext::hasShadow() const
344 return !!immutableState()->drawLooper();
347 bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const
349 if (contextDisabled())
352 if (!m_canvas->getClipDeviceBounds(&skIBounds))
354 SkRect skBounds = SkRect::Make(skIBounds);
355 *bounds = FloatRect(skBounds);
359 SkMatrix GraphicsContext::getTotalMatrix() const
361 if (contextDisabled())
362 return SkMatrix::I();
365 return m_canvas->getTotalMatrix();
367 const RecordingState& recordingState = m_recordingStateStack.last();
368 SkMatrix totalMatrix = recordingState.m_savedMatrix;
369 totalMatrix.preConcat(m_canvas->getTotalMatrix());
374 void GraphicsContext::adjustTextRenderMode(SkPaint* paint)
376 if (contextDisabled())
379 if (!paint->isLCDRenderText())
382 paint->setLCDRenderText(couldUseLCDRenderedText());
385 bool GraphicsContext::couldUseLCDRenderedText()
387 // Our layers only have a single alpha channel. This means that subpixel
388 // rendered text cannot be composited correctly when the layer is
389 // collapsed. Therefore, subpixel text is contextDisabled when we are drawing
391 if (contextDisabled() || isDrawingToLayer() || !isCertainlyOpaque())
394 return shouldSmoothFonts();
397 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode)
399 if (contextDisabled())
401 mutableState()->setCompositeOperation(compositeOperation, blendMode);
404 SkColorFilter* GraphicsContext::colorFilter()
406 return immutableState()->colorFilter();
409 void GraphicsContext::setColorFilter(ColorFilter colorFilter)
411 GraphicsContextState* stateToSet = mutableState();
413 // We only support one active color filter at the moment. If (when) this becomes a problem,
414 // we should switch to using color filter chains (Skia work in progress).
415 ASSERT(!stateToSet->colorFilter());
416 stateToSet->setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter));
419 bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y)
421 if (contextDisabled())
424 return m_canvas->readPixels(info, pixels, rowBytes, x, y);
427 void GraphicsContext::setMatrix(const SkMatrix& matrix)
429 if (contextDisabled())
434 m_canvas->setMatrix(matrix);
437 void GraphicsContext::concat(const SkMatrix& matrix)
439 if (contextDisabled())
442 if (matrix.isIdentity())
447 m_canvas->concat(matrix);
450 void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds)
452 beginLayer(opacity, immutableState()->compositeOperator(), bounds);
455 void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilter colorFilter, ImageFilter* imageFilter)
457 if (contextDisabled())
461 layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
462 layerPaint.setXfermode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode()).get());
463 layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).get());
464 layerPaint.setImageFilter(imageFilter);
467 SkRect skBounds = WebCoreFloatRectToSKRect(*bounds);
468 saveLayer(&skBounds, &layerPaint);
470 saveLayer(0, &layerPaint);
478 void GraphicsContext::endLayer()
480 if (contextDisabled())
485 ASSERT(m_layerCount > 0);
491 void GraphicsContext::beginRecording(const FloatRect& bounds)
493 RefPtr<DisplayList> displayList = adoptRef(new DisplayList(bounds));
495 SkCanvas* savedCanvas = m_canvas;
496 SkMatrix savedMatrix = getTotalMatrix();
498 if (!contextDisabled()) {
499 IntRect recordingRect = enclosingIntRect(bounds);
500 m_canvas = displayList->beginRecording(recordingRect.size(),
501 SkPicture::kUsePathBoundsForClip_RecordingFlag);
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(savedCanvas, savedMatrix, displayList));
515 PassRefPtr<DisplayList> GraphicsContext::endRecording()
517 ASSERT(!m_recordingStateStack.isEmpty());
519 RecordingState recording = m_recordingStateStack.last();
520 if (!contextDisabled()) {
521 ASSERT(recording.m_displayList->isRecording());
522 recording.m_displayList->endRecording();
525 m_recordingStateStack.removeLast();
526 m_canvas = recording.m_savedCanvas;
528 return recording.m_displayList.release();
531 bool GraphicsContext::isRecording() const
533 return !m_recordingStateStack.isEmpty();
536 void GraphicsContext::drawDisplayList(DisplayList* displayList)
539 ASSERT(!displayList->isRecording());
541 if (contextDisabled() || displayList->bounds().isEmpty())
546 const FloatRect& bounds = displayList->bounds();
547 if (bounds.x() || bounds.y())
548 m_canvas->translate(bounds.x(), bounds.y());
550 m_canvas->drawPicture(*displayList->picture());
552 if (bounds.x() || bounds.y())
553 m_canvas->translate(-bounds.x(), -bounds.y());
556 void GraphicsContext::setupPaintForFilling(SkPaint* paint) const
558 if (contextDisabled())
561 *paint = immutableState()->fillPaint();
564 void GraphicsContext::setupPaintForStroking(SkPaint* paint) const
566 if (contextDisabled())
569 *paint = immutableState()->strokePaint();
572 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
574 if (contextDisabled())
581 setPathFromConvexPoints(&path, numPoints, points);
583 SkPaint paint(immutableState()->fillPaint());
584 paint.setAntiAlias(shouldAntialias);
585 drawPath(path, paint);
587 if (strokeStyle() != NoStroke)
588 drawPath(path, immutableState()->strokePaint());
591 void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int offset, const Color& color)
593 // FIXME: Implement support for offset.
594 if (contextDisabled())
598 paint.setAntiAlias(true);
599 paint.setStyle(SkPaint::kStroke_Style);
600 paint.setColor(color.rgb());
602 drawOuterPath(focusRingPath.skPath(), paint, width);
603 drawInnerPath(focusRingPath.skPath(), paint, width);
606 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
608 if (contextDisabled())
611 unsigned rectCount = rects.size();
615 SkRegion focusRingRegion;
616 const int focusRingOutset = getFocusRingOutset(offset);
617 for (unsigned i = 0; i < rectCount; i++) {
618 SkIRect r = rects[i];
619 r.inset(-focusRingOutset, -focusRingOutset);
620 focusRingRegion.op(r, SkRegion::kUnion_Op);
625 paint.setAntiAlias(true);
626 paint.setStyle(SkPaint::kStroke_Style);
628 paint.setColor(color.rgb());
629 focusRingRegion.getBoundaryPath(&path);
630 drawOuterPath(path, paint, width);
631 drawInnerPath(path, paint, width);
634 static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
636 IntRect bounds(holeRect);
638 bounds.inflate(shadowBlur);
640 if (shadowSpread < 0)
641 bounds.inflate(-shadowSpread);
643 IntRect offsetBounds = bounds;
644 offsetBounds.move(-shadowOffset);
645 return unionRect(bounds, offsetBounds);
648 void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges)
650 if (contextDisabled())
653 IntRect holeRect(rect.rect());
654 holeRect.inflate(-shadowSpread);
656 if (holeRect.isEmpty()) {
657 if (rect.isRounded())
658 fillRoundedRect(rect, shadowColor);
660 fillRect(rect.rect(), shadowColor);
664 if (clippedEdges & LeftEdge) {
665 holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0);
666 holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur);
668 if (clippedEdges & TopEdge) {
669 holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur);
670 holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur);
672 if (clippedEdges & RightEdge)
673 holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur);
674 if (clippedEdges & BottomEdge)
675 holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur);
677 Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
679 IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset);
680 RoundedRect roundedHole(holeRect, rect.radii());
683 if (rect.isRounded()) {
685 path.addRoundedRect(rect);
687 roundedHole.shrinkRadii(shadowSpread);
692 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
693 drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor,
694 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
695 setDrawLooper(drawLooperBuilder.release());
696 fillRectWithRoundedHole(outerRect, roundedHole, fillColor);
701 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
703 if (contextDisabled())
706 StrokeStyle penStyle = strokeStyle();
707 if (penStyle == NoStroke)
710 FloatPoint p1 = point1;
711 FloatPoint p2 = point2;
712 bool isVerticalLine = (p1.x() == p2.x());
713 int width = roundf(strokeThickness());
715 // We know these are vertical or horizontal lines, so the length will just
716 // be the sum of the displacement component vectors give or take 1 -
717 // probably worth the speed up of no square root, which also won't be exact.
718 FloatSize disp = p2 - p1;
719 int length = SkScalarRoundToInt(disp.width() + disp.height());
720 SkPaint paint(immutableState()->strokePaint(length));
722 if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
723 // Do a rect fill of our endpoints. This ensures we always have the
724 // appearance of being a border. We then draw the actual dotted/dashed line.
726 r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
727 r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
729 if (isVerticalLine) {
730 r1.offset(-width / 2, 0);
731 r2.offset(-width / 2, -width);
733 r1.offset(0, -width / 2);
734 r2.offset(-width, -width / 2);
737 fillPaint.setColor(paint.getColor());
738 drawRect(r1, fillPaint);
739 drawRect(r2, fillPaint);
742 adjustLineToPixelBoundaries(p1, p2, width, penStyle);
743 SkPoint pts[2] = { p1.data(), p2.data() };
745 m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
747 if (m_trackOpaqueRegion)
748 m_opaqueRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint);
751 void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
753 if (contextDisabled())
756 int deviceScaleFactor = m_useHighResMarker ? 2 : 1;
758 // Create the pattern we'll use to draw the underline.
759 int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0;
760 static SkBitmap* misspellBitmap1x[2] = { 0, 0 };
761 static SkBitmap* misspellBitmap2x[2] = { 0, 0 };
762 SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x;
763 if (!misspellBitmap[index]) {
765 // Match the artwork used by the Mac.
766 const int rowPixels = 4 * deviceScaleFactor;
767 const int colPixels = 3 * deviceScaleFactor;
769 if (!bitmap.allocN32Pixels(rowPixels, colPixels))
772 bitmap.eraseARGB(0, 0, 0, 0);
773 const uint32_t transparentColor = 0x00000000;
775 if (deviceScaleFactor == 1) {
776 const uint32_t colors[2][6] = {
777 { 0x2a2a0600, 0x57571000, 0xa8a81b00, 0xbfbf1f00, 0x70701200, 0xe0e02400 },
778 { 0x2a0f0f0f, 0x571e1e1e, 0xa83d3d3d, 0xbf454545, 0x70282828, 0xe0515151 }
781 // Pattern: a b a a b a
784 for (int x = 0; x < colPixels; ++x) {
785 uint32_t* row = bitmap.getAddr32(0, x);
786 row[0] = colors[index][x * 2];
787 row[1] = colors[index][x * 2 + 1];
788 row[2] = colors[index][x * 2];
789 row[3] = transparentColor;
791 } else if (deviceScaleFactor == 2) {
792 const uint32_t colors[2][18] = {
793 { 0x0a090101, 0x33320806, 0x55540f0a, 0x37360906, 0x6e6c120c, 0x6e6c120c, 0x7674140d, 0x8d8b1810, 0x8d8b1810,
794 0x96941a11, 0xb3b01f15, 0xb3b01f15, 0x6d6b130c, 0xd9d62619, 0xd9d62619, 0x19180402, 0x7c7a150e, 0xcecb2418 },
795 { 0x0a020202, 0x33141414, 0x55232323, 0x37161616, 0x6e2e2e2e, 0x6e2e2e2e, 0x76313131, 0x8d3a3a3a, 0x8d3a3a3a,
796 0x963e3e3e, 0xb34b4b4b, 0xb34b4b4b, 0x6d2d2d2d, 0xd95b5b5b, 0xd95b5b5b, 0x19090909, 0x7c343434, 0xce575757 }
799 // Pattern: a b c c b a
805 for (int x = 0; x < colPixels; ++x) {
806 uint32_t* row = bitmap.getAddr32(0, x);
807 row[0] = colors[index][x * 3];
808 row[1] = colors[index][x * 3 + 1];
809 row[2] = colors[index][x * 3 + 2];
810 row[3] = colors[index][x * 3 + 2];
811 row[4] = colors[index][x * 3 + 1];
812 row[5] = colors[index][x * 3];
813 row[6] = transparentColor;
814 row[7] = transparentColor;
817 ASSERT_NOT_REACHED();
819 misspellBitmap[index] = new SkBitmap(bitmap);
821 // We use a 2-pixel-high misspelling indicator because that seems to be
822 // what WebKit is designed for, and how much room there is in a typical
824 const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below.
825 const int colPixels = 2 * deviceScaleFactor;
827 if (!bitmap.allocN32Pixels(rowPixels, colPixels))
830 bitmap.eraseARGB(0, 0, 0, 0);
831 if (deviceScaleFactor == 1)
832 draw1xMarker(&bitmap, index);
833 else if (deviceScaleFactor == 2)
834 draw2xMarker(&bitmap, index);
836 ASSERT_NOT_REACHED();
838 misspellBitmap[index] = new SkBitmap(bitmap);
843 SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
844 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
846 // Make sure to draw only complete dots.
847 int rowPixels = misspellBitmap[index]->width();
848 float widthMod = fmodf(width * deviceScaleFactor, rowPixels);
849 if (rowPixels - widthMod > deviceScaleFactor)
850 width -= widthMod / deviceScaleFactor;
852 SkScalar originX = WebCoreFloatToSkScalar(pt.x());
854 // Offset it vertically by 1 so that there's some space under the text.
855 SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
856 originX *= deviceScaleFactor;
857 originY *= deviceScaleFactor;
860 RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(
861 *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
863 matrix.setTranslate(originX, originY);
864 shader->setLocalMatrix(matrix);
867 paint.setShader(shader.get());
870 rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
872 if (deviceScaleFactor == 2) {
874 scale(FloatSize(0.5, 0.5));
876 drawRect(rect, paint);
877 if (deviceScaleFactor == 2)
881 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool printing)
883 if (contextDisabled())
890 switch (strokeStyle()) {
895 int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
897 r.fLeft = WebCoreFloatToSkScalar(pt.x());
898 // Avoid anti-aliasing lines. Currently, these are always horizontal.
899 // Round to nearest pixel to match text and other content.
900 r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f));
901 r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
902 r.fBottom = r.fTop + SkIntToScalar(thickness);
903 paint = immutableState()->fillPaint();
904 // Text lines are drawn using the stroke color.
905 paint.setColor(effectiveStrokeColor());
911 int y = floorf(pt.y() + std::max<float>(strokeThickness() / 2.0f, 0.5f));
912 drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y));
917 ASSERT_NOT_REACHED();
920 // Draws a filled rectangle with a stroked border.
921 void GraphicsContext::drawRect(const IntRect& rect)
923 if (contextDisabled())
926 ASSERT(!rect.isEmpty());
930 SkRect skRect = rect;
931 int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000;
932 if (fillcolorNotTransparent)
933 drawRect(skRect, immutableState()->fillPaint());
935 if (immutableState()->strokeData().style() != NoStroke
936 && immutableState()->strokeData().color().alpha()) {
937 // Stroke a width: 1 inset border
938 SkPaint paint(immutableState()->fillPaint());
939 paint.setColor(effectiveStrokeColor());
940 paint.setStyle(SkPaint::kStroke_Style);
941 paint.setStrokeWidth(1);
943 skRect.inset(0.5f, 0.5f);
944 drawRect(skRect, paint);
948 void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point)
950 if (contextDisabled())
953 font.drawText(this, runInfo, point);
956 void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point)
958 if (contextDisabled())
961 font.drawEmphasisMarks(this, runInfo, mark, point);
964 void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
966 if (contextDisabled())
969 // sub-run painting is not supported for Bidi text.
970 const TextRun& run = runInfo.run;
971 ASSERT((runInfo.from == 0) && (runInfo.to == run.length()));
972 BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
973 bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
974 bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
976 // FIXME: This ownership should be reversed. We should pass BidiRunList
977 // to BidiResolver in createBidiRunsForLine.
978 BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
979 bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
980 if (!bidiRuns.runCount())
983 FloatPoint currPoint = point;
984 BidiCharacterRun* bidiRun = bidiRuns.firstRun();
986 TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
987 bool isRTL = bidiRun->level() % 2;
988 subrun.setDirection(isRTL ? RTL : LTR);
989 subrun.setDirectionalOverride(bidiRun->dirOverride(false));
991 TextRunPaintInfo subrunInfo(subrun);
992 subrunInfo.bounds = runInfo.bounds;
993 font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction);
995 bidiRun = bidiRun->next();
996 // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
998 currPoint.move(font.width(subrun), 0);
1001 bidiRuns.deleteRuns();
1004 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to)
1006 if (contextDisabled())
1009 fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor);
1012 void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1016 drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
1019 void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1023 drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
1026 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1028 drawImage(image, dest, src, op, blink::WebBlendModeNormal, shouldRespectImageOrientation);
1031 void GraphicsContext::drawImage(Image* image, const FloatRect& dest)
1035 drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size())));
1038 void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation)
1040 if (contextDisabled() || !image)
1042 image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation);
1045 void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing)
1047 if (contextDisabled() || !image)
1049 image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing);
1052 void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect,
1053 const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op)
1055 if (contextDisabled() || !image)
1058 if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
1060 drawImage(image, dest, srcRect, op);
1064 image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op);
1067 void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest,
1068 const FloatRect* src, CompositeOperator op)
1070 if (contextDisabled() || !image)
1073 image->draw(this, dest, src, op);
1076 void GraphicsContext::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y)
1078 if (contextDisabled())
1081 m_canvas->writePixels(info, pixels, rowBytes, x, y);
1083 if (m_trackOpaqueRegion) {
1084 SkRect rect = SkRect::MakeXYWH(x, y, info.width(), info.height());
1087 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1088 if (kOpaque_SkAlphaType != info.alphaType())
1089 paint.setAlpha(0x80); // signal to m_opaqueRegion that we are not fully opaque
1091 m_opaqueRegion.didDrawRect(this, rect, paint, 0);
1092 // more efficient would be to call markRectAsOpaque or MarkRectAsNonOpaque directly,
1093 // rather than cons-ing up a paint with an xfermode and alpha
1097 void GraphicsContext::writePixels(const SkBitmap& bitmap, int x, int y)
1099 if (contextDisabled())
1102 if (!bitmap.getTexture()) {
1103 SkAutoLockPixels alp(bitmap);
1104 if (bitmap.getPixels())
1105 writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), x, y);
1109 void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint)
1111 if (contextDisabled())
1114 m_canvas->drawBitmap(bitmap, left, top, paint);
1116 if (m_trackOpaqueRegion) {
1117 SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height());
1118 m_opaqueRegion.didDrawRect(this, rect, *paint, &bitmap);
1122 void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
1123 const SkRect& dst, const SkPaint* paint)
1125 if (contextDisabled())
1128 SkCanvas::DrawBitmapRectFlags flags =
1129 immutableState()->shouldClampToSourceRect() ? SkCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag;
1131 m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags);
1133 if (m_trackOpaqueRegion)
1134 m_opaqueRegion.didDrawRect(this, dst, *paint, &bitmap);
1137 void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint)
1139 if (contextDisabled())
1142 m_canvas->drawOval(oval, paint);
1144 if (m_trackOpaqueRegion)
1145 m_opaqueRegion.didDrawBounded(this, oval, paint);
1148 void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint)
1150 if (contextDisabled())
1153 m_canvas->drawPath(path, paint);
1155 if (m_trackOpaqueRegion)
1156 m_opaqueRegion.didDrawPath(this, path, paint);
1159 void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint)
1161 if (contextDisabled())
1164 m_canvas->drawRect(rect, paint);
1166 if (m_trackOpaqueRegion)
1167 m_opaqueRegion.didDrawRect(this, rect, paint, 0);
1170 void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap)
1172 if (contextDisabled())
1175 if (m_trackOpaqueRegion)
1176 m_opaqueRegion.didDrawRect(this, rect, paint, bitmap);
1179 void GraphicsContext::drawPosText(const void* text, size_t byteLength,
1180 const SkPoint pos[], const SkRect& textRect, const SkPaint& paint)
1182 if (contextDisabled())
1185 m_canvas->drawPosText(text, byteLength, pos, paint);
1186 didDrawTextInRect(textRect);
1188 // FIXME: compute bounds for positioned text.
1189 if (m_trackOpaqueRegion)
1190 m_opaqueRegion.didDrawUnbounded(this, paint, OpaqueRegionSkia::FillOrStroke);
1193 void GraphicsContext::fillPath(const Path& pathToFill)
1195 if (contextDisabled() || pathToFill.isEmpty())
1198 // Use const_cast and temporarily modify the fill type instead of copying the path.
1199 SkPath& path = const_cast<SkPath&>(pathToFill.skPath());
1200 SkPath::FillType previousFillType = path.getFillType();
1202 SkPath::FillType temporaryFillType =
1203 immutableState()->fillRule() == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1204 path.setFillType(temporaryFillType);
1206 drawPath(path, immutableState()->fillPaint());
1208 path.setFillType(previousFillType);
1211 void GraphicsContext::fillRect(const FloatRect& rect)
1213 if (contextDisabled())
1218 drawRect(r, immutableState()->fillPaint());
1221 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
1223 if (contextDisabled())
1227 SkPaint paint = immutableState()->fillPaint();
1228 paint.setColor(color.rgb());
1232 void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight,
1233 const IntRect& inner, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color& color) {
1234 if (contextDisabled())
1237 SkVector outerRadii[4];
1238 SkVector innerRadii[4];
1239 setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft);
1240 setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft);
1244 rrOuter.setRectRadii(outer, outerRadii);
1245 rrInner.setRectRadii(inner, innerRadii);
1247 SkPaint paint(immutableState()->fillPaint());
1248 paint.setColor(color.rgb());
1250 m_canvas->drawDRRect(rrOuter, rrInner, paint);
1252 if (m_trackOpaqueRegion)
1253 m_opaqueRegion.didDrawBounded(this, rrOuter.getBounds(), paint);
1256 void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color)
1258 fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(),
1259 inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color);
1262 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
1263 const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
1265 if (contextDisabled())
1268 if (topLeft.width() + topRight.width() > rect.width()
1269 || bottomLeft.width() + bottomRight.width() > rect.width()
1270 || topLeft.height() + bottomLeft.height() > rect.height()
1271 || topRight.height() + bottomRight.height() > rect.height()) {
1272 // Not all the radii fit, return a rect. This matches the behavior of
1273 // Path::createRoundedRectangle. Without this we attempt to draw a round
1274 // shadow for a square box.
1275 fillRect(rect, color);
1280 setRadii(radii, topLeft, topRight, bottomRight, bottomLeft);
1283 rr.setRectRadii(rect, radii);
1285 SkPaint paint(immutableState()->fillPaint());
1286 paint.setColor(color.rgb());
1288 m_canvas->drawRRect(rr, paint);
1290 if (m_trackOpaqueRegion)
1291 m_opaqueRegion.didDrawBounded(this, rr.getBounds(), paint);
1294 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
1296 if (contextDisabled())
1299 SkRect rect = ellipse;
1300 drawOval(rect, immutableState()->fillPaint());
1303 void GraphicsContext::strokePath(const Path& pathToStroke)
1305 if (contextDisabled() || pathToStroke.isEmpty())
1308 const SkPath& path = pathToStroke.skPath();
1309 drawPath(path, immutableState()->strokePaint());
1312 void GraphicsContext::strokeRect(const FloatRect& rect)
1314 strokeRect(rect, strokeThickness());
1317 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1319 if (contextDisabled())
1322 SkPaint paint(immutableState()->strokePaint());
1323 paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1324 // Reset the dash effect to account for the width
1325 immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0);
1326 // strokerect has special rules for CSS when the rect is degenerate:
1327 // if width==0 && height==0, do nothing
1328 // if width==0 || height==0, then just draw line for the other dimension
1330 bool validW = r.width() > 0;
1331 bool validH = r.height() > 0;
1332 if (validW && validH) {
1334 } else if (validW || validH) {
1335 // we are expected to respect the lineJoin, so we can't just call
1336 // drawLine -- we have to create a path that doubles back on itself.
1338 path.moveTo(r.fLeft, r.fTop);
1339 path.lineTo(r.fRight, r.fBottom);
1341 drawPath(path, paint);
1345 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
1347 if (contextDisabled())
1350 drawOval(ellipse, immutableState()->strokePaint());
1353 void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkRegion::Op regionOp)
1355 if (contextDisabled())
1358 if (!rect.isRounded()) {
1359 clipRect(rect.rect(), NotAntiAliased, regionOp);
1364 RoundedRect::Radii wkRadii = rect.radii();
1365 setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft());
1368 r.setRectRadii(rect.rect(), radii);
1370 clipRRect(r, AntiAliased, regionOp);
1373 void GraphicsContext::clipOut(const Path& pathToClip)
1375 if (contextDisabled())
1378 // Use const_cast and temporarily toggle the inverse fill type instead of copying the path.
1379 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1380 path.toggleInverseFillType();
1381 clipPath(path, AntiAliased);
1382 path.toggleInverseFillType();
1385 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
1387 if (contextDisabled() || pathToClip.isEmpty())
1390 // Use const_cast and temporarily modify the fill type instead of copying the path.
1391 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1392 SkPath::FillType previousFillType = path.getFillType();
1394 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1395 path.setFillType(temporaryFillType);
1396 clipPath(path, AntiAliased);
1398 path.setFillType(previousFillType);
1401 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
1403 if (contextDisabled())
1410 setPathFromConvexPoints(&path, numPoints, points);
1411 clipPath(path, antialiased ? AntiAliased : NotAntiAliased);
1414 void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
1416 if (contextDisabled())
1419 clipRoundedRect(rect, SkRegion::kDifference_Op);
1422 void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule)
1424 if (contextDisabled())
1427 // Use const_cast and temporarily modify the fill type instead of copying the path.
1428 SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1429 SkPath::FillType previousFillType = path.getFillType();
1431 SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1432 path.setFillType(temporaryFillType);
1435 path.setFillType(previousFillType);
1438 void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1440 if (contextDisabled())
1443 realizeCanvasSave();
1445 m_canvas->clipRect(rect, op, aa == AntiAliased);
1448 void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion::Op op)
1450 if (contextDisabled())
1453 realizeCanvasSave();
1455 m_canvas->clipPath(path, op, aa == AntiAliased);
1458 void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1460 if (contextDisabled())
1463 realizeCanvasSave();
1465 m_canvas->clipRRect(rect, op, aa == AntiAliased);
1468 void GraphicsContext::beginCull(const FloatRect& rect)
1470 if (contextDisabled())
1473 realizeCanvasSave();
1474 m_canvas->pushCull(rect);
1477 void GraphicsContext::endCull()
1479 if (contextDisabled())
1482 realizeCanvasSave();
1484 m_canvas->popCull();
1487 void GraphicsContext::rotate(float angleInRadians)
1489 if (contextDisabled())
1492 realizeCanvasSave();
1494 m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f)));
1497 void GraphicsContext::translate(float w, float h)
1499 if (contextDisabled())
1505 realizeCanvasSave();
1507 m_canvas->translate(WebCoreFloatToSkScalar(w), WebCoreFloatToSkScalar(h));
1510 void GraphicsContext::scale(const FloatSize& size)
1512 if (contextDisabled())
1515 if (size.width() == 1.0f && size.height() == 1.0f)
1518 realizeCanvasSave();
1520 m_canvas->scale(WebCoreFloatToSkScalar(size.width()), WebCoreFloatToSkScalar(size.height()));
1523 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1525 if (contextDisabled())
1528 SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data()));
1529 SkAnnotateRectWithURL(m_canvas, destRect, url.get());
1532 void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRect& rect)
1534 if (contextDisabled())
1537 SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data()));
1538 SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get());
1541 void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& pos)
1543 if (contextDisabled())
1546 SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data()));
1547 SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameData);
1550 AffineTransform GraphicsContext::getCTM(IncludeDeviceScale) const
1552 if (contextDisabled())
1553 return AffineTransform();
1555 SkMatrix m = getTotalMatrix();
1556 return AffineTransform(SkScalarToDouble(m.getScaleX()),
1557 SkScalarToDouble(m.getSkewY()),
1558 SkScalarToDouble(m.getSkewX()),
1559 SkScalarToDouble(m.getScaleY()),
1560 SkScalarToDouble(m.getTranslateX()),
1561 SkScalarToDouble(m.getTranslateY()));
1564 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op)
1566 if (contextDisabled())
1569 CompositeOperator previousOperator = compositeOperation();
1570 setCompositeOperation(op);
1571 fillRect(rect, color);
1572 setCompositeOperation(previousOperator);
1575 void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color)
1577 if (contextDisabled())
1580 if (rect.isRounded())
1581 fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color);
1583 fillRect(rect.rect(), color);
1586 void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color)
1588 if (contextDisabled())
1594 if (!roundedHoleRect.radii().isZero())
1595 path.addRoundedRect(roundedHoleRect);
1597 path.addRect(roundedHoleRect.rect());
1599 WindRule oldFillRule = fillRule();
1600 Color oldFillColor = fillColor();
1602 setFillRule(RULE_EVENODD);
1603 setFillColor(color);
1607 setFillRule(oldFillRule);
1608 setFillColor(oldFillColor);
1611 void GraphicsContext::clearRect(const FloatRect& rect)
1613 if (contextDisabled())
1617 SkPaint paint(immutableState()->fillPaint());
1618 paint.setXfermodeMode(SkXfermode::kClear_Mode);
1622 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
1624 // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
1625 // works out. For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
1626 // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
1627 // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
1628 if (penStyle == DottedStroke || penStyle == DashedStroke) {
1629 if (p1.x() == p2.x()) {
1630 p1.setY(p1.y() + strokeWidth);
1631 p2.setY(p2.y() - strokeWidth);
1633 p1.setX(p1.x() + strokeWidth);
1634 p2.setX(p2.x() - strokeWidth);
1638 if (static_cast<int>(strokeWidth) % 2) { //odd
1639 if (p1.x() == p2.x()) {
1640 // We're a vertical line. Adjust our x.
1641 p1.setX(p1.x() + 0.5f);
1642 p2.setX(p2.x() + 0.5f);
1644 // We're a horizontal line. Adjust our y.
1645 p1.setY(p1.y() + 0.5f);
1646 p2.setY(p2.y() + 0.5f);
1651 PassOwnPtr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& size, OpacityMode opacityMode) const
1653 // Make the buffer larger if the context's transform is scaling it so we need a higher
1654 // resolution than one pixel per unit. Also set up a corresponding scale factor on the
1655 // graphics context.
1657 AffineTransform transform = getCTM(DefinitelyIncludeDeviceScale);
1658 IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
1660 SkAlphaType alphaType = (opacityMode == Opaque) ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1661 SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), alphaType);
1662 RefPtr<SkSurface> skSurface = adoptRef(m_canvas->newSurface(info));
1665 OwnPtr<ImageBufferSurface> surface = adoptPtr(new CompatibleImageBufferSurface(skSurface.release(), scaledSize, opacityMode));
1666 ASSERT(surface->isValid());
1667 OwnPtr<ImageBuffer> buffer = adoptPtr(new ImageBuffer(surface.release()));
1669 buffer->context()->scale(FloatSize(static_cast<float>(scaledSize.width()) / size.width(),
1670 static_cast<float>(scaledSize.height()) / size.height()));
1672 return buffer.release();
1675 void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points)
1677 path->incReserve(numPoints);
1678 path->moveTo(WebCoreFloatToSkScalar(points[0].x()),
1679 WebCoreFloatToSkScalar(points[0].y()));
1680 for (size_t i = 1; i < numPoints; ++i) {
1681 path->lineTo(WebCoreFloatToSkScalar(points[i].x()),
1682 WebCoreFloatToSkScalar(points[i].y()));
1685 /* The code used to just blindly call this
1686 path->setIsConvex(true);
1687 But webkit can sometimes send us non-convex 4-point values, so we mark the path's
1688 convexity as unknown, so it will get computed by skia at draw time.
1689 See crbug.com 108605
1691 SkPath::Convexity convexity = SkPath::kConvex_Convexity;
1693 convexity = SkPath::kUnknown_Convexity;
1694 path->setConvexity(convexity);
1697 void GraphicsContext::drawOuterPath(const SkPath& path, SkPaint& paint, int width)
1701 paint.setStrokeWidth(width);
1702 paint.setPathEffect(new SkCornerPathEffect((width - 1) * 0.5f))->unref();
1704 paint.setStrokeWidth(1);
1705 paint.setPathEffect(SkCornerPathEffect::Create(1))->unref();
1707 drawPath(path, paint);
1710 void GraphicsContext::drawInnerPath(const SkPath& path, SkPaint& paint, int width)
1713 paint.setAlpha(128);
1714 paint.setStrokeWidth(width * 0.5f);
1715 drawPath(path, paint);
1719 void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft)
1721 radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()),
1722 SkIntToScalar(topLeft.height()));
1723 radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()),
1724 SkIntToScalar(topRight.height()));
1725 radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()),
1726 SkIntToScalar(bottomRight.height()));
1727 radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()),
1728 SkIntToScalar(bottomLeft.height()));
1731 PassRefPtr<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter)
1733 switch (colorFilter) {
1734 case ColorFilterLuminanceToAlpha:
1735 return adoptRef(SkLumaColorFilter::Create());
1736 case ColorFilterLinearRGBToSRGB:
1737 return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
1738 case ColorFilterSRGBToLinearRGB:
1739 return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
1740 case ColorFilterNone:
1743 ASSERT_NOT_REACHED();
1751 CGColorSpaceRef PLATFORM_EXPORT deviceRGBColorSpaceRef()
1753 static CGColorSpaceRef deviceSpace = CGColorSpaceCreateDeviceRGB();
1757 void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index)
1759 const SkPMColor lineColor = lineColors(index);
1760 const SkPMColor antiColor1 = antiColors1(index);
1761 const SkPMColor antiColor2 = antiColors2(index);
1763 uint32_t* row1 = bitmap->getAddr32(0, 0);
1764 uint32_t* row2 = bitmap->getAddr32(0, 1);
1765 uint32_t* row3 = bitmap->getAddr32(0, 2);
1766 uint32_t* row4 = bitmap->getAddr32(0, 3);
1768 // Pattern: X0o o0X0o o0
1772 const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0, 0, 0, antiColor2, antiColor1 };
1773 const SkPMColor row2Color[] = { lineColor, lineColor, antiColor1, antiColor2, 0, antiColor2, antiColor1, lineColor };
1774 const SkPMColor row3Color[] = { 0, antiColor2, antiColor1, lineColor, lineColor, lineColor, antiColor1, antiColor2 };
1775 const SkPMColor row4Color[] = { 0, 0, antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 };
1777 for (int x = 0; x < bitmap->width() + 8; x += 8) {
1778 int count = std::min(bitmap->width() - x, 8);
1780 memcpy(row1 + x, row1Color, count * sizeof(SkPMColor));
1781 memcpy(row2 + x, row2Color, count * sizeof(SkPMColor));
1782 memcpy(row3 + x, row3Color, count * sizeof(SkPMColor));
1783 memcpy(row4 + x, row4Color, count * sizeof(SkPMColor));
1788 void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index)
1790 const uint32_t lineColor = lineColors(index);
1791 const uint32_t antiColor = antiColors2(index);
1793 // Pattern: X o o X o o X
1795 uint32_t* row1 = bitmap->getAddr32(0, 0);
1796 uint32_t* row2 = bitmap->getAddr32(0, 1);
1797 for (int x = 0; x < bitmap->width(); x++) {
1800 row1[x] = lineColor;
1803 row1[x] = antiColor;
1804 row2[x] = antiColor;
1807 row2[x] = lineColor;
1810 row1[x] = antiColor;
1811 row2[x] = antiColor;
1817 const SkPMColor GraphicsContext::lineColors(int index)
1819 static const SkPMColor colors[] = {
1820 SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
1821 SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray.
1824 return colors[index];
1827 const SkPMColor GraphicsContext::antiColors1(int index)
1829 static const SkPMColor colors[] = {
1830 SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red.
1831 SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0) // Semitransparent gray.
1834 return colors[index];
1837 const SkPMColor GraphicsContext::antiColors2(int index)
1839 static const SkPMColor colors[] = {
1840 SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
1841 SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0) // More transparent gray
1844 return colors[index];
1848 void GraphicsContext::didDrawTextInRect(const SkRect& textRect)
1850 if (m_trackTextRegion) {
1851 TRACE_EVENT0("skia", "PlatformContextSkia::trackTextRegion");
1852 m_textRegion.join(textRect);