Home | History | Annotate | Download | only in graphics
      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 
     56 using namespace std;
     57 using blink::WebBlendMode;
     58 
     59 namespace WebCore {
     60 
     61 namespace {
     62 
     63 class CompatibleImageBufferSurface : public ImageBufferSurface {
     64     WTF_MAKE_NONCOPYABLE(CompatibleImageBufferSurface); WTF_MAKE_FAST_ALLOCATED;
     65 public:
     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 
     86 private:
     87     RefPtr<SkSurface> m_surface;
     88 };
     89 
     90 } // unnamed namespace
     91 
     92 struct 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 
    100 struct 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 
    113 GraphicsContext::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 
    143 GraphicsContext::~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 
    157 void 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 
    168 void 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 
    191 void 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 
    203 void GraphicsContext::restoreLayer()
    204 {
    205     if (contextDisabled())
    206         return;
    207 
    208     m_canvas->restore();
    209     if (m_trackOpaqueRegion)
    210         m_opaqueRegion.popCanvasLayer(this);
    211 }
    212 
    213 void 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 
    234 void 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 
    247 void 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 
    260 void 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 
    273 void 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 
    287 void 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 
    301 void 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 
    319 void GraphicsContext::setDrawLooper(PassOwnPtr<DrawLooperBuilder> drawLooperBuilder)
    320 {
    321     if (contextDisabled())
    322         return;
    323 
    324     mutableState()->setDrawLooper(drawLooperBuilder->detachDrawLooper());
    325 }
    326 
    327 void GraphicsContext::clearDrawLooper()
    328 {
    329     if (contextDisabled())
    330         return;
    331 
    332     mutableState()->clearDrawLooper();
    333 }
    334 
    335 bool GraphicsContext::hasShadow() const
    336 {
    337     return !!immutableState()->drawLooper();
    338 }
    339 
    340 bool 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 
    352 SkMatrix 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 
    367 void GraphicsContext::adjustTextRenderMode(SkPaint* paint)
    368 {
    369     if (contextDisabled())
    370         return;
    371 
    372     if (!paint->isLCDRenderText())
    373         return;
    374 
    375     paint->setLCDRenderText(couldUseLCDRenderedText());
    376 }
    377 
    378 bool 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 
    390 void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation, WebBlendMode blendMode)
    391 {
    392     if (contextDisabled())
    393         return;
    394     mutableState()->setCompositeOperation(compositeOperation, blendMode);
    395 }
    396 
    397 SkColorFilter* GraphicsContext::colorFilter()
    398 {
    399     return immutableState()->colorFilter();
    400 }
    401 
    402 void 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 
    412 bool 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 
    420 void GraphicsContext::setMatrix(const SkMatrix& matrix)
    421 {
    422     if (contextDisabled())
    423         return;
    424 
    425     realizeCanvasSave();
    426 
    427     m_canvas->setMatrix(matrix);
    428 }
    429 
    430 void 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 
    443 void GraphicsContext::beginTransparencyLayer(float opacity, const FloatRect* bounds)
    444 {
    445     beginLayer(opacity, immutableState()->compositeOperator(), bounds);
    446 }
    447 
    448 void 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 
    471 void 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 
    484 void 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 
    507 PassRefPtr<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 
    523 bool GraphicsContext::isRecording() const
    524 {
    525     return !m_recordingStateStack.isEmpty();
    526 }
    527 
    528 void 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 
    548 void 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 
    567 void 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 
    582 void 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 
    610 static 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 
    624 void 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 
    677 void 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 
    727 void 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 
    856 void 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.
    896 void 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 
    923 void 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 
    931 void 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 
    939 void 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 
    979 void 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 
    987 void 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 
    994 void 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 
   1001 void 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 
   1006 void GraphicsContext::drawImage(Image* image, const FloatRect& dest)
   1007 {
   1008     if (!image)
   1009         return;
   1010     drawImage(image, dest, FloatRect(IntRect(IntPoint(), image->size())));
   1011 }
   1012 
   1013 void 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 
   1020 void 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 
   1027 void 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 
   1042 void 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 
   1051 void 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 
   1072 void 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 
   1084 void 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 
   1097 void 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 
   1112 void 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 
   1123 void 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 
   1134 void 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 
   1145 void 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 
   1154 void 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 
   1168 void 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 
   1186 void GraphicsContext::fillRect(const FloatRect& rect)
   1187 {
   1188     if (contextDisabled())
   1189         return;
   1190 
   1191     SkRect r = rect;
   1192 
   1193     drawRect(r, immutableState()->fillPaint());
   1194 }
   1195 
   1196 void 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 
   1207 void 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 
   1231 void 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 
   1237 void 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 
   1269 void GraphicsContext::fillEllipse(const FloatRect& ellipse)
   1270 {
   1271     if (contextDisabled())
   1272         return;
   1273 
   1274     SkRect rect = ellipse;
   1275     drawOval(rect, immutableState()->fillPaint());
   1276 }
   1277 
   1278 void 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 
   1287 void GraphicsContext::strokeRect(const FloatRect& rect)
   1288 {
   1289     strokeRect(rect, strokeThickness());
   1290 }
   1291 
   1292 void 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 
   1320 void GraphicsContext::strokeEllipse(const FloatRect& ellipse)
   1321 {
   1322     if (contextDisabled())
   1323         return;
   1324 
   1325     drawOval(ellipse, immutableState()->strokePaint());
   1326 }
   1327 
   1328 void 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 
   1348 void 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 
   1360 void 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 
   1376 void 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 
   1389 void GraphicsContext::clipOutRoundedRect(const RoundedRect& rect)
   1390 {
   1391     if (contextDisabled())
   1392         return;
   1393 
   1394     clipRoundedRect(rect, SkRegion::kDifference_Op);
   1395 }
   1396 
   1397 void 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 
   1413 void 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 
   1423 void 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 
   1433 void 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 
   1443 void GraphicsContext::beginCull(const FloatRect& rect)
   1444 {
   1445     if (contextDisabled())
   1446         return;
   1447 
   1448     realizeCanvasSave();
   1449     m_canvas->pushCull(rect);
   1450 }
   1451 
   1452 void GraphicsContext::endCull()
   1453 {
   1454     if (contextDisabled())
   1455         return;
   1456 
   1457     realizeCanvasSave();
   1458 
   1459     m_canvas->popCull();
   1460 }
   1461 
   1462 void 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 
   1472 void 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 
   1485 void 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 
   1498 void 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 
   1507 void 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 
   1516 void 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 
   1525 AffineTransform 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 
   1539 void 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 
   1550 void 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 
   1561 void 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 
   1586 void 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 
   1597 void 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 
   1626 PassOwnPtr<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 
   1650 void 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 
   1672 void 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 
   1685 void 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 
   1694 void 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 
   1706 PassRefPtr<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)
   1726 void 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 
   1757 void 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 
   1786 const 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 
   1796 const 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 
   1806 const 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 
   1817 void 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