1/*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2013 Google Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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.
25 */
26
27#include "config.h"
28#include "platform/graphics/GraphicsContext.h"
29
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/text/BidiResolver.h"
38#include "platform/text/TextRunIterator.h"
39#include "platform/weborigin/KURL.h"
40#include "third_party/skia/include/core/SkAnnotation.h"
41#include "third_party/skia/include/core/SkColorFilter.h"
42#include "third_party/skia/include/core/SkData.h"
43#include "third_party/skia/include/core/SkDevice.h"
44#include "third_party/skia/include/core/SkPicture.h"
45#include "third_party/skia/include/core/SkRRect.h"
46#include "third_party/skia/include/core/SkRefCnt.h"
47#include "third_party/skia/include/core/SkSurface.h"
48#include "third_party/skia/include/effects/SkBlurMaskFilter.h"
49#include "third_party/skia/include/effects/SkCornerPathEffect.h"
50#include "third_party/skia/include/effects/SkLumaColorFilter.h"
51#include "third_party/skia/include/gpu/GrRenderTarget.h"
52#include "third_party/skia/include/gpu/GrTexture.h"
53#include "wtf/Assertions.h"
54#include "wtf/MathExtras.h"
55
56using namespace std;
57using blink::WebBlendMode;
58
59namespace WebCore {
60
61namespace {
62
63class CompatibleImageBufferSurface : public ImageBufferSurface {
64    WTF_MAKE_NONCOPYABLE(CompatibleImageBufferSurface); WTF_MAKE_FAST_ALLOCATED;
65public:
66    CompatibleImageBufferSurface(PassRefPtr<SkSurface> surface, const IntSize& size, OpacityMode opacityMode)
67        : ImageBufferSurface(size, opacityMode)
68        , m_surface(surface)
69    {
70    }
71    virtual ~CompatibleImageBufferSurface() { }
72
73    virtual SkCanvas* canvas() const OVERRIDE { return m_surface ? m_surface->getCanvas() : 0; }
74    virtual bool isValid() const OVERRIDE { return m_surface; }
75    virtual bool isAccelerated() const OVERRIDE { return isValid() && m_surface->getCanvas()->getTopDevice()->accessRenderTarget(); }
76    virtual Platform3DObject getBackingTexture() const OVERRIDE
77    {
78        ASSERT(isAccelerated());
79        GrRenderTarget* renderTarget = m_surface->getCanvas()->getTopDevice()->accessRenderTarget();
80        if (renderTarget) {
81            return renderTarget->asTexture()->getTextureHandle();
82        }
83        return 0;
84    };
85
86private:
87    RefPtr<SkSurface> m_surface;
88};
89
90} // unnamed namespace
91
92struct GraphicsContext::CanvasSaveState {
93    CanvasSaveState(bool pendingSave, int count)
94        : m_pendingSave(pendingSave), m_restoreCount(count) { }
95
96    bool m_pendingSave;
97    int m_restoreCount;
98};
99
100struct GraphicsContext::RecordingState {
101    RecordingState(SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassRefPtr<DisplayList> displayList)
102        : m_savedCanvas(currentCanvas)
103        , m_displayList(displayList)
104        , m_savedMatrix(currentMatrix)
105    {
106    }
107
108    SkCanvas* m_savedCanvas;
109    RefPtr<DisplayList> m_displayList;
110    const SkMatrix m_savedMatrix;
111};
112
113GraphicsContext::GraphicsContext(SkCanvas* canvas, DisabledMode disableContextOrPainting)
114    : m_canvas(canvas)
115    , m_paintStateStack()
116    , m_paintStateIndex(0)
117    , m_pendingCanvasSave(false)
118    , m_annotationMode(0)
119#if ASSERT_ENABLED
120    , m_annotationCount(0)
121    , m_layerCount(0)
122    , m_disableDestructionChecks(false)
123#endif
124    , m_disabledState(disableContextOrPainting)
125    , m_trackOpaqueRegion(false)
126    , m_trackTextRegion(false)
127    , m_useHighResMarker(false)
128    , m_updatingControlTints(false)
129    , m_accelerated(false)
130    , m_isCertainlyOpaque(true)
131    , m_printing(false)
132    , m_antialiasHairlineImages(false)
133{
134    if (!canvas)
135        m_disabledState |= PaintingDisabled;
136
137    // FIXME: Do some tests to determine how many states are typically used, and allocate
138    // several here.
139    m_paintStateStack.append(GraphicsContextState::create());
140    m_paintState = m_paintStateStack.last().get();
141}
142
143GraphicsContext::~GraphicsContext()
144{
145#if ASSERT_ENABLED
146    if (!m_disableDestructionChecks) {
147        ASSERT(!m_paintStateIndex);
148        ASSERT(!m_paintState->saveCount());
149        ASSERT(!m_annotationCount);
150        ASSERT(!m_layerCount);
151        ASSERT(m_recordingStateStack.isEmpty());
152        ASSERT(m_canvasStateStack.isEmpty());
153    }
154#endif
155}
156
157void GraphicsContext::save()
158{
159    if (contextDisabled())
160        return;
161
162    m_paintState->incrementSaveCount();
163
164    m_canvasStateStack.append(CanvasSaveState(m_pendingCanvasSave, m_canvas->getSaveCount()));
165    m_pendingCanvasSave = true;
166}
167
168void GraphicsContext::restore()
169{
170    if (contextDisabled())
171        return;
172
173    if (!m_paintStateIndex && !m_paintState->saveCount()) {
174        WTF_LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
175        return;
176    }
177
178    if (m_paintState->saveCount()) {
179        m_paintState->decrementSaveCount();
180    } else {
181        m_paintStateIndex--;
182        m_paintState = m_paintStateStack[m_paintStateIndex].get();
183    }
184
185    CanvasSaveState savedState = m_canvasStateStack.last();
186    m_canvasStateStack.removeLast();
187    m_pendingCanvasSave = savedState.m_pendingSave;
188    m_canvas->restoreToCount(savedState.m_restoreCount);
189}
190
191void GraphicsContext::saveLayer(const SkRect* bounds, const SkPaint* paint)
192{
193    if (contextDisabled())
194        return;
195
196    realizeCanvasSave();
197
198    m_canvas->saveLayer(bounds, paint);
199    if (m_trackOpaqueRegion)
200        m_opaqueRegion.pushCanvasLayer(paint);
201}
202
203void GraphicsContext::restoreLayer()
204{
205    if (contextDisabled())
206        return;
207
208    m_canvas->restore();
209    if (m_trackOpaqueRegion)
210        m_opaqueRegion.popCanvasLayer(this);
211}
212
213void GraphicsContext::beginAnnotation(const char* rendererName, const char* paintPhase,
214    const String& elementId, const String& elementClass, const String& elementTag)
215{
216    if (contextDisabled())
217        return;
218
219    canvas()->beginCommentGroup("GraphicsContextAnnotation");
220
221    GraphicsContextAnnotation annotation(rendererName, paintPhase, elementId, elementClass, elementTag);
222    AnnotationList annotations;
223    annotation.asAnnotationList(annotations);
224
225    AnnotationList::const_iterator end = annotations.end();
226    for (AnnotationList::const_iterator it = annotations.begin(); it != end; ++it)
227        canvas()->addComment(it->first, it->second.ascii().data());
228
229#if ASSERT_ENABLED
230    ++m_annotationCount;
231#endif
232}
233
234void GraphicsContext::endAnnotation()
235{
236    if (contextDisabled())
237        return;
238
239    canvas()->endCommentGroup();
240
241    ASSERT(m_annotationCount > 0);
242#if ASSERT_ENABLED
243    --m_annotationCount;
244#endif
245}
246
247void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
248{
249    if (contextDisabled())
250        return;
251
252    ASSERT(pattern);
253    if (!pattern) {
254        setStrokeColor(Color::black);
255        return;
256    }
257    mutableState()->setStrokePattern(pattern);
258}
259
260void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
261{
262    if (contextDisabled())
263        return;
264
265    ASSERT(gradient);
266    if (!gradient) {
267        setStrokeColor(Color::black);
268        return;
269    }
270    mutableState()->setStrokeGradient(gradient);
271}
272
273void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
274{
275    if (contextDisabled())
276        return;
277
278    ASSERT(pattern);
279    if (!pattern) {
280        setFillColor(Color::black);
281        return;
282    }
283
284    mutableState()->setFillPattern(pattern);
285}
286
287void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
288{
289    if (contextDisabled())
290        return;
291
292    ASSERT(gradient);
293    if (!gradient) {
294        setFillColor(Color::black);
295        return;
296    }
297
298    mutableState()->setFillGradient(gradient);
299}
300
301void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color& color,
302    DrawLooperBuilder::ShadowTransformMode shadowTransformMode,
303    DrawLooperBuilder::ShadowAlphaMode shadowAlphaMode)
304{
305    if (contextDisabled())
306        return;
307
308    if (!color.alpha() || (!offset.width() && !offset.height() && !blur)) {
309        clearShadow();
310        return;
311    }
312
313    OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
314    drawLooperBuilder->addShadow(offset, blur, color, shadowTransformMode, shadowAlphaMode);
315    drawLooperBuilder->addUnmodifiedContent();
316    setDrawLooper(drawLooperBuilder.release());
317}
318
319void GraphicsContext::setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder)
320{
321    if (contextDisabled())
322        return;
323
324    mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper());
325}
326
327void GraphicsContext::clearDrawLooper()
328{
329    if (contextDisabled())
330        return;
331
332    mutableState()->clearDrawLooper();
333}
334
335bool GraphicsContext::hasShadow() const
336{
337    return !!immutableState()->drawLooper();
338}
339
340bool GraphicsContext::getTransformedClipBounds(FloatRect* bounds) const
341{
342    if (contextDisabled())
343        return false;
344    SkIRect skIBounds;
345    if (!m_canvas->getClipDeviceBounds(&skIBounds))
346        return false;
347    SkRect skBounds = SkRect::Make(skIBounds);
348    *bounds = FloatRect(skBounds);
349    return true;
350}
351
352SkMatrix GraphicsContext::getTotalMatrix() const
353{
354    if (contextDisabled())
355        return SkMatrix::I();
356
357    if (!isRecording())
358        return m_canvas->getTotalMatrix();
359
360    const RecordingState& recordingState = m_recordingStateStack.last();
361    SkMatrix totalMatrix = recordingState.m_savedMatrix;
362    totalMatrix.preConcat(m_canvas->getTotalMatrix());
363
364    return totalMatrix;
365}
366
367void GraphicsContext::adjustTextRenderMode(SkPaint* paint)
368{
369    if (contextDisabled())
370        return;
371
372    if (!paint->isLCDRenderText())
373        return;
374
375    paint->setLCDRenderText(couldUseLCDRenderedText());
376}
377
378bool GraphicsContext::couldUseLCDRenderedText()
379{
380    // Our layers only have a single alpha channel. This means that subpixel
381    // rendered text cannot be composited correctly when the layer is
382    // collapsed. Therefore, subpixel text is contextDisabled when we are drawing
383    // onto a layer.
384    if (contextDisabled() || m_canvas->isDrawingToLayer() || !isCertainlyOpaque())
385        return false;
386
387    return shouldSmoothFonts();
388}
389
390void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode)
391{
392    if (contextDisabled())
393        return;
394    mutableState()->setCompositeOperation(compositeOperation, blendMode);
395}
396
397SkColorFilter* GraphicsContext::colorFilter()
398{
399    return immutableState()->colorFilter();
400}
401
402void GraphicsContext::setColorFilter(ColorFilter colorFilter)
403{
404    GraphicsContextState* stateToSet = mutableState();
405
406    // We only support one active color filter at the moment. If (when) this becomes a problem,
407    // we should switch to using color filter chains (Skia work in progress).
408    ASSERT(!stateToSet->colorFilter());
409    stateToSet->setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter));
410}
411
412bool GraphicsContext::readPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, int x, int y)
413{
414    if (contextDisabled())
415        return false;
416
417    return m_canvas->readPixels(info, pixels, rowBytes, x, y);
418}
419
420void GraphicsContext::setMatrix(const SkMatrix& matrix)
421{
422    if (contextDisabled())
423        return;
424
425    realizeCanvasSave();
426
427    m_canvas->setMatrix(matrix);
428}
429
430void GraphicsContext::concat(const SkMatrix& matrix)
431{
432    if (contextDisabled())
433        return;
434
435    if (matrix.isIdentity())
436        return;
437
438    realizeCanvasSave();
439
440    m_canvas->concat(matrix);
441}
442
443void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds)
444{
445    beginLayer(opacity, immutableState()->compositeOperator(), bounds);
446}
447
448void GraphicsContext::beginLayer(float opacity, CompositeOperator op, const FloatRect* bounds, ColorFilter colorFilter, ImageFilter* imageFilter)
449{
450    if (contextDisabled())
451        return;
452
453    SkPaint layerPaint;
454    layerPaint.setAlpha(static_cast<unsigned char>(opacity * 255));
455    layerPaint.setXfermode(WebCoreCompositeToSkiaComposite(op, m_paintState->blendMode()).get());
456    layerPaint.setColorFilter(WebCoreColorFilterToSkiaColorFilter(colorFilter).get());
457    layerPaint.setImageFilter(imageFilter);
458
459    if (bounds) {
460        SkRect skBounds = WebCoreFloatRectToSKRect(*bounds);
461        saveLayer(&skBounds, &layerPaint);
462    } else {
463        saveLayer(0, &layerPaint);
464    }
465
466#if ASSERT_ENABLED
467    ++m_layerCount;
468#endif
469}
470
471void GraphicsContext::endLayer()
472{
473    if (contextDisabled())
474        return;
475
476    restoreLayer();
477
478    ASSERT(m_layerCount > 0);
479#if ASSERT_ENABLED
480    --m_layerCount;
481#endif
482}
483
484void GraphicsContext::beginRecording(const FloatRect& bounds)
485{
486    RefPtr<DisplayList> displayList = adoptRef(new DisplayList(bounds));
487
488    SkCanvas* savedCanvas = m_canvas;
489    SkMatrix savedMatrix = getTotalMatrix();
490
491    if (!contextDisabled()) {
492        IntRect recordingRect = enclosingIntRect(bounds);
493        m_canvas = displayList->beginRecording(recordingRect.size());
494
495        // We want the bounds offset mapped to (0, 0), such that the display list content
496        // is fully contained within the SkPictureRecord's bounds.
497        if (!toFloatSize(bounds.location()).isZero()) {
498            m_canvas->translate(-bounds.x(), -bounds.y());
499            // To avoid applying the offset repeatedly in getTotalMatrix(), we pre-apply it here.
500            savedMatrix.preTranslate(bounds.x(), bounds.y());
501        }
502    }
503
504    m_recordingStateStack.append(RecordingState(savedCanvas, savedMatrix, displayList));
505}
506
507PassRefPtr<DisplayList> GraphicsContext::endRecording()
508{
509    ASSERT(!m_recordingStateStack.isEmpty());
510
511    RecordingState recording = m_recordingStateStack.last();
512    if (!contextDisabled()) {
513        ASSERT(recording.m_displayList->isRecording());
514        recording.m_displayList->endRecording();
515    }
516
517    m_recordingStateStack.removeLast();
518    m_canvas = recording.m_savedCanvas;
519
520    return recording.m_displayList.release();
521}
522
523bool GraphicsContext::isRecording() const
524{
525    return !m_recordingStateStack.isEmpty();
526}
527
528void GraphicsContext::drawDisplayList(DisplayList* displayList)
529{
530    ASSERT(displayList);
531    ASSERT(!displayList->isRecording());
532
533    if (contextDisabled() || displayList->bounds().isEmpty())
534        return;
535
536    realizeCanvasSave();
537
538    const FloatRect& bounds = displayList->bounds();
539    if (bounds.x() || bounds.y())
540        m_canvas->translate(bounds.x(), bounds.y());
541
542    m_canvas->drawPicture(displayList->picture());
543
544    if (bounds.x() || bounds.y())
545        m_canvas->translate(-bounds.x(), -bounds.y());
546}
547
548void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
549{
550    if (contextDisabled())
551        return;
552
553    if (numPoints <= 1)
554        return;
555
556    SkPath path;
557    setPathFromConvexPoints(&path, numPoints, points);
558
559    SkPaint paint(immutableState()->fillPaint());
560    paint.setAntiAlias(shouldAntialias);
561    drawPath(path, paint);
562
563    if (strokeStyle() != NoStroke)
564        drawPath(path, immutableState()->strokePaint());
565}
566
567void GraphicsContext::drawFocusRing(const Path& focusRingPath, int width, int offset, const Color& color)
568{
569    // FIXME: Implement support for offset.
570    if (contextDisabled())
571        return;
572
573    SkPaint paint;
574    paint.setAntiAlias(true);
575    paint.setStyle(SkPaint::kStroke_Style);
576    paint.setColor(color.rgb());
577
578    drawOuterPath(focusRingPath.skPath(), paint, width);
579    drawInnerPath(focusRingPath.skPath(), paint, width);
580}
581
582void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
583{
584    if (contextDisabled())
585        return;
586
587    unsigned rectCount = rects.size();
588    if (!rectCount)
589        return;
590
591    SkRegion focusRingRegion;
592    const int focusRingOutset = getFocusRingOutset(offset);
593    for (unsigned i = 0; i < rectCount; i++) {
594        SkIRect r = rects[i];
595        r.inset(-focusRingOutset, -focusRingOutset);
596        focusRingRegion.op(r, SkRegion::kUnion_Op);
597    }
598
599    SkPath path;
600    SkPaint paint;
601    paint.setAntiAlias(true);
602    paint.setStyle(SkPaint::kStroke_Style);
603
604    paint.setColor(color.rgb());
605    focusRingRegion.getBoundaryPath(&path);
606    drawOuterPath(path, paint, width);
607    drawInnerPath(path, paint, width);
608}
609
610static inline IntRect areaCastingShadowInHole(const IntRect& holeRect, int shadowBlur, int shadowSpread, const IntSize& shadowOffset)
611{
612    IntRect bounds(holeRect);
613
614    bounds.inflate(shadowBlur);
615
616    if (shadowSpread < 0)
617        bounds.inflate(-shadowSpread);
618
619    IntRect offsetBounds = bounds;
620    offsetBounds.move(-shadowOffset);
621    return unionRect(bounds, offsetBounds);
622}
623
624void GraphicsContext::drawInnerShadow(const RoundedRect& rect, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges)
625{
626    if (contextDisabled())
627        return;
628
629    IntRect holeRect(rect.rect());
630    holeRect.inflate(-shadowSpread);
631
632    if (holeRect.isEmpty()) {
633        if (rect.isRounded())
634            fillRoundedRect(rect, shadowColor);
635        else
636            fillRect(rect.rect(), shadowColor);
637        return;
638    }
639
640    if (clippedEdges & LeftEdge) {
641        holeRect.move(-max(shadowOffset.width(), 0) - shadowBlur, 0);
642        holeRect.setWidth(holeRect.width() + max(shadowOffset.width(), 0) + shadowBlur);
643    }
644    if (clippedEdges & TopEdge) {
645        holeRect.move(0, -max(shadowOffset.height(), 0) - shadowBlur);
646        holeRect.setHeight(holeRect.height() + max(shadowOffset.height(), 0) + shadowBlur);
647    }
648    if (clippedEdges & RightEdge)
649        holeRect.setWidth(holeRect.width() - min(shadowOffset.width(), 0) + shadowBlur);
650    if (clippedEdges & BottomEdge)
651        holeRect.setHeight(holeRect.height() - min(shadowOffset.height(), 0) + shadowBlur);
652
653    Color fillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), 255);
654
655    IntRect outerRect = areaCastingShadowInHole(rect.rect(), shadowBlur, shadowSpread, shadowOffset);
656    RoundedRect roundedHole(holeRect, rect.radii());
657
658    save();
659    if (rect.isRounded()) {
660        Path path;
661        path.addRoundedRect(rect);
662        clipPath(path);
663        roundedHole.shrinkRadii(shadowSpread);
664    } else {
665        clip(rect.rect());
666    }
667
668    OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::create();
669    drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor,
670        DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
671    setDrawLooper(drawLooperBuilder.release());
672    fillRectWithRoundedHole(outerRect, roundedHole, fillColor);
673    restore();
674    clearDrawLooper();
675}
676
677void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
678{
679    if (contextDisabled())
680        return;
681
682    StrokeStyle penStyle = strokeStyle();
683    if (penStyle == NoStroke)
684        return;
685
686    FloatPoint p1 = point1;
687    FloatPoint p2 = point2;
688    bool isVerticalLine = (p1.x() == p2.x());
689    int width = roundf(strokeThickness());
690
691    // We know these are vertical or horizontal lines, so the length will just
692    // be the sum of the displacement component vectors give or take 1 -
693    // probably worth the speed up of no square root, which also won't be exact.
694    FloatSize disp = p2 - p1;
695    int length = SkScalarRoundToInt(disp.width() + disp.height());
696    SkPaint paint(immutableState()->strokePaint(length));
697
698    if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
699        // Do a rect fill of our endpoints.  This ensures we always have the
700        // appearance of being a border.  We then draw the actual dotted/dashed line.
701        SkRect r1, r2;
702        r1.set(p1.x(), p1.y(), p1.x() + width, p1.y() + width);
703        r2.set(p2.x(), p2.y(), p2.x() + width, p2.y() + width);
704
705        if (isVerticalLine) {
706            r1.offset(-width / 2, 0);
707            r2.offset(-width / 2, -width);
708        } else {
709            r1.offset(0, -width / 2);
710            r2.offset(-width, -width / 2);
711        }
712        SkPaint fillPaint;
713        fillPaint.setColor(paint.getColor());
714        drawRect(r1, fillPaint);
715        drawRect(r2, fillPaint);
716    }
717
718    adjustLineToPixelBoundaries(p1, p2, width, penStyle);
719    SkPoint pts[2] = { p1.data(), p2.data() };
720
721    m_canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, paint);
722
723    if (m_trackOpaqueRegion)
724        m_opaqueRegion.didDrawPoints(this, SkCanvas::kLines_PointMode, 2, pts, paint);
725}
726
727void GraphicsContext::drawLineForDocumentMarker(const FloatPoint& pt, float width, DocumentMarkerLineStyle style)
728{
729    if (contextDisabled())
730        return;
731
732    int deviceScaleFactor = m_useHighResMarker ? 2 : 1;
733
734    // Create the pattern we'll use to draw the underline.
735    int index = style == DocumentMarkerGrammarLineStyle ? 1 : 0;
736    static SkBitmap* misspellBitmap1x[2] = { 0, 0 };
737    static SkBitmap* misspellBitmap2x[2] = { 0, 0 };
738    SkBitmap** misspellBitmap = deviceScaleFactor == 2 ? misspellBitmap2x : misspellBitmap1x;
739    if (!misspellBitmap[index]) {
740#if OS(MACOSX)
741        // Match the artwork used by the Mac.
742        const int rowPixels = 4 * deviceScaleFactor;
743        const int colPixels = 3 * deviceScaleFactor;
744        SkBitmap bitmap;
745        if (!bitmap.allocN32Pixels(rowPixels, colPixels))
746            return;
747
748        bitmap.eraseARGB(0, 0, 0, 0);
749        const uint32_t transparentColor = 0x00000000;
750
751        if (deviceScaleFactor == 1) {
752            const uint32_t colors[2][6] = {
753                { 0x2a2a0600, 0x57571000,  0xa8a81b00, 0xbfbf1f00,  0x70701200, 0xe0e02400 },
754                { 0x2a0f0f0f, 0x571e1e1e,  0xa83d3d3d, 0xbf454545,  0x70282828, 0xe0515151 }
755            };
756
757            // Pattern: a b a   a b a
758            //          c d c   c d c
759            //          e f e   e f e
760            for (int x = 0; x < colPixels; ++x) {
761                uint32_t* row = bitmap.getAddr32(0, x);
762                row[0] = colors[index][x * 2];
763                row[1] = colors[index][x * 2 + 1];
764                row[2] = colors[index][x * 2];
765                row[3] = transparentColor;
766            }
767        } else if (deviceScaleFactor == 2) {
768            const uint32_t colors[2][18] = {
769                { 0x0a090101, 0x33320806, 0x55540f0a,  0x37360906, 0x6e6c120c, 0x6e6c120c,  0x7674140d, 0x8d8b1810, 0x8d8b1810,
770                  0x96941a11, 0xb3b01f15, 0xb3b01f15,  0x6d6b130c, 0xd9d62619, 0xd9d62619,  0x19180402, 0x7c7a150e, 0xcecb2418 },
771                { 0x0a020202, 0x33141414, 0x55232323,  0x37161616, 0x6e2e2e2e, 0x6e2e2e2e,  0x76313131, 0x8d3a3a3a, 0x8d3a3a3a,
772                  0x963e3e3e, 0xb34b4b4b, 0xb34b4b4b,  0x6d2d2d2d, 0xd95b5b5b, 0xd95b5b5b,  0x19090909, 0x7c343434, 0xce575757 }
773            };
774
775            // Pattern: a b c c b a
776            //          d e f f e d
777            //          g h j j h g
778            //          k l m m l k
779            //          n o p p o n
780            //          q r s s r q
781            for (int x = 0; x < colPixels; ++x) {
782                uint32_t* row = bitmap.getAddr32(0, x);
783                row[0] = colors[index][x * 3];
784                row[1] = colors[index][x * 3 + 1];
785                row[2] = colors[index][x * 3 + 2];
786                row[3] = colors[index][x * 3 + 2];
787                row[4] = colors[index][x * 3 + 1];
788                row[5] = colors[index][x * 3];
789                row[6] = transparentColor;
790                row[7] = transparentColor;
791            }
792        } else
793            ASSERT_NOT_REACHED();
794
795        misspellBitmap[index] = new SkBitmap(bitmap);
796#else
797        // We use a 2-pixel-high misspelling indicator because that seems to be
798        // what WebKit is designed for, and how much room there is in a typical
799        // page for it.
800        const int rowPixels = 32 * deviceScaleFactor; // Must be multiple of 4 for pattern below.
801        const int colPixels = 2 * deviceScaleFactor;
802        SkBitmap bitmap;
803        if (!bitmap.allocN32Pixels(rowPixels, colPixels))
804            return;
805
806        bitmap.eraseARGB(0, 0, 0, 0);
807        if (deviceScaleFactor == 1)
808            draw1xMarker(&bitmap, index);
809        else if (deviceScaleFactor == 2)
810            draw2xMarker(&bitmap, index);
811        else
812            ASSERT_NOT_REACHED();
813
814        misspellBitmap[index] = new SkBitmap(bitmap);
815#endif
816    }
817
818#if OS(MACOSX)
819    SkScalar originX = WebCoreFloatToSkScalar(pt.x()) * deviceScaleFactor;
820    SkScalar originY = WebCoreFloatToSkScalar(pt.y()) * deviceScaleFactor;
821
822    // Make sure to draw only complete dots.
823    int rowPixels = misspellBitmap[index]->width();
824    float widthMod = fmodf(width * deviceScaleFactor, rowPixels);
825    if (rowPixels - widthMod > deviceScaleFactor)
826        width -= widthMod / deviceScaleFactor;
827#else
828    SkScalar originX = WebCoreFloatToSkScalar(pt.x());
829
830    // Offset it vertically by 1 so that there's some space under the text.
831    SkScalar originY = WebCoreFloatToSkScalar(pt.y()) + 1;
832    originX *= deviceScaleFactor;
833    originY *= deviceScaleFactor;
834#endif
835
836    SkMatrix localMatrix;
837    localMatrix.setTranslate(originX, originY);
838    RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(
839        *misspellBitmap[index], SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix));
840
841    SkPaint paint;
842    paint.setShader(shader.get());
843
844    SkRect rect;
845    rect.set(originX, originY, originX + WebCoreFloatToSkScalar(width) * deviceScaleFactor, originY + SkIntToScalar(misspellBitmap[index]->height()));
846
847    if (deviceScaleFactor == 2) {
848        save();
849        scale(0.5, 0.5);
850    }
851    drawRect(rect, paint);
852    if (deviceScaleFactor == 2)
853        restore();
854}
855
856void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool printing)
857{
858    if (contextDisabled())
859        return;
860
861    if (width <= 0)
862        return;
863
864    SkPaint paint;
865    switch (strokeStyle()) {
866    case NoStroke:
867    case SolidStroke:
868    case DoubleStroke:
869    case WavyStroke: {
870        int thickness = SkMax32(static_cast<int>(strokeThickness()), 1);
871        SkRect r;
872        r.fLeft = WebCoreFloatToSkScalar(pt.x());
873        // Avoid anti-aliasing lines. Currently, these are always horizontal.
874        // Round to nearest pixel to match text and other content.
875        r.fTop = WebCoreFloatToSkScalar(floorf(pt.y() + 0.5f));
876        r.fRight = r.fLeft + WebCoreFloatToSkScalar(width);
877        r.fBottom = r.fTop + SkIntToScalar(thickness);
878        paint = immutableState()->fillPaint();
879        // Text lines are drawn using the stroke color.
880        paint.setColor(effectiveStrokeColor());
881        drawRect(r, paint);
882        return;
883    }
884    case DottedStroke:
885    case DashedStroke: {
886        int y = floorf(pt.y() + std::max<float>(strokeThickness() / 2.0f, 0.5f));
887        drawLine(IntPoint(pt.x(), y), IntPoint(pt.x() + width, y));
888        return;
889    }
890    }
891
892    ASSERT_NOT_REACHED();
893}
894
895// Draws a filled rectangle with a stroked border.
896void GraphicsContext::drawRect(const IntRect& rect)
897{
898    if (contextDisabled())
899        return;
900
901    ASSERT(!rect.isEmpty());
902    if (rect.isEmpty())
903        return;
904
905    SkRect skRect = rect;
906    int fillcolorNotTransparent = immutableState()->fillColor().rgb() & 0xFF000000;
907    if (fillcolorNotTransparent)
908        drawRect(skRect, immutableState()->fillPaint());
909
910    if (immutableState()->strokeData().style() != NoStroke
911        && immutableState()->strokeData().color().alpha()) {
912        // Stroke a width: 1 inset border
913        SkPaint paint(immutableState()->fillPaint());
914        paint.setColor(effectiveStrokeColor());
915        paint.setStyle(SkPaint::kStroke_Style);
916        paint.setStrokeWidth(1);
917
918        skRect.inset(0.5f, 0.5f);
919        drawRect(skRect, paint);
920    }
921}
922
923void GraphicsContext::drawText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point)
924{
925    if (contextDisabled())
926        return;
927
928    font.drawText(this, runInfo, point);
929}
930
931void GraphicsContext::drawEmphasisMarks(const Font& font, const TextRunPaintInfo& runInfo, const AtomicString& mark, const FloatPoint& point)
932{
933    if (contextDisabled())
934        return;
935
936    font.drawEmphasisMarks(this, runInfo, mark, point);
937}
938
939void GraphicsContext::drawBidiText(const Font& font, const TextRunPaintInfo& runInfo, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction)
940{
941    if (contextDisabled())
942        return;
943
944    // sub-run painting is not supported for Bidi text.
945    const TextRun& run = runInfo.run;
946    ASSERT((runInfo.from == 0) && (runInfo.to == run.length()));
947    BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
948    bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride()));
949    bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0));
950
951    // FIXME: This ownership should be reversed. We should pass BidiRunList
952    // to BidiResolver in createBidiRunsForLine.
953    BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs();
954    bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
955    if (!bidiRuns.runCount())
956        return;
957
958    FloatPoint currPoint = point;
959    BidiCharacterRun* bidiRun = bidiRuns.firstRun();
960    while (bidiRun) {
961        TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start());
962        bool isRTL = bidiRun->level() % 2;
963        subrun.setDirection(isRTL ? RTL : LTR);
964        subrun.setDirectionalOverride(bidiRun->dirOverride(false));
965
966        TextRunPaintInfo subrunInfo(subrun);
967        subrunInfo.bounds = runInfo.bounds;
968        font.drawText(this, subrunInfo, currPoint, customFontNotReadyAction);
969
970        bidiRun = bidiRun->next();
971        // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
972        if (bidiRun)
973            currPoint.move(font.width(subrun), 0);
974    }
975
976    bidiRuns.deleteRuns();
977}
978
979void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const FloatPoint& point, int h, const Color& backgroundColor, int from, int to)
980{
981    if (contextDisabled())
982        return;
983
984    fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor);
985}
986
987void GraphicsContext::drawImage(Image* image, const IntPoint& p, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
988{
989    if (!image)
990        return;
991    drawImage(image, FloatRect(IntRect(p, image->size())), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
992}
993
994void GraphicsContext::drawImage(Image* image, const IntRect& r, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
995{
996    if (!image)
997        return;
998    drawImage(image, FloatRect(r), FloatRect(FloatPoint(), FloatSize(image->size())), op, shouldRespectImageOrientation);
999}
1000
1001void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, RespectImageOrientationEnum shouldRespectImageOrientation)
1002{
1003    drawImage(image, dest, src, op, blink::WebBlendModeNormal, shouldRespectImageOrientation);
1004}
1005
1006void GraphicsContext::drawImage(Image* image, const FloatRect& dest)
1007{
1008    if (!image)
1009        return;
1010    drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size())));
1011}
1012
1013void GraphicsContext::drawImage(Image* image, const FloatRect& dest, const FloatRect& src, CompositeOperator op, WebBlendMode blendMode, RespectImageOrientationEnum shouldRespectImageOrientation)
1014{
1015    if (contextDisabled() || !image)
1016        return;
1017    image->draw(this, dest, src, op, blendMode, shouldRespectImageOrientation);
1018}
1019
1020void GraphicsContext::drawTiledImage(Image* image, const IntRect& destRect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, WebBlendMode blendMode, const IntSize& repeatSpacing)
1021{
1022    if (contextDisabled() || !image)
1023        return;
1024    image->drawTiled(this, destRect, srcPoint, tileSize, op, blendMode, repeatSpacing);
1025}
1026
1027void GraphicsContext::drawTiledImage(Image* image, const IntRect& dest, const IntRect& srcRect,
1028    const FloatSize& tileScaleFactor, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op)
1029{
1030    if (contextDisabled() || !image)
1031        return;
1032
1033    if (hRule == Image::StretchTile && vRule == Image::StretchTile) {
1034        // Just do a scale.
1035        drawImage(image, dest, srcRect, op);
1036        return;
1037    }
1038
1039    image->drawTiled(this, dest, srcRect, tileScaleFactor, hRule, vRule, op);
1040}
1041
1042void GraphicsContext::drawImageBuffer(ImageBuffer* image, const FloatRect& dest,
1043    const FloatRect* src, CompositeOperator op)
1044{
1045    if (contextDisabled() || !image)
1046        return;
1047
1048    image->draw(this, dest, src, op);
1049}
1050
1051void GraphicsContext::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes, int x, int y)
1052{
1053    if (contextDisabled())
1054        return;
1055
1056    m_canvas->writePixels(info, pixels, rowBytes, x, y);
1057
1058    if (m_trackOpaqueRegion) {
1059        SkRect rect = SkRect::MakeXYWH(x, y, info.width(), info.height());
1060        SkPaint paint;
1061
1062        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
1063        if (kOpaque_SkAlphaType != info.alphaType())
1064            paint.setAlpha(0x80); // signal to m_opaqueRegion that we are not fully opaque
1065
1066        m_opaqueRegion.didDrawRect(this, rect, paint, 0);
1067        // more efficient would be to call markRectAsOpaque or MarkRectAsNonOpaque directly,
1068        // rather than cons-ing up a paint with an xfermode and alpha
1069    }
1070}
1071
1072void GraphicsContext::writePixels(const SkBitmap& bitmap, int x, int y)
1073{
1074    if (contextDisabled())
1075        return;
1076
1077    if (!bitmap.getTexture()) {
1078        SkAutoLockPixels alp(bitmap);
1079        if (bitmap.getPixels())
1080            writePixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), x, y);
1081    }
1082}
1083
1084void GraphicsContext::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint)
1085{
1086    if (contextDisabled())
1087        return;
1088
1089    m_canvas->drawBitmap(bitmap, left, top, paint);
1090
1091    if (m_trackOpaqueRegion) {
1092        SkRect rect = SkRect::MakeXYWH(left, top, bitmap.width(), bitmap.height());
1093        m_opaqueRegion.didDrawRect(this, rect, *paint, &bitmap);
1094    }
1095}
1096
1097void GraphicsContext::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
1098    const SkRect& dst, const SkPaint* paint)
1099{
1100    if (contextDisabled())
1101        return;
1102
1103    SkCanvas::DrawBitmapRectFlags flags =
1104        immutableState()->shouldClampToSourceRect() ? SkCanvas::kNone_DrawBitmapRectFlag : SkCanvas::kBleed_DrawBitmapRectFlag;
1105
1106    m_canvas->drawBitmapRectToRect(bitmap, src, dst, paint, flags);
1107
1108    if (m_trackOpaqueRegion)
1109        m_opaqueRegion.didDrawRect(this, dst, *paint, &bitmap);
1110}
1111
1112void GraphicsContext::drawOval(const SkRect& oval, const SkPaint& paint)
1113{
1114    if (contextDisabled())
1115        return;
1116
1117    m_canvas->drawOval(oval, paint);
1118
1119    if (m_trackOpaqueRegion)
1120        m_opaqueRegion.didDrawBounded(this, oval, paint);
1121}
1122
1123void GraphicsContext::drawPath(const SkPath& path, const SkPaint& paint)
1124{
1125    if (contextDisabled())
1126        return;
1127
1128    m_canvas->drawPath(path, paint);
1129
1130    if (m_trackOpaqueRegion)
1131        m_opaqueRegion.didDrawPath(this, path, paint);
1132}
1133
1134void GraphicsContext::drawRect(const SkRect& rect, const SkPaint& paint)
1135{
1136    if (contextDisabled())
1137        return;
1138
1139    m_canvas->drawRect(rect, paint);
1140
1141    if (m_trackOpaqueRegion)
1142        m_opaqueRegion.didDrawRect(this, rect, paint, 0);
1143}
1144
1145void GraphicsContext::didDrawRect(const SkRect& rect, const SkPaint& paint, const SkBitmap* bitmap)
1146{
1147    if (contextDisabled())
1148        return;
1149
1150    if (m_trackOpaqueRegion)
1151        m_opaqueRegion.didDrawRect(this, rect, paint, bitmap);
1152}
1153
1154void GraphicsContext::drawPosText(const void* text, size_t byteLength,
1155    const SkPoint pos[],  const SkRect& textRect, const SkPaint& paint)
1156{
1157    if (contextDisabled())
1158        return;
1159
1160    m_canvas->drawPosText(text, byteLength, pos, paint);
1161    didDrawTextInRect(textRect);
1162
1163    // FIXME: compute bounds for positioned text.
1164    if (m_trackOpaqueRegion)
1165        m_opaqueRegion.didDrawUnbounded(this, paint, OpaqueRegionSkia::FillOrStroke);
1166}
1167
1168void GraphicsContext::fillPath(const Path& pathToFill)
1169{
1170    if (contextDisabled() || pathToFill.isEmpty())
1171        return;
1172
1173    // Use const_cast and temporarily modify the fill type instead of copying the path.
1174    SkPath& path = const_cast<SkPath&>(pathToFill.skPath());
1175    SkPath::FillType previousFillType = path.getFillType();
1176
1177    SkPath::FillType temporaryFillType =
1178        immutableState()->fillRule() == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1179    path.setFillType(temporaryFillType);
1180
1181    drawPath(path, immutableState()->fillPaint());
1182
1183    path.setFillType(previousFillType);
1184}
1185
1186void GraphicsContext::fillRect(const FloatRect& rect)
1187{
1188    if (contextDisabled())
1189        return;
1190
1191    SkRect r = rect;
1192
1193    drawRect(r, immutableState()->fillPaint());
1194}
1195
1196void GraphicsContext::fillRect(const FloatRect& rect, const Color& color)
1197{
1198    if (contextDisabled())
1199        return;
1200
1201    SkRect r = rect;
1202    SkPaint paint = immutableState()->fillPaint();
1203    paint.setColor(color.rgb());
1204    drawRect(r, paint);
1205}
1206
1207void GraphicsContext::fillBetweenRoundedRects(const IntRect& outer, const IntSize& outerTopLeft, const IntSize& outerTopRight, const IntSize& outerBottomLeft, const IntSize& outerBottomRight,
1208    const IntRect& inner, const IntSize& innerTopLeft, const IntSize& innerTopRight, const IntSize& innerBottomLeft, const IntSize& innerBottomRight, const Color& color) {
1209    if (contextDisabled())
1210        return;
1211
1212    SkVector outerRadii[4];
1213    SkVector innerRadii[4];
1214    setRadii(outerRadii, outerTopLeft, outerTopRight, outerBottomRight, outerBottomLeft);
1215    setRadii(innerRadii, innerTopLeft, innerTopRight, innerBottomRight, innerBottomLeft);
1216
1217    SkRRect rrOuter;
1218    SkRRect rrInner;
1219    rrOuter.setRectRadii(outer, outerRadii);
1220    rrInner.setRectRadii(inner, innerRadii);
1221
1222    SkPaint paint(immutableState()->fillPaint());
1223    paint.setColor(color.rgb());
1224
1225    m_canvas->drawDRRect(rrOuter, rrInner, paint);
1226
1227    if (m_trackOpaqueRegion)
1228        m_opaqueRegion.didDrawBounded(this, rrOuter.getBounds(), paint);
1229}
1230
1231void GraphicsContext::fillBetweenRoundedRects(const RoundedRect& outer, const RoundedRect& inner, const Color& color)
1232{
1233    fillBetweenRoundedRects(outer.rect(), outer.radii().topLeft(), outer.radii().topRight(), outer.radii().bottomLeft(), outer.radii().bottomRight(),
1234        inner.rect(), inner.radii().topLeft(), inner.radii().topRight(), inner.radii().bottomLeft(), inner.radii().bottomRight(), color);
1235}
1236
1237void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
1238    const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color)
1239{
1240    if (contextDisabled())
1241        return;
1242
1243    if (topLeft.width() + topRight.width() > rect.width()
1244            || bottomLeft.width() + bottomRight.width() > rect.width()
1245            || topLeft.height() + bottomLeft.height() > rect.height()
1246            || topRight.height() + bottomRight.height() > rect.height()) {
1247        // Not all the radii fit, return a rect. This matches the behavior of
1248        // Path::createRoundedRectangle. Without this we attempt to draw a round
1249        // shadow for a square box.
1250        fillRect(rect, color);
1251        return;
1252    }
1253
1254    SkVector radii[4];
1255    setRadii(radii, topLeft, topRight, bottomRight, bottomLeft);
1256
1257    SkRRect rr;
1258    rr.setRectRadii(rect, radii);
1259
1260    SkPaint paint(immutableState()->fillPaint());
1261    paint.setColor(color.rgb());
1262
1263    m_canvas->drawRRect(rr, paint);
1264
1265    if (m_trackOpaqueRegion)
1266        m_opaqueRegion.didDrawBounded(this, rr.getBounds(), paint);
1267}
1268
1269void GraphicsContext::fillEllipse(const FloatRect& ellipse)
1270{
1271    if (contextDisabled())
1272        return;
1273
1274    SkRect rect = ellipse;
1275    drawOval(rect, immutableState()->fillPaint());
1276}
1277
1278void GraphicsContext::strokePath(const Path& pathToStroke)
1279{
1280    if (contextDisabled() || pathToStroke.isEmpty())
1281        return;
1282
1283    const SkPath& path = pathToStroke.skPath();
1284    drawPath(path, immutableState()->strokePaint());
1285}
1286
1287void GraphicsContext::strokeRect(const FloatRect& rect)
1288{
1289    strokeRect(rect, strokeThickness());
1290}
1291
1292void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
1293{
1294    if (contextDisabled())
1295        return;
1296
1297    SkPaint paint(immutableState()->strokePaint());
1298    paint.setStrokeWidth(WebCoreFloatToSkScalar(lineWidth));
1299    // Reset the dash effect to account for the width
1300    immutableState()->strokeData().setupPaintDashPathEffect(&paint, 0);
1301    // strokerect has special rules for CSS when the rect is degenerate:
1302    // if width==0 && height==0, do nothing
1303    // if width==0 || height==0, then just draw line for the other dimension
1304    SkRect r(rect);
1305    bool validW = r.width() > 0;
1306    bool validH = r.height() > 0;
1307    if (validW && validH) {
1308        drawRect(r, paint);
1309    } else if (validW || validH) {
1310        // we are expected to respect the lineJoin, so we can't just call
1311        // drawLine -- we have to create a path that doubles back on itself.
1312        SkPath path;
1313        path.moveTo(r.fLeft, r.fTop);
1314        path.lineTo(r.fRight, r.fBottom);
1315        path.close();
1316        drawPath(path, paint);
1317    }
1318}
1319
1320void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
1321{
1322    if (contextDisabled())
1323        return;
1324
1325    drawOval(ellipse, immutableState()->strokePaint());
1326}
1327
1328void GraphicsContext::clipRoundedRect(const RoundedRect& rect, SkRegion::Op regionOp)
1329{
1330    if (contextDisabled())
1331        return;
1332
1333    if (!rect.isRounded()) {
1334        clipRect(rect.rect(), NotAntiAliased, regionOp);
1335        return;
1336    }
1337
1338    SkVector radii[4];
1339    RoundedRect::Radii wkRadii = rect.radii();
1340    setRadii(radii, wkRadii.topLeft(), wkRadii.topRight(), wkRadii.bottomRight(), wkRadii.bottomLeft());
1341
1342    SkRRect r;
1343    r.setRectRadii(rect.rect(), radii);
1344
1345    clipRRect(r, AntiAliased, regionOp);
1346}
1347
1348void GraphicsContext::clipOut(const Path& pathToClip)
1349{
1350    if (contextDisabled())
1351        return;
1352
1353    // Use const_cast and temporarily toggle the inverse fill type instead of copying the path.
1354    SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1355    path.toggleInverseFillType();
1356    clipPath(path, AntiAliased);
1357    path.toggleInverseFillType();
1358}
1359
1360void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
1361{
1362    if (contextDisabled() || pathToClip.isEmpty())
1363        return;
1364
1365    // Use const_cast and temporarily modify the fill type instead of copying the path.
1366    SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1367    SkPath::FillType previousFillType = path.getFillType();
1368
1369    SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1370    path.setFillType(temporaryFillType);
1371    clipPath(path, AntiAliased);
1372
1373    path.setFillType(previousFillType);
1374}
1375
1376void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased)
1377{
1378    if (contextDisabled())
1379        return;
1380
1381    if (numPoints <= 1)
1382        return;
1383
1384    SkPath path;
1385    setPathFromConvexPoints(&path, numPoints, points);
1386    clipPath(path, antialiased ? AntiAliased : NotAntiAliased);
1387}
1388
1389void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
1390{
1391    if (contextDisabled())
1392        return;
1393
1394    clipRoundedRect(rect, SkRegion::kDifference_Op);
1395}
1396
1397void GraphicsContext::canvasClip(const Path& pathToClip, WindRule clipRule)
1398{
1399    if (contextDisabled())
1400        return;
1401
1402    // Use const_cast and temporarily modify the fill type instead of copying the path.
1403    SkPath& path = const_cast<SkPath&>(pathToClip.skPath());
1404    SkPath::FillType previousFillType = path.getFillType();
1405
1406    SkPath::FillType temporaryFillType = clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
1407    path.setFillType(temporaryFillType);
1408    clipPath(path);
1409
1410    path.setFillType(previousFillType);
1411}
1412
1413void GraphicsContext::clipRect(const SkRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1414{
1415    if (contextDisabled())
1416        return;
1417
1418    realizeCanvasSave();
1419
1420    m_canvas->clipRect(rect, op, aa == AntiAliased);
1421}
1422
1423void GraphicsContext::clipPath(const SkPath& path, AntiAliasingMode aa, SkRegion::Op op)
1424{
1425    if (contextDisabled())
1426        return;
1427
1428    realizeCanvasSave();
1429
1430    m_canvas->clipPath(path, op, aa == AntiAliased);
1431}
1432
1433void GraphicsContext::clipRRect(const SkRRect& rect, AntiAliasingMode aa, SkRegion::Op op)
1434{
1435    if (contextDisabled())
1436        return;
1437
1438    realizeCanvasSave();
1439
1440    m_canvas->clipRRect(rect, op, aa == AntiAliased);
1441}
1442
1443void GraphicsContext::beginCull(const FloatRect& rect)
1444{
1445    if (contextDisabled())
1446        return;
1447
1448    realizeCanvasSave();
1449    m_canvas->pushCull(rect);
1450}
1451
1452void GraphicsContext::endCull()
1453{
1454    if (contextDisabled())
1455        return;
1456
1457    realizeCanvasSave();
1458
1459    m_canvas->popCull();
1460}
1461
1462void GraphicsContext::rotate(float angleInRadians)
1463{
1464    if (contextDisabled())
1465        return;
1466
1467    realizeCanvasSave();
1468
1469    m_canvas->rotate(WebCoreFloatToSkScalar(angleInRadians * (180.0f / 3.14159265f)));
1470}
1471
1472void GraphicsContext::translate(float x, float y)
1473{
1474    if (contextDisabled())
1475        return;
1476
1477    if (!x && !y)
1478        return;
1479
1480    realizeCanvasSave();
1481
1482    m_canvas->translate(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1483}
1484
1485void GraphicsContext::scale(float x, float y)
1486{
1487    if (contextDisabled())
1488        return;
1489
1490    if (x == 1.0f && y == 1.0f)
1491        return;
1492
1493    realizeCanvasSave();
1494
1495    m_canvas->scale(WebCoreFloatToSkScalar(x), WebCoreFloatToSkScalar(y));
1496}
1497
1498void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
1499{
1500    if (contextDisabled())
1501        return;
1502
1503    SkAutoDataUnref url(SkData::NewWithCString(link.string().utf8().data()));
1504    SkAnnotateRectWithURL(m_canvas, destRect, url.get());
1505}
1506
1507void GraphicsContext::setURLFragmentForRect(const String& destName, const IntRect& rect)
1508{
1509    if (contextDisabled())
1510        return;
1511
1512    SkAutoDataUnref skDestName(SkData::NewWithCString(destName.utf8().data()));
1513    SkAnnotateLinkToDestination(m_canvas, rect, skDestName.get());
1514}
1515
1516void GraphicsContext::addURLTargetAtPoint(const String& name, const IntPoint& pos)
1517{
1518    if (contextDisabled())
1519        return;
1520
1521    SkAutoDataUnref nameData(SkData::NewWithCString(name.utf8().data()));
1522    SkAnnotateNamedDestination(m_canvas, SkPoint::Make(pos.x(), pos.y()), nameData);
1523}
1524
1525AffineTransform GraphicsContext::getCTM() const
1526{
1527    if (contextDisabled())
1528        return AffineTransform();
1529
1530    SkMatrix m = getTotalMatrix();
1531    return AffineTransform(SkScalarToDouble(m.getScaleX()),
1532                           SkScalarToDouble(m.getSkewY()),
1533                           SkScalarToDouble(m.getSkewX()),
1534                           SkScalarToDouble(m.getScaleY()),
1535                           SkScalarToDouble(m.getTranslateX()),
1536                           SkScalarToDouble(m.getTranslateY()));
1537}
1538
1539void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, CompositeOperator op)
1540{
1541    if (contextDisabled())
1542        return;
1543
1544    CompositeOperator previousOperator = compositeOperation();
1545    setCompositeOperation(op);
1546    fillRect(rect, color);
1547    setCompositeOperation(previousOperator);
1548}
1549
1550void GraphicsContext::fillRoundedRect(const RoundedRect& rect, const Color& color)
1551{
1552    if (contextDisabled())
1553        return;
1554
1555    if (rect.isRounded())
1556        fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color);
1557    else
1558        fillRect(rect.rect(), color);
1559}
1560
1561void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedRect& roundedHoleRect, const Color& color)
1562{
1563    if (contextDisabled())
1564        return;
1565
1566    Path path;
1567    path.addRect(rect);
1568
1569    if (!roundedHoleRect.radii().isZero())
1570        path.addRoundedRect(roundedHoleRect);
1571    else
1572        path.addRect(roundedHoleRect.rect());
1573
1574    WindRule oldFillRule = fillRule();
1575    Color oldFillColor = fillColor();
1576
1577    setFillRule(RULE_EVENODD);
1578    setFillColor(color);
1579
1580    fillPath(path);
1581
1582    setFillRule(oldFillRule);
1583    setFillColor(oldFillColor);
1584}
1585
1586void GraphicsContext::clearRect(const FloatRect& rect)
1587{
1588    if (contextDisabled())
1589        return;
1590
1591    SkRect r = rect;
1592    SkPaint paint(immutableState()->fillPaint());
1593    paint.setXfermodeMode(SkXfermode::kClear_Mode);
1594    drawRect(r, paint);
1595}
1596
1597void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, StrokeStyle penStyle)
1598{
1599    // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
1600    // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
1601    // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
1602    // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
1603    if (penStyle == DottedStroke || penStyle == DashedStroke) {
1604        if (p1.x() == p2.x()) {
1605            p1.setY(p1.y() + strokeWidth);
1606            p2.setY(p2.y() - strokeWidth);
1607        } else {
1608            p1.setX(p1.x() + strokeWidth);
1609            p2.setX(p2.x() - strokeWidth);
1610        }
1611    }
1612
1613    if (static_cast<int>(strokeWidth) % 2) { //odd
1614        if (p1.x() == p2.x()) {
1615            // We're a vertical line.  Adjust our x.
1616            p1.setX(p1.x() + 0.5f);
1617            p2.setX(p2.x() + 0.5f);
1618        } else {
1619            // We're a horizontal line. Adjust our y.
1620            p1.setY(p1.y() + 0.5f);
1621            p2.setY(p2.y() + 0.5f);
1622        }
1623    }
1624}
1625
1626PassOwnPtr<ImageBuffer> GraphicsContext::createCompatibleBuffer(const IntSize& size, OpacityMode opacityMode) const
1627{
1628    // Make the buffer larger if the context's transform is scaling it so we need a higher
1629    // resolution than one pixel per unit. Also set up a corresponding scale factor on the
1630    // graphics context.
1631
1632    AffineTransform transform = getCTM();
1633    IntSize scaledSize(static_cast<int>(ceil(size.width() * transform.xScale())), static_cast<int>(ceil(size.height() * transform.yScale())));
1634
1635    SkAlphaType alphaType = (opacityMode == Opaque) ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
1636    SkImageInfo info = SkImageInfo::MakeN32(size.width(), size.height(), alphaType);
1637    RefPtr<SkSurface> skSurface = adoptRef(m_canvas->newSurface(info));
1638    if (!skSurface)
1639        return nullptr;
1640    OwnPtr<ImageBufferSurface> surface = adoptPtr(new CompatibleImageBufferSurface(skSurface.release(), scaledSize, opacityMode));
1641    ASSERT(surface->isValid());
1642    OwnPtr<ImageBuffer> buffer = adoptPtr(new ImageBuffer(surface.release()));
1643
1644    buffer->context()->scale(static_cast<float>(scaledSize.width()) / size.width(),
1645        static_cast<float>(scaledSize.height()) / size.height());
1646
1647    return buffer.release();
1648}
1649
1650void GraphicsContext::setPathFromConvexPoints(SkPath* path, size_t numPoints, const FloatPoint* points)
1651{
1652    path->incReserve(numPoints);
1653    path->moveTo(WebCoreFloatToSkScalar(points[0].x()),
1654                 WebCoreFloatToSkScalar(points[0].y()));
1655    for (size_t i = 1; i < numPoints; ++i) {
1656        path->lineTo(WebCoreFloatToSkScalar(points[i].x()),
1657                     WebCoreFloatToSkScalar(points[i].y()));
1658    }
1659
1660    /*  The code used to just blindly call this
1661            path->setIsConvex(true);
1662        But webkit can sometimes send us non-convex 4-point values, so we mark the path's
1663        convexity as unknown, so it will get computed by skia at draw time.
1664        See crbug.com 108605
1665    */
1666    SkPath::Convexity convexity = SkPath::kConvex_Convexity;
1667    if (numPoints == 4)
1668        convexity = SkPath::kUnknown_Convexity;
1669    path->setConvexity(convexity);
1670}
1671
1672void GraphicsContext::drawOuterPath(const SkPath& path, SkPaint& paint, int width)
1673{
1674#if OS(MACOSX)
1675    paint.setAlpha(64);
1676    paint.setStrokeWidth(width);
1677    paint.setPathEffect(SkCornerPathEffect::Create((width - 1) * 0.5f))->unref();
1678#else
1679    paint.setStrokeWidth(1);
1680    paint.setPathEffect(SkCornerPathEffect::Create(1))->unref();
1681#endif
1682    drawPath(path, paint);
1683}
1684
1685void GraphicsContext::drawInnerPath(const SkPath& path, SkPaint& paint, int width)
1686{
1687#if OS(MACOSX)
1688    paint.setAlpha(128);
1689    paint.setStrokeWidth(width * 0.5f);
1690    drawPath(path, paint);
1691#endif
1692}
1693
1694void GraphicsContext::setRadii(SkVector* radii, IntSize topLeft, IntSize topRight, IntSize bottomRight, IntSize bottomLeft)
1695{
1696    radii[SkRRect::kUpperLeft_Corner].set(SkIntToScalar(topLeft.width()),
1697        SkIntToScalar(topLeft.height()));
1698    radii[SkRRect::kUpperRight_Corner].set(SkIntToScalar(topRight.width()),
1699        SkIntToScalar(topRight.height()));
1700    radii[SkRRect::kLowerRight_Corner].set(SkIntToScalar(bottomRight.width()),
1701        SkIntToScalar(bottomRight.height()));
1702    radii[SkRRect::kLowerLeft_Corner].set(SkIntToScalar(bottomLeft.width()),
1703        SkIntToScalar(bottomLeft.height()));
1704}
1705
1706PassRefPtr<SkColorFilter> GraphicsContext::WebCoreColorFilterToSkiaColorFilter(ColorFilter colorFilter)
1707{
1708    switch (colorFilter) {
1709    case ColorFilterLuminanceToAlpha:
1710        return adoptRef(SkLumaColorFilter::Create());
1711    case ColorFilterLinearRGBToSRGB:
1712        return ImageBuffer::createColorSpaceFilter(ColorSpaceLinearRGB, ColorSpaceDeviceRGB);
1713    case ColorFilterSRGBToLinearRGB:
1714        return ImageBuffer::createColorSpaceFilter(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
1715    case ColorFilterNone:
1716        break;
1717    default:
1718        ASSERT_NOT_REACHED();
1719        break;
1720    }
1721
1722    return nullptr;
1723}
1724
1725#if !OS(MACOSX)
1726void GraphicsContext::draw2xMarker(SkBitmap* bitmap, int index)
1727{
1728    const SkPMColor lineColor = lineColors(index);
1729    const SkPMColor antiColor1 = antiColors1(index);
1730    const SkPMColor antiColor2 = antiColors2(index);
1731
1732    uint32_t* row1 = bitmap->getAddr32(0, 0);
1733    uint32_t* row2 = bitmap->getAddr32(0, 1);
1734    uint32_t* row3 = bitmap->getAddr32(0, 2);
1735    uint32_t* row4 = bitmap->getAddr32(0, 3);
1736
1737    // Pattern: X0o   o0X0o   o0
1738    //          XX0o o0XXX0o o0X
1739    //           o0XXX0o o0XXX0o
1740    //            o0X0o   o0X0o
1741    const SkPMColor row1Color[] = { lineColor, antiColor1, antiColor2, 0,          0,         0,          antiColor2, antiColor1 };
1742    const SkPMColor row2Color[] = { lineColor, lineColor,  antiColor1, antiColor2, 0,         antiColor2, antiColor1, lineColor };
1743    const SkPMColor row3Color[] = { 0,         antiColor2, antiColor1, lineColor,  lineColor, lineColor,  antiColor1, antiColor2 };
1744    const SkPMColor row4Color[] = { 0,         0,          antiColor2, antiColor1, lineColor, antiColor1, antiColor2, 0 };
1745
1746    for (int x = 0; x < bitmap->width() + 8; x += 8) {
1747        int count = std::min(bitmap->width() - x, 8);
1748        if (count > 0) {
1749            memcpy(row1 + x, row1Color, count * sizeof(SkPMColor));
1750            memcpy(row2 + x, row2Color, count * sizeof(SkPMColor));
1751            memcpy(row3 + x, row3Color, count * sizeof(SkPMColor));
1752            memcpy(row4 + x, row4Color, count * sizeof(SkPMColor));
1753        }
1754    }
1755}
1756
1757void GraphicsContext::draw1xMarker(SkBitmap* bitmap, int index)
1758{
1759    const uint32_t lineColor = lineColors(index);
1760    const uint32_t antiColor = antiColors2(index);
1761
1762    // Pattern: X o   o X o   o X
1763    //            o X o   o X o
1764    uint32_t* row1 = bitmap->getAddr32(0, 0);
1765    uint32_t* row2 = bitmap->getAddr32(0, 1);
1766    for (int x = 0; x < bitmap->width(); x++) {
1767        switch (x % 4) {
1768        case 0:
1769            row1[x] = lineColor;
1770            break;
1771        case 1:
1772            row1[x] = antiColor;
1773            row2[x] = antiColor;
1774            break;
1775        case 2:
1776            row2[x] = lineColor;
1777            break;
1778        case 3:
1779            row1[x] = antiColor;
1780            row2[x] = antiColor;
1781            break;
1782        }
1783    }
1784}
1785
1786const SkPMColor GraphicsContext::lineColors(int index)
1787{
1788    static const SkPMColor colors[] = {
1789        SkPreMultiplyARGB(0xFF, 0xFF, 0x00, 0x00), // Opaque red.
1790        SkPreMultiplyARGB(0xFF, 0xC0, 0xC0, 0xC0) // Opaque gray.
1791    };
1792
1793    return colors[index];
1794}
1795
1796const SkPMColor GraphicsContext::antiColors1(int index)
1797{
1798    static const SkPMColor colors[] = {
1799        SkPreMultiplyARGB(0xB0, 0xFF, 0x00, 0x00), // Semitransparent red.
1800        SkPreMultiplyARGB(0xB0, 0xC0, 0xC0, 0xC0)  // Semitransparent gray.
1801    };
1802
1803    return colors[index];
1804}
1805
1806const SkPMColor GraphicsContext::antiColors2(int index)
1807{
1808    static const SkPMColor colors[] = {
1809        SkPreMultiplyARGB(0x60, 0xFF, 0x00, 0x00), // More transparent red
1810        SkPreMultiplyARGB(0x60, 0xC0, 0xC0, 0xC0)  // More transparent gray
1811    };
1812
1813    return colors[index];
1814}
1815#endif
1816
1817void GraphicsContext::didDrawTextInRect(const SkRect& textRect)
1818{
1819    if (m_trackTextRegion) {
1820        TRACE_EVENT0("skia", "GraphicsContext::didDrawTextInRect");
1821        m_textRegion.join(textRect);
1822    }
1823}
1824
1825}
1826