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