Home | History | Annotate | Download | only in android
      1 /*
      2  * Copyright 2006, The Android Open Source Project
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *  * Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  *  * Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
     14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 #include "config.h"
     26 #include "GraphicsContext.h"
     27 
     28 #include "AffineTransform.h"
     29 #include "Gradient.h"
     30 #include "NotImplemented.h"
     31 #include "Path.h"
     32 #include "Pattern.h"
     33 #include "PlatformGraphicsContext.h"
     34 #include "SkBitmapRef.h"
     35 #include "SkBlurDrawLooper.h"
     36 #include "SkBlurMaskFilter.h"
     37 #include "SkCanvas.h"
     38 #include "SkColorPriv.h"
     39 #include "SkDashPathEffect.h"
     40 #include "SkDevice.h"
     41 #include "SkGradientShader.h"
     42 #include "SkPaint.h"
     43 #include "SkString.h"
     44 #include "SkiaUtils.h"
     45 #include "TransformationMatrix.h"
     46 #include "android_graphics.h"
     47 
     48 using namespace std;
     49 
     50 #define GC2CANVAS(ctx)  (ctx)->m_data->getPlatformGfxCtx()->mCanvas
     51 
     52 namespace WebCore {
     53 
     54 static int RoundToInt(float x)
     55 {
     56     return (int)roundf(x);
     57 }
     58 
     59 template <typename T> T* deepCopyPtr(const T* src)
     60 {
     61     return src ? new T(*src) : 0;
     62 }
     63 
     64 // Set a bitmap shader that mimics dashing by width-on, width-off.
     65 // Returns false if it could not succeed (e.g. there was an existing shader)
     66 static bool setBitmapDash(SkPaint* paint, int width) {
     67     if (width <= 0 || paint->getShader())
     68         return false;
     69 
     70     SkColor c = paint->getColor();
     71 
     72     SkBitmap bm;
     73     bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1);
     74     bm.allocPixels();
     75     bm.lockPixels();
     76 
     77     // set the ON pixel
     78     *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c),
     79                                             SkColorGetG(c), SkColorGetB(c));
     80     // set the OFF pixel
     81     *bm.getAddr32(1, 0) = 0;
     82     bm.unlockPixels();
     83 
     84     SkMatrix matrix;
     85     matrix.setScale(SkIntToScalar(width), SK_Scalar1);
     86 
     87     SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
     88                                                SkShader::kClamp_TileMode);
     89     s->setLocalMatrix(matrix);
     90 
     91     paint->setShader(s)->unref();
     92     return true;
     93 }
     94 
     95 // TODO / questions
     96 
     97 // alpha: how does this interact with the alpha in Color? multiply them together?
     98 // mode: do I always respect this? If so, then
     99 // the rgb() & 0xFF000000 check will abort drawing too often
    100 // Is Color premultiplied or not? If it is, then I can't blindly pass it to paint.setColor()
    101 
    102 struct ShadowRec {
    103     SkScalar blur;
    104     SkScalar dx;
    105     SkScalar dy;
    106     SkColor color;  // alpha>0 means valid shadow
    107     ShadowRec(SkScalar b = 0,
    108               SkScalar x = 0,
    109               SkScalar y = 0,
    110               SkColor c = 0) // by default, alpha=0, so no shadow
    111             : blur(b), dx(x), dy(y), color(c)
    112         {};
    113 };
    114 
    115 class GraphicsContextPlatformPrivate {
    116 public:
    117     struct State {
    118         SkPathEffect* pathEffect;
    119         float miterLimit;
    120         float alpha;
    121         float strokeThickness;
    122         SkPaint::Cap lineCap;
    123         SkPaint::Join lineJoin;
    124         SkXfermode::Mode mode;
    125         int dashRatio; // Ratio of the length of a dash to its width
    126         ShadowRec shadow;
    127         SkColor fillColor;
    128         SkColor strokeColor;
    129         bool useAA;
    130         // This is a list of clipping paths which are currently active, in the
    131         // order in which they were pushed.
    132         WTF::Vector<SkPath> antiAliasClipPaths;
    133 
    134         State()
    135             : pathEffect(0)
    136             , miterLimit(4)
    137             , alpha(1)
    138             , strokeThickness(0) // Same as default in GraphicsContextPrivate.h
    139             , lineCap(SkPaint::kDefault_Cap)
    140             , lineJoin(SkPaint::kDefault_Join)
    141             , mode(SkXfermode::kSrcOver_Mode)
    142             , dashRatio(3)
    143             , fillColor(SK_ColorBLACK)
    144             , strokeColor(SK_ColorBLACK)
    145             , useAA(true)
    146         {
    147         }
    148 
    149         State(const State& other)
    150             : pathEffect(other.pathEffect)
    151             , miterLimit(other.miterLimit)
    152             , alpha(other.alpha)
    153             , strokeThickness(other.strokeThickness)
    154             , lineCap(other.lineCap)
    155             , lineJoin(other.lineJoin)
    156             , mode(other.mode)
    157             , dashRatio(other.dashRatio)
    158             , shadow(other.shadow)
    159             , fillColor(other.fillColor)
    160             , strokeColor(other.strokeColor)
    161             , useAA(other.useAA)
    162         {
    163             SkSafeRef(pathEffect);
    164         }
    165 
    166         ~State()
    167         {
    168             SkSafeUnref(pathEffect);
    169         }
    170 
    171         void setShadow(int radius, int dx, int dy, SkColor c)
    172         {
    173             // Cut the radius in half, to visually match the effect seen in
    174             // safari browser
    175             shadow.blur = SkScalarHalf(SkIntToScalar(radius));
    176             shadow.dx = SkIntToScalar(dx);
    177             shadow.dy = SkIntToScalar(dy);
    178             shadow.color = c;
    179         }
    180 
    181         bool setupShadowPaint(GraphicsContext* ctx, SkPaint* paint, SkPoint* offset)
    182         {
    183             paint->setAntiAlias(true);
    184             paint->setDither(true);
    185             paint->setXfermodeMode(mode);
    186             paint->setColor(shadow.color);
    187             offset->set(shadow.dx, shadow.dy);
    188 
    189             // Currently, only GraphicsContexts associated with the
    190             // HTMLCanvasElement have shadows ignore transforms set.  This
    191             // allows us to distinguish between CSS and Canvas shadows which
    192             // have different rendering specifications.
    193             uint32_t flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
    194             if (ctx->shadowsIgnoreTransforms()) {
    195                 offset->fY = -offset->fY;
    196                 flags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag;
    197             }
    198 
    199             if (shadow.blur > 0) {
    200                 paint->setMaskFilter(SkBlurMaskFilter::Create(shadow.blur,
    201                                      SkBlurMaskFilter::kNormal_BlurStyle))->unref();
    202             }
    203             return SkColorGetA(shadow.color) && (shadow.blur || shadow.dx || shadow.dy);
    204         }
    205 
    206         SkColor applyAlpha(SkColor c) const
    207         {
    208             int s = RoundToInt(alpha * 256);
    209             if (s >= 256)
    210                 return c;
    211             if (s < 0)
    212                 return 0;
    213 
    214             int a = SkAlphaMul(SkColorGetA(c), s);
    215             return (c & 0x00FFFFFF) | (a << 24);
    216         }
    217     };
    218 
    219     GraphicsContextPlatformPrivate(GraphicsContext* gfxCtx, PlatformGraphicsContext* platformGfxCtx)
    220         : m_parentGfxCtx(gfxCtx)
    221         , m_platformGfxCtx(platformGfxCtx)
    222         , m_stateStack(sizeof(State))
    223     {
    224         State* state = static_cast<State*>(m_stateStack.push_back());
    225         new (state) State();
    226         m_state = state;
    227     }
    228 
    229     ~GraphicsContextPlatformPrivate()
    230     {
    231         // We force restores so we don't leak any subobjects owned by our
    232         // stack of State records.
    233         while (m_stateStack.count() > 0)
    234             this->restore();
    235 
    236         if (m_platformGfxCtx && m_platformGfxCtx->deleteUs())
    237             delete m_platformGfxCtx;
    238     }
    239 
    240     void save()
    241     {
    242         State* newState = static_cast<State*>(m_stateStack.push_back());
    243         new (newState) State(*m_state);
    244         m_state = newState;
    245     }
    246 
    247     void restore()
    248     {
    249         if (!m_state->antiAliasClipPaths.isEmpty())
    250             applyAntiAliasedClipPaths(m_state->antiAliasClipPaths);
    251 
    252         m_state->~State();
    253         m_stateStack.pop_back();
    254         m_state = static_cast<State*>(m_stateStack.back());
    255     }
    256 
    257     void setFillColor(const Color& c)
    258     {
    259         m_state->fillColor = c.rgb();
    260     }
    261 
    262     void setStrokeColor(const Color& c)
    263     {
    264         m_state->strokeColor = c.rgb();
    265     }
    266 
    267     void setStrokeThickness(float f)
    268     {
    269         m_state->strokeThickness = f;
    270     }
    271 
    272     void setupPaintCommon(SkPaint* paint) const
    273     {
    274         paint->setAntiAlias(m_state->useAA);
    275         paint->setDither(true);
    276         paint->setXfermodeMode(m_state->mode);
    277         if (SkColorGetA(m_state->shadow.color) > 0) {
    278 
    279             // Currently, only GraphicsContexts associated with the
    280             // HTMLCanvasElement have shadows ignore transforms set.  This
    281             // allows us to distinguish between CSS and Canvas shadows which
    282             // have different rendering specifications.
    283             SkScalar dy = m_state->shadow.dy;
    284             uint32_t flags = SkBlurDrawLooper::kHighQuality_BlurFlag;
    285             if (m_parentGfxCtx->shadowsIgnoreTransforms()) {
    286                 dy = -dy;
    287                 flags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag;
    288                 flags |= SkBlurDrawLooper::kOverrideColor_BlurFlag;
    289             }
    290 
    291             SkDrawLooper* looper = new SkBlurDrawLooper(m_state->shadow.blur,
    292                                                         m_state->shadow.dx,
    293                                                         dy,
    294                                                         m_state->shadow.color,
    295                                                         flags);
    296             paint->setLooper(looper)->unref();
    297         }
    298     }
    299 
    300     void setupPaintFill(SkPaint* paint) const
    301     {
    302         this->setupPaintCommon(paint);
    303         paint->setColor(m_state->applyAlpha(m_state->fillColor));
    304     }
    305 
    306     void setupPaintBitmap(SkPaint* paint) const
    307     {
    308         this->setupPaintCommon(paint);
    309         // We only want the global alpha for bitmaps,
    310         // so just give applyAlpha opaque black
    311         paint->setColor(m_state->applyAlpha(0xFF000000));
    312     }
    313 
    314     // Sets up the paint for stroking. Returns true if the style is really
    315     // just a dash of squares (the size of the paint's stroke-width.
    316     bool setupPaintStroke(SkPaint* paint, SkRect* rect, bool isHLine = false)
    317     {
    318         this->setupPaintCommon(paint);
    319         paint->setColor(m_state->applyAlpha(m_state->strokeColor));
    320 
    321         float width = m_state->strokeThickness;
    322 
    323         // This allows dashing and dotting to work properly for hairline strokes
    324         // FIXME: Should we only do this for dashed and dotted strokes?
    325         if (!width)
    326             width = 1;
    327 
    328         paint->setStyle(SkPaint::kStroke_Style);
    329         paint->setStrokeWidth(SkFloatToScalar(width));
    330         paint->setStrokeCap(m_state->lineCap);
    331         paint->setStrokeJoin(m_state->lineJoin);
    332         paint->setStrokeMiter(SkFloatToScalar(m_state->miterLimit));
    333 
    334         if (rect && (RoundToInt(width) & 1))
    335             rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
    336 
    337         SkPathEffect* pe = m_state->pathEffect;
    338         if (pe) {
    339             paint->setPathEffect(pe);
    340             return false;
    341         }
    342         switch (m_parentGfxCtx->strokeStyle()) {
    343         case NoStroke:
    344         case SolidStroke:
    345             width = 0;
    346             break;
    347         case DashedStroke:
    348             width = m_state->dashRatio * width;
    349             break;
    350             // No break
    351         case DottedStroke:
    352             break;
    353         }
    354 
    355         if (width > 0) {
    356             // Return true if we're basically a dotted dash of squares
    357             bool justSqrs = RoundToInt(width) == RoundToInt(paint->getStrokeWidth());
    358 
    359             if (justSqrs || !isHLine || !setBitmapDash(paint, width)) {
    360 #if 0
    361                 // this is slow enough that we just skip it for now
    362                 // see http://b/issue?id=4163023
    363                 SkScalar intervals[] = { width, width };
    364                 pe = new SkDashPathEffect(intervals, 2, 0);
    365                 paint->setPathEffect(pe)->unref();
    366 #endif
    367             }
    368             return justSqrs;
    369         }
    370         return false;
    371     }
    372 
    373     void clipPathAntiAliased(const SkPath& clipPath)
    374     {
    375         // If we are currently tracking any anti-alias clip paths, then we already
    376         // have a layer in place and don't need to add another.
    377         bool haveLayerOutstanding = m_state->antiAliasClipPaths.size();
    378 
    379         // See comments in applyAntiAliasedClipPaths about how this works.
    380         m_state->antiAliasClipPaths.append(clipPath);
    381         if (!haveLayerOutstanding) {
    382             SkRect bounds = clipPath.getBounds();
    383             if (m_platformGfxCtx && m_platformGfxCtx->mCanvas) {
    384                 m_platformGfxCtx->mCanvas->saveLayerAlpha(&bounds, 255,
    385                     static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag
    386                         | SkCanvas::kFullColorLayer_SaveFlag
    387                         | SkCanvas::kClipToLayer_SaveFlag));
    388                 m_platformGfxCtx->mCanvas->save();
    389             } else
    390                 ASSERT(0);
    391         }
    392     }
    393 
    394     void applyAntiAliasedClipPaths(WTF::Vector<SkPath>& paths)
    395     {
    396         // Anti-aliased clipping:
    397         //
    398         // Refer to PlatformContextSkia.cpp's applyAntiAliasedClipPaths() for more details
    399 
    400         if (m_platformGfxCtx && m_platformGfxCtx->mCanvas)
    401             m_platformGfxCtx->mCanvas->restore();
    402 
    403         SkPaint paint;
    404         paint.setXfermodeMode(SkXfermode::kClear_Mode);
    405         paint.setAntiAlias(true);
    406         paint.setStyle(SkPaint::kFill_Style);
    407 
    408         if (m_platformGfxCtx && m_platformGfxCtx->mCanvas)  {
    409             for (size_t i = paths.size() - 1; i < paths.size(); --i) {
    410                 paths[i].setFillType(SkPath::kInverseWinding_FillType);
    411                 m_platformGfxCtx->mCanvas->drawPath(paths[i], paint);
    412             }
    413             m_platformGfxCtx->mCanvas->restore();
    414         } else
    415             ASSERT(0);
    416     }
    417 
    418     PlatformGraphicsContext* getPlatformGfxCtx()
    419     {
    420         return m_platformGfxCtx;
    421     }
    422 
    423     State* getState()
    424     {
    425         return m_state;
    426     }
    427 private:
    428     State* m_state;
    429     GraphicsContext* m_parentGfxCtx; // Back-ptr to our parent
    430     PlatformGraphicsContext* m_platformGfxCtx;
    431     SkDeque m_stateStack;
    432     // Not supported yet
    433     State& operator=(const State&);
    434 };
    435 
    436 static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm)
    437 {
    438     SkShader::TileMode mode = SkShader::kClamp_TileMode;
    439 
    440     switch (sm) {
    441     case SpreadMethodPad:
    442         mode = SkShader::kClamp_TileMode;
    443         break;
    444     case SpreadMethodReflect:
    445         mode = SkShader::kMirror_TileMode;
    446         break;
    447     case SpreadMethodRepeat:
    448         mode = SkShader::kRepeat_TileMode;
    449         break;
    450     }
    451     return mode;
    452 }
    453 
    454 static void extactShader(SkPaint* paint, Pattern* pat, Gradient* grad)
    455 {
    456     if (pat) {
    457         // platformPattern() returns a cached obj
    458         paint->setShader(pat->platformPattern(AffineTransform()));
    459     } else if (grad) {
    460         // grad->getShader() returns a cached obj
    461         GradientSpreadMethod sm = grad->spreadMethod();
    462         paint->setShader(grad->getShader(SpreadMethod2TileMode(sm)));
    463     }
    464 }
    465 
    466 ////////////////////////////////////////////////////////////////////////////////////////////////
    467 
    468 GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
    469 {
    470     PlatformGraphicsContext* pgc = new PlatformGraphicsContext();
    471 
    472     SkBitmap bitmap;
    473 
    474     bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
    475     bitmap.allocPixels();
    476     bitmap.eraseColor(0);
    477     pgc->mCanvas->setBitmapDevice(bitmap);
    478 
    479     GraphicsContext* ctx = new GraphicsContext(pgc);
    480     return ctx;
    481 }
    482 
    483 ////////////////////////////////////////////////////////////////////////////////////////////////
    484 
    485 void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
    486 {
    487     m_data = new GraphicsContextPlatformPrivate(this, gc);
    488     setPaintingDisabled(!gc || !gc->mCanvas);
    489 }
    490 
    491 void GraphicsContext::platformDestroy()
    492 {
    493     delete m_data;
    494 }
    495 
    496 void GraphicsContext::savePlatformState()
    497 {
    498     // Save our private State
    499     m_data->save();
    500     // Save our native canvas
    501     GC2CANVAS(this)->save();
    502 }
    503 
    504 void GraphicsContext::restorePlatformState()
    505 {
    506     // Restore our private State
    507     m_data->restore();
    508     // Restore our native canvas
    509     GC2CANVAS(this)->restore();
    510 }
    511 
    512 bool GraphicsContext::willFill() const
    513 {
    514     return m_data->getState()->fillColor;
    515 }
    516 
    517 bool GraphicsContext::willStroke() const
    518 {
    519     return m_data->getState()->strokeColor;
    520 }
    521 
    522 // Draws a filled rectangle with a stroked border.
    523 void GraphicsContext::drawRect(const IntRect& rect)
    524 {
    525     if (paintingDisabled())
    526         return;
    527 
    528     SkPaint paint;
    529     SkRect r(rect);
    530 
    531     if (fillColor().alpha()) {
    532         m_data->setupPaintFill(&paint);
    533         GC2CANVAS(this)->drawRect(r, paint);
    534     }
    535 
    536     // According to GraphicsContext.h, stroking inside drawRect always means
    537     // a stroke of 1 inside the rect.
    538     if (strokeStyle() != NoStroke && strokeColor().alpha()) {
    539         paint.reset();
    540         m_data->setupPaintStroke(&paint, &r);
    541         paint.setPathEffect(0); // No dashing please
    542         paint.setStrokeWidth(SK_Scalar1); // Always just 1.0 width
    543         r.inset(SK_ScalarHalf, SK_ScalarHalf); // Ensure we're "inside"
    544         GC2CANVAS(this)->drawRect(r, paint);
    545     }
    546 }
    547 
    548 // This is only used to draw borders.
    549 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
    550 {
    551     if (paintingDisabled())
    552         return;
    553 
    554     StrokeStyle style = strokeStyle();
    555     if (style == NoStroke)
    556         return;
    557 
    558     SkPaint paint;
    559     SkCanvas* canvas = GC2CANVAS(this);
    560     const int idx = SkAbs32(point2.x() - point1.x());
    561     const int idy = SkAbs32(point2.y() - point1.y());
    562 
    563     // Special-case horizontal and vertical lines that are really just dots
    564     if (m_data->setupPaintStroke(&paint, 0, !idy) && (!idx || !idy)) {
    565         const SkScalar diameter = paint.getStrokeWidth();
    566         const SkScalar radius = SkScalarHalf(diameter);
    567         SkScalar x = SkIntToScalar(SkMin32(point1.x(), point2.x()));
    568         SkScalar y = SkIntToScalar(SkMin32(point1.y(), point2.y()));
    569         SkScalar dx, dy;
    570         int count;
    571         SkRect bounds;
    572 
    573         if (!idy) { // Horizontal
    574             bounds.set(x, y - radius, x + SkIntToScalar(idx), y + radius);
    575             x += radius;
    576             dx = diameter * 2;
    577             dy = 0;
    578             count = idx;
    579         } else { // Vertical
    580             bounds.set(x - radius, y, x + radius, y + SkIntToScalar(idy));
    581             y += radius;
    582             dx = 0;
    583             dy = diameter * 2;
    584             count = idy;
    585         }
    586 
    587         // The actual count is the number of ONs we hit alternating
    588         // ON(diameter), OFF(diameter), ...
    589         {
    590             SkScalar width = SkScalarDiv(SkIntToScalar(count), diameter);
    591             // Now compute the number of cells (ON and OFF)
    592             count = SkScalarRound(width);
    593             // Now compute the number of ONs
    594             count = (count + 1) >> 1;
    595         }
    596 
    597         SkAutoMalloc storage(count * sizeof(SkPoint));
    598         SkPoint* verts = (SkPoint*)storage.get();
    599         // Now build the array of vertices to past to drawPoints
    600         for (int i = 0; i < count; i++) {
    601             verts[i].set(x, y);
    602             x += dx;
    603             y += dy;
    604         }
    605 
    606         paint.setStyle(SkPaint::kFill_Style);
    607         paint.setPathEffect(0);
    608 
    609         // Clipping to bounds is not required for correctness, but it does
    610         // allow us to reject the entire array of points if we are completely
    611         // offscreen. This is common in a webpage for android, where most of
    612         // the content is clipped out. If drawPoints took an (optional) bounds
    613         // parameter, that might even be better, as we would *just* use it for
    614         // culling, and not both wacking the canvas' save/restore stack.
    615         canvas->save(SkCanvas::kClip_SaveFlag);
    616         canvas->clipRect(bounds);
    617         canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint);
    618         canvas->restore();
    619     } else {
    620         SkPoint pts[2] = { point1, point2 };
    621         canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
    622     }
    623 }
    624 
    625 static void setrectForUnderline(SkRect* r, GraphicsContext* context, const FloatPoint& point, int yOffset, float width)
    626 {
    627     float lineThickness = context->strokeThickness();
    628 #if 0
    629     if (lineThickness < 1) // Do we really need/want this?
    630         lineThickness = 1;
    631 #endif
    632     r->fLeft    = point.x();
    633     r->fTop     = point.y() + yOffset;
    634     r->fRight   = r->fLeft + width;
    635     r->fBottom  = r->fTop + lineThickness;
    636 }
    637 
    638 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool)
    639 {
    640     if (paintingDisabled())
    641         return;
    642 
    643     SkRect r;
    644     setrectForUnderline(&r, this, pt, 0, width);
    645 
    646     SkPaint paint;
    647     paint.setAntiAlias(true);
    648     paint.setColor(this->strokeColor().rgb());
    649 
    650     GC2CANVAS(this)->drawRect(r, paint);
    651 }
    652 
    653 // TODO: Should we draw different based on TextCheckingLineStyle?
    654 void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width, TextCheckingLineStyle)
    655 {
    656     if (paintingDisabled())
    657         return;
    658 
    659     SkRect r;
    660     setrectForUnderline(&r, this, pt, 0, width);
    661 
    662     SkPaint paint;
    663     paint.setAntiAlias(true);
    664     paint.setColor(SK_ColorRED); // Is this specified somewhere?
    665 
    666     GC2CANVAS(this)->drawRect(r, paint);
    667 }
    668 
    669 // This method is only used to draw the little circles used in lists.
    670 void GraphicsContext::drawEllipse(const IntRect& rect)
    671 {
    672     if (paintingDisabled())
    673         return;
    674 
    675     SkPaint paint;
    676     SkRect oval(rect);
    677 
    678     if (fillColor().rgb() & 0xFF000000) {
    679         m_data->setupPaintFill(&paint);
    680         GC2CANVAS(this)->drawOval(oval, paint);
    681     }
    682     if (strokeStyle() != NoStroke) {
    683         paint.reset();
    684         m_data->setupPaintStroke(&paint, &oval);
    685         GC2CANVAS(this)->drawOval(oval, paint);
    686     }
    687 }
    688 
    689 static inline int fastMod(int value, int max)
    690 {
    691     int sign = SkExtractSign(value);
    692 
    693     value = SkApplySign(value, sign);
    694     if (value >= max)
    695         value %= max;
    696     return SkApplySign(value, sign);
    697 }
    698 
    699 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
    700 {
    701     if (paintingDisabled())
    702         return;
    703 
    704     SkPath path;
    705     SkPaint paint;
    706     SkRect oval(r);
    707 
    708     if (strokeStyle() == NoStroke) {
    709         m_data->setupPaintFill(&paint); // We want the fill color
    710         paint.setStyle(SkPaint::kStroke_Style);
    711         paint.setStrokeWidth(SkFloatToScalar(this->strokeThickness()));
    712     } else
    713         m_data->setupPaintStroke(&paint, 0);
    714 
    715     // We do this before converting to scalar, so we don't overflow SkFixed
    716     startAngle = fastMod(startAngle, 360);
    717     angleSpan = fastMod(angleSpan, 360);
    718 
    719     path.addArc(oval, SkIntToScalar(-startAngle), SkIntToScalar(-angleSpan));
    720     GC2CANVAS(this)->drawPath(path, paint);
    721 }
    722 
    723 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
    724 {
    725     if (paintingDisabled())
    726         return;
    727 
    728     if (numPoints <= 1)
    729         return;
    730 
    731     SkPaint paint;
    732     SkPath path;
    733 
    734     path.incReserve(numPoints);
    735     path.moveTo(SkFloatToScalar(points[0].x()), SkFloatToScalar(points[0].y()));
    736     for (size_t i = 1; i < numPoints; i++)
    737         path.lineTo(SkFloatToScalar(points[i].x()), SkFloatToScalar(points[i].y()));
    738 
    739     if (GC2CANVAS(this)->quickReject(path, shouldAntialias ?
    740             SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType)) {
    741         return;
    742     }
    743 
    744     if (fillColor().rgb() & 0xFF000000) {
    745         m_data->setupPaintFill(&paint);
    746         paint.setAntiAlias(shouldAntialias);
    747         GC2CANVAS(this)->drawPath(path, paint);
    748     }
    749 
    750     if (strokeStyle() != NoStroke) {
    751         paint.reset();
    752         m_data->setupPaintStroke(&paint, 0);
    753         paint.setAntiAlias(shouldAntialias);
    754         GC2CANVAS(this)->drawPath(path, paint);
    755     }
    756 }
    757 
    758 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
    759                                       const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace)
    760 {
    761     if (paintingDisabled())
    762         return;
    763 
    764     SkPaint paint;
    765     SkPath path;
    766     SkScalar radii[8];
    767 
    768     radii[0] = SkIntToScalar(topLeft.width());
    769     radii[1] = SkIntToScalar(topLeft.height());
    770     radii[2] = SkIntToScalar(topRight.width());
    771     radii[3] = SkIntToScalar(topRight.height());
    772     radii[4] = SkIntToScalar(bottomRight.width());
    773     radii[5] = SkIntToScalar(bottomRight.height());
    774     radii[6] = SkIntToScalar(bottomLeft.width());
    775     radii[7] = SkIntToScalar(bottomLeft.height());
    776     path.addRoundRect(rect, radii);
    777 
    778     m_data->setupPaintFill(&paint);
    779     paint.setColor(color.rgb());
    780     GC2CANVAS(this)->drawPath(path, paint);
    781 }
    782 
    783 void GraphicsContext::fillRect(const FloatRect& rect)
    784 {
    785     SkPaint paint;
    786 
    787     m_data->setupPaintFill(&paint);
    788 
    789     extactShader(&paint,
    790                  m_state.fillPattern.get(),
    791                  m_state.fillGradient.get());
    792 
    793     GC2CANVAS(this)->drawRect(rect, paint);
    794 }
    795 
    796 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
    797 {
    798     if (paintingDisabled())
    799         return;
    800 
    801     if (color.rgb() & 0xFF000000) {
    802         SkPaint paint;
    803 
    804         m_data->setupPaintCommon(&paint);
    805         paint.setColor(color.rgb()); // Punch in the specified color
    806         paint.setShader(0); // In case we had one set
    807 
    808         // Sometimes we record and draw portions of the page, using clips
    809         // for each portion. The problem with this is that webkit, sometimes,
    810         // sees that we're only recording a portion, and they adjust some of
    811         // their rectangle coordinates accordingly (e.g.
    812         // RenderBoxModelObject::paintFillLayerExtended() which calls
    813         // rect.intersect(paintInfo.rect) and then draws the bg with that
    814         // rect. The result is that we end up drawing rects that are meant to
    815         // seam together (one for each portion), but if the rects have
    816         // fractional coordinates (e.g. we are zoomed by a fractional amount)
    817         // we will double-draw those edges, resulting in visual cracks or
    818         // artifacts.
    819 
    820         // The fix seems to be to just turn off antialasing for rects (this
    821         // entry-point in GraphicsContext seems to have been sufficient,
    822         // though perhaps we'll find we need to do this as well in fillRect(r)
    823         // as well.) Currently setupPaintCommon() enables antialiasing.
    824 
    825         // Since we never show the page rotated at a funny angle, disabling
    826         // antialiasing seems to have no real down-side, and it does fix the
    827         // bug when we're zoomed (and drawing portions that need to seam).
    828         paint.setAntiAlias(false);
    829 
    830         GC2CANVAS(this)->drawRect(rect, paint);
    831     }
    832 }
    833 
    834 void GraphicsContext::clip(const FloatRect& rect)
    835 {
    836     if (paintingDisabled())
    837         return;
    838 
    839     GC2CANVAS(this)->clipRect(rect);
    840 }
    841 
    842 void GraphicsContext::clip(const Path& path)
    843 {
    844     if (paintingDisabled())
    845         return;
    846 
    847     m_data->clipPathAntiAliased(*path.platformPath());
    848 }
    849 
    850 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
    851 {
    852     if (paintingDisabled())
    853         return;
    854 
    855     SkPath path;
    856     SkRect r(rect);
    857 
    858     path.addOval(r, SkPath::kCW_Direction);
    859     // Only perform the inset if we won't invert r
    860     if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
    861         // Adding one to the thickness doesn't make the border too thick as
    862         // it's painted over afterwards. But without this adjustment the
    863         // border appears a little anemic after anti-aliasing.
    864         r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
    865         path.addOval(r, SkPath::kCCW_Direction);
    866     }
    867     m_data->clipPathAntiAliased(path);
    868 }
    869 
    870 void GraphicsContext::canvasClip(const Path& path)
    871 {
    872     clip(path);
    873 }
    874 
    875 void GraphicsContext::clipOut(const IntRect& r)
    876 {
    877     if (paintingDisabled())
    878         return;
    879 
    880     GC2CANVAS(this)->clipRect(r, SkRegion::kDifference_Op);
    881 }
    882 
    883 #if ENABLE(SVG)
    884 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
    885 {
    886     if (paintingDisabled())
    887         return;
    888 
    889     SkPath path = *pathToClip.platformPath();
    890     path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
    891     GC2CANVAS(this)->clipPath(path);
    892 }
    893 #endif
    894 
    895 void GraphicsContext::clipOut(const Path& p)
    896 {
    897     if (paintingDisabled())
    898         return;
    899 
    900     GC2CANVAS(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op);
    901 }
    902 
    903 //////////////////////////////////////////////////////////////////////////////////////////////////
    904 
    905 #if SVG_SUPPORT
    906 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
    907 {
    908     return new KRenderingDeviceContextQuartz(platformContext());
    909 }
    910 #endif
    911 
    912 // These are the flags we need when we call saveLayer for transparency.
    913 // Since it does not appear that webkit intends this to also save/restore
    914 // the matrix or clip, I do not give those flags (for performance)
    915 #define TRANSPARENCY_SAVEFLAGS                                  \
    916     (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag |   \
    917                           SkCanvas::kFullColorLayer_SaveFlag)
    918 
    919 void GraphicsContext::beginTransparencyLayer(float opacity)
    920 {
    921     if (paintingDisabled())
    922         return;
    923 
    924     SkCanvas* canvas = GC2CANVAS(this);
    925     canvas->saveLayerAlpha(0, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS);
    926 }
    927 
    928 void GraphicsContext::endTransparencyLayer()
    929 {
    930     if (paintingDisabled())
    931         return;
    932 
    933     GC2CANVAS(this)->restore();
    934 }
    935 
    936 ///////////////////////////////////////////////////////////////////////////
    937 
    938 void GraphicsContext::setupBitmapPaint(SkPaint* paint)
    939 {
    940     m_data->setupPaintBitmap(paint);
    941 }
    942 
    943 void GraphicsContext::setupFillPaint(SkPaint* paint)
    944 {
    945     m_data->setupPaintFill(paint);
    946 }
    947 
    948 void GraphicsContext::setupStrokePaint(SkPaint* paint)
    949 {
    950     m_data->setupPaintStroke(paint, 0);
    951 }
    952 
    953 bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset)
    954 {
    955     return m_data->getState()->setupShadowPaint(this, paint, offset);
    956 }
    957 
    958 void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace)
    959 {
    960     m_data->setStrokeColor(c);
    961 }
    962 
    963 void GraphicsContext::setPlatformStrokeThickness(float f)
    964 {
    965     m_data->setStrokeThickness(f);
    966 }
    967 
    968 void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace)
    969 {
    970     m_data->setFillColor(c);
    971 }
    972 
    973 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
    974 {
    975     if (paintingDisabled())
    976         return;
    977 
    978     if (blur <= 0)
    979         this->clearPlatformShadow();
    980 
    981     SkColor c;
    982     if (color.isValid())
    983         c = color.rgb();
    984     else
    985         c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color
    986     m_data->getState()->setShadow(blur, size.width(), size.height(), c);
    987 }
    988 
    989 void GraphicsContext::clearPlatformShadow()
    990 {
    991     if (paintingDisabled())
    992         return;
    993 
    994     m_data->getState()->setShadow(0, 0, 0, 0);
    995 }
    996 
    997 ///////////////////////////////////////////////////////////////////////////////
    998 
    999 void GraphicsContext::drawFocusRing(const Vector<IntRect>&, int, int, const Color&)
   1000 {
   1001     // Do nothing, since we draw the focus ring independently.
   1002 }
   1003 
   1004 void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
   1005 {
   1006     // Do nothing, since we draw the focus ring independently.
   1007 }
   1008 
   1009 PlatformGraphicsContext* GraphicsContext::platformContext() const
   1010 {
   1011     ASSERT(!paintingDisabled());
   1012     return m_data->getPlatformGfxCtx();
   1013 }
   1014 
   1015 void GraphicsContext::setMiterLimit(float limit)
   1016 {
   1017     m_data->getState()->miterLimit = limit;
   1018 }
   1019 
   1020 void GraphicsContext::setAlpha(float alpha)
   1021 {
   1022     m_data->getState()->alpha = alpha;
   1023 }
   1024 
   1025 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
   1026 {
   1027     m_data->getState()->mode = WebCoreCompositeToSkiaComposite(op);
   1028 }
   1029 
   1030 void GraphicsContext::clearRect(const FloatRect& rect)
   1031 {
   1032     if (paintingDisabled())
   1033         return;
   1034 
   1035     SkPaint paint;
   1036 
   1037     m_data->setupPaintFill(&paint);
   1038     paint.setXfermodeMode(SkXfermode::kClear_Mode);
   1039     GC2CANVAS(this)->drawRect(rect, paint);
   1040 }
   1041 
   1042 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
   1043 {
   1044     if (paintingDisabled())
   1045         return;
   1046 
   1047     SkPaint paint;
   1048 
   1049     m_data->setupPaintStroke(&paint, 0);
   1050     paint.setStrokeWidth(SkFloatToScalar(lineWidth));
   1051     GC2CANVAS(this)->drawRect(rect, paint);
   1052 }
   1053 
   1054 void GraphicsContext::setLineCap(LineCap cap)
   1055 {
   1056     switch (cap) {
   1057     case ButtCap:
   1058         m_data->getState()->lineCap = SkPaint::kButt_Cap;
   1059         break;
   1060     case RoundCap:
   1061         m_data->getState()->lineCap = SkPaint::kRound_Cap;
   1062         break;
   1063     case SquareCap:
   1064         m_data->getState()->lineCap = SkPaint::kSquare_Cap;
   1065         break;
   1066     default:
   1067         SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap));
   1068         break;
   1069     }
   1070 }
   1071 
   1072 #if ENABLE(SVG)
   1073 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
   1074 {
   1075     if (paintingDisabled())
   1076         return;
   1077 
   1078     size_t dashLength = dashes.size();
   1079     if (!dashLength)
   1080         return;
   1081 
   1082     size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
   1083     SkScalar* intervals = new SkScalar[count];
   1084 
   1085     for (unsigned int i = 0; i < count; i++)
   1086         intervals[i] = SkFloatToScalar(dashes[i % dashLength]);
   1087     SkPathEffect **effectPtr = &m_data->getState()->pathEffect;
   1088     SkSafeUnref(*effectPtr);
   1089     *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset));
   1090 
   1091     delete[] intervals;
   1092 }
   1093 #endif
   1094 
   1095 void GraphicsContext::setLineJoin(LineJoin join)
   1096 {
   1097     switch (join) {
   1098     case MiterJoin:
   1099         m_data->getState()->lineJoin = SkPaint::kMiter_Join;
   1100         break;
   1101     case RoundJoin:
   1102         m_data->getState()->lineJoin = SkPaint::kRound_Join;
   1103         break;
   1104     case BevelJoin:
   1105         m_data->getState()->lineJoin = SkPaint::kBevel_Join;
   1106         break;
   1107     default:
   1108         SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join));
   1109         break;
   1110     }
   1111 }
   1112 
   1113 void GraphicsContext::scale(const FloatSize& size)
   1114 {
   1115     if (paintingDisabled())
   1116         return;
   1117     GC2CANVAS(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
   1118 }
   1119 
   1120 void GraphicsContext::rotate(float angleInRadians)
   1121 {
   1122     if (paintingDisabled())
   1123         return;
   1124     GC2CANVAS(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f)));
   1125 }
   1126 
   1127 void GraphicsContext::translate(float x, float y)
   1128 {
   1129     if (paintingDisabled())
   1130         return;
   1131     GC2CANVAS(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y));
   1132 }
   1133 
   1134 void GraphicsContext::concatCTM(const AffineTransform& affine)
   1135 {
   1136     if (paintingDisabled())
   1137         return;
   1138     GC2CANVAS(this)->concat(affine);
   1139 }
   1140 
   1141 // This is intended to round the rect to device pixels (through the CTM)
   1142 // and then invert the result back into source space, with the hope that when
   1143 // it is drawn (through the matrix), it will land in the "right" place (i.e.
   1144 // on pixel boundaries).
   1145 
   1146 // For android, we record this geometry once and then draw it though various
   1147 // scale factors as the user zooms, without re-recording. Thus this routine
   1148 // should just leave the original geometry alone.
   1149 
   1150 // If we instead draw into bitmap tiles, we should then perform this
   1151 // transform -> round -> inverse step.
   1152 
   1153 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
   1154 {
   1155     return rect;
   1156 }
   1157 
   1158 //////////////////////////////////////////////////////////////////////////////////////////////////
   1159 
   1160 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
   1161 {
   1162 // Appears to be PDF specific, so we ignore it
   1163 #if 0
   1164 if (paintingDisabled())
   1165     return;
   1166 
   1167 CFURLRef urlRef = link.createCFURL();
   1168 if (urlRef) {
   1169     CGContextRef context = platformContext();
   1170 
   1171     // Get the bounding box to handle clipping.
   1172     CGRect box = CGContextGetClipBoundingBox(context);
   1173 
   1174     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
   1175     IntRect rect = destRect;
   1176     rect.intersect(intBox);
   1177 
   1178     CGPDFContextSetURLForRect(context, urlRef,
   1179         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
   1180 
   1181     CFRelease(urlRef);
   1182 }
   1183 #endif
   1184 }
   1185 
   1186 void GraphicsContext::setPlatformShouldAntialias(bool useAA)
   1187 {
   1188     if (paintingDisabled())
   1189         return;
   1190     m_data->getState()->useAA = useAA;
   1191 }
   1192 
   1193 AffineTransform GraphicsContext::getCTM() const
   1194 {
   1195     const SkMatrix& m = GC2CANVAS(this)->getTotalMatrix();
   1196     return AffineTransform(SkScalarToDouble(m.getScaleX()), // a
   1197                            SkScalarToDouble(m.getSkewY()), // b
   1198                            SkScalarToDouble(m.getSkewX()), // c
   1199                            SkScalarToDouble(m.getScaleY()), // d
   1200                            SkScalarToDouble(m.getTranslateX()), // e
   1201                            SkScalarToDouble(m.getTranslateY())); // f
   1202 }
   1203 
   1204 void GraphicsContext::setCTM(const AffineTransform& transform)
   1205 {
   1206     if (paintingDisabled())
   1207         return;
   1208 
   1209     GC2CANVAS(this)->setMatrix(transform);
   1210 }
   1211 
   1212 ///////////////////////////////////////////////////////////////////////////////
   1213 
   1214 void GraphicsContext::fillPath(const Path& pathToFill)
   1215 {
   1216     SkPath* path = pathToFill.platformPath();
   1217     if (paintingDisabled() || !path)
   1218         return;
   1219 
   1220     switch (this->fillRule()) {
   1221     case RULE_NONZERO:
   1222         path->setFillType(SkPath::kWinding_FillType);
   1223         break;
   1224     case RULE_EVENODD:
   1225         path->setFillType(SkPath::kEvenOdd_FillType);
   1226         break;
   1227     }
   1228 
   1229     SkPaint paint;
   1230     m_data->setupPaintFill(&paint);
   1231 
   1232     extactShader(&paint,
   1233                  m_state.fillPattern.get(),
   1234                  m_state.fillGradient.get());
   1235 
   1236     GC2CANVAS(this)->drawPath(*path, paint);
   1237 }
   1238 
   1239 void GraphicsContext::strokePath(const Path& pathToStroke)
   1240 {
   1241     const SkPath* path = pathToStroke.platformPath();
   1242     if (paintingDisabled() || !path)
   1243         return;
   1244 
   1245     SkPaint paint;
   1246     m_data->setupPaintStroke(&paint, 0);
   1247 
   1248     extactShader(&paint,
   1249                  m_state.strokePattern.get(),
   1250                  m_state.strokeGradient.get());
   1251 
   1252     GC2CANVAS(this)->drawPath(*path, paint);
   1253 }
   1254 
   1255 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
   1256 {
   1257     notImplemented();
   1258     return InterpolationDefault;
   1259 }
   1260 
   1261 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
   1262 {
   1263 #if 0
   1264     enum InterpolationQuality {
   1265         InterpolationDefault,
   1266         InterpolationNone,
   1267         InterpolationLow,
   1268         InterpolationMedium,
   1269         InterpolationHigh
   1270     };
   1271 #endif
   1272     // TODO: record this, so we can know when to use bitmap-filtering when we draw
   1273     // ... not sure how meaningful this will be given our playback model.
   1274 
   1275     // Certainly safe to do nothing for the present.
   1276 }
   1277 
   1278 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias)
   1279 {
   1280     if (paintingDisabled())
   1281         return;
   1282 
   1283     if (numPoints <= 1)
   1284         return;
   1285 
   1286     // FIXME: IMPLEMENT!
   1287 }
   1288 
   1289 } // namespace WebCore
   1290 
   1291 ///////////////////////////////////////////////////////////////////////////////
   1292 
   1293 SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc)
   1294 {
   1295     return gc->platformContext()->mCanvas;
   1296 }
   1297