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     save();
    786     SkPaint paint;
    787 
    788     m_data->setupPaintFill(&paint);
    789 
    790     extactShader(&paint,
    791                  m_state.fillPattern.get(),
    792                  m_state.fillGradient.get());
    793 
    794     GC2CANVAS(this)->drawRect(rect, paint);
    795     restore();
    796 }
    797 
    798 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace)
    799 {
    800     if (paintingDisabled())
    801         return;
    802 
    803     if (color.rgb() & 0xFF000000) {
    804         save();
    805         SkPaint paint;
    806 
    807         m_data->setupPaintCommon(&paint);
    808         paint.setColor(color.rgb()); // Punch in the specified color
    809         paint.setShader(0); // In case we had one set
    810 
    811         // Sometimes we record and draw portions of the page, using clips
    812         // for each portion. The problem with this is that webkit, sometimes,
    813         // sees that we're only recording a portion, and they adjust some of
    814         // their rectangle coordinates accordingly (e.g.
    815         // RenderBoxModelObject::paintFillLayerExtended() which calls
    816         // rect.intersect(paintInfo.rect) and then draws the bg with that
    817         // rect. The result is that we end up drawing rects that are meant to
    818         // seam together (one for each portion), but if the rects have
    819         // fractional coordinates (e.g. we are zoomed by a fractional amount)
    820         // we will double-draw those edges, resulting in visual cracks or
    821         // artifacts.
    822 
    823         // The fix seems to be to just turn off antialasing for rects (this
    824         // entry-point in GraphicsContext seems to have been sufficient,
    825         // though perhaps we'll find we need to do this as well in fillRect(r)
    826         // as well.) Currently setupPaintCommon() enables antialiasing.
    827 
    828         // Since we never show the page rotated at a funny angle, disabling
    829         // antialiasing seems to have no real down-side, and it does fix the
    830         // bug when we're zoomed (and drawing portions that need to seam).
    831         paint.setAntiAlias(false);
    832 
    833         GC2CANVAS(this)->drawRect(rect, paint);
    834         restore();
    835     }
    836 }
    837 
    838 void GraphicsContext::clip(const FloatRect& rect)
    839 {
    840     if (paintingDisabled())
    841         return;
    842 
    843     GC2CANVAS(this)->clipRect(rect);
    844 }
    845 
    846 void GraphicsContext::clip(const Path& path)
    847 {
    848     if (paintingDisabled())
    849         return;
    850 
    851     m_data->clipPathAntiAliased(*path.platformPath());
    852 }
    853 
    854 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
    855 {
    856     if (paintingDisabled())
    857         return;
    858 
    859     SkPath path;
    860     SkRect r(rect);
    861 
    862     path.addOval(r, SkPath::kCW_Direction);
    863     // Only perform the inset if we won't invert r
    864     if (2 * thickness < rect.width() && 2 * thickness < rect.height()) {
    865         // Adding one to the thickness doesn't make the border too thick as
    866         // it's painted over afterwards. But without this adjustment the
    867         // border appears a little anemic after anti-aliasing.
    868         r.inset(SkIntToScalar(thickness + 1), SkIntToScalar(thickness + 1));
    869         path.addOval(r, SkPath::kCCW_Direction);
    870     }
    871     m_data->clipPathAntiAliased(path);
    872 }
    873 
    874 void GraphicsContext::canvasClip(const Path& path)
    875 {
    876     clip(path);
    877 }
    878 
    879 void GraphicsContext::clipOut(const IntRect& r)
    880 {
    881     if (paintingDisabled())
    882         return;
    883 
    884     GC2CANVAS(this)->clipRect(r, SkRegion::kDifference_Op);
    885 }
    886 
    887 #if ENABLE(SVG)
    888 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
    889 {
    890     if (paintingDisabled())
    891         return;
    892 
    893     SkPath path = *pathToClip.platformPath();
    894     path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
    895     GC2CANVAS(this)->clipPath(path);
    896 }
    897 #endif
    898 
    899 void GraphicsContext::clipOut(const Path& p)
    900 {
    901     if (paintingDisabled())
    902         return;
    903 
    904     GC2CANVAS(this)->clipPath(*p.platformPath(), SkRegion::kDifference_Op);
    905 }
    906 
    907 //////////////////////////////////////////////////////////////////////////////////////////////////
    908 
    909 #if SVG_SUPPORT
    910 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
    911 {
    912     return new KRenderingDeviceContextQuartz(platformContext());
    913 }
    914 #endif
    915 
    916 // These are the flags we need when we call saveLayer for transparency.
    917 // Since it does not appear that webkit intends this to also save/restore
    918 // the matrix or clip, I do not give those flags (for performance)
    919 #define TRANSPARENCY_SAVEFLAGS                                  \
    920     (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag |   \
    921                           SkCanvas::kFullColorLayer_SaveFlag)
    922 
    923 void GraphicsContext::beginTransparencyLayer(float opacity)
    924 {
    925     if (paintingDisabled())
    926         return;
    927 
    928     SkCanvas* canvas = GC2CANVAS(this);
    929     canvas->saveLayerAlpha(0, (int)(opacity * 255), TRANSPARENCY_SAVEFLAGS);
    930 }
    931 
    932 void GraphicsContext::endTransparencyLayer()
    933 {
    934     if (paintingDisabled())
    935         return;
    936 
    937     GC2CANVAS(this)->restore();
    938 }
    939 
    940 ///////////////////////////////////////////////////////////////////////////
    941 
    942 void GraphicsContext::setupBitmapPaint(SkPaint* paint)
    943 {
    944     m_data->setupPaintBitmap(paint);
    945 }
    946 
    947 void GraphicsContext::setupFillPaint(SkPaint* paint)
    948 {
    949     m_data->setupPaintFill(paint);
    950 }
    951 
    952 void GraphicsContext::setupStrokePaint(SkPaint* paint)
    953 {
    954     m_data->setupPaintStroke(paint, 0);
    955 }
    956 
    957 bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset)
    958 {
    959     return m_data->getState()->setupShadowPaint(this, paint, offset);
    960 }
    961 
    962 void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace)
    963 {
    964     m_data->setStrokeColor(c);
    965 }
    966 
    967 void GraphicsContext::setPlatformStrokeThickness(float f)
    968 {
    969     m_data->setStrokeThickness(f);
    970 }
    971 
    972 void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace)
    973 {
    974     m_data->setFillColor(c);
    975 }
    976 
    977 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
    978 {
    979     if (paintingDisabled())
    980         return;
    981 
    982     if (blur <= 0)
    983         this->clearPlatformShadow();
    984 
    985     SkColor c;
    986     if (color.isValid())
    987         c = color.rgb();
    988     else
    989         c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color
    990     m_data->getState()->setShadow(blur, size.width(), size.height(), c);
    991 }
    992 
    993 void GraphicsContext::clearPlatformShadow()
    994 {
    995     if (paintingDisabled())
    996         return;
    997 
    998     m_data->getState()->setShadow(0, 0, 0, 0);
    999 }
   1000 
   1001 ///////////////////////////////////////////////////////////////////////////////
   1002 
   1003 void GraphicsContext::drawFocusRing(const Vector<IntRect>&, int, int, const Color&)
   1004 {
   1005     // Do nothing, since we draw the focus ring independently.
   1006 }
   1007 
   1008 void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
   1009 {
   1010     // Do nothing, since we draw the focus ring independently.
   1011 }
   1012 
   1013 PlatformGraphicsContext* GraphicsContext::platformContext() const
   1014 {
   1015     ASSERT(!paintingDisabled());
   1016     return m_data->getPlatformGfxCtx();
   1017 }
   1018 
   1019 void GraphicsContext::setMiterLimit(float limit)
   1020 {
   1021     m_data->getState()->miterLimit = limit;
   1022 }
   1023 
   1024 void GraphicsContext::setAlpha(float alpha)
   1025 {
   1026     m_data->getState()->alpha = alpha;
   1027 }
   1028 
   1029 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
   1030 {
   1031     m_data->getState()->mode = WebCoreCompositeToSkiaComposite(op);
   1032 }
   1033 
   1034 void GraphicsContext::clearRect(const FloatRect& rect)
   1035 {
   1036     if (paintingDisabled())
   1037         return;
   1038 
   1039     SkPaint paint;
   1040 
   1041     m_data->setupPaintFill(&paint);
   1042     paint.setXfermodeMode(SkXfermode::kClear_Mode);
   1043     GC2CANVAS(this)->drawRect(rect, paint);
   1044 }
   1045 
   1046 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
   1047 {
   1048     if (paintingDisabled())
   1049         return;
   1050 
   1051     SkPaint paint;
   1052 
   1053     m_data->setupPaintStroke(&paint, 0);
   1054     paint.setStrokeWidth(SkFloatToScalar(lineWidth));
   1055     GC2CANVAS(this)->drawRect(rect, paint);
   1056 }
   1057 
   1058 void GraphicsContext::setLineCap(LineCap cap)
   1059 {
   1060     switch (cap) {
   1061     case ButtCap:
   1062         m_data->getState()->lineCap = SkPaint::kButt_Cap;
   1063         break;
   1064     case RoundCap:
   1065         m_data->getState()->lineCap = SkPaint::kRound_Cap;
   1066         break;
   1067     case SquareCap:
   1068         m_data->getState()->lineCap = SkPaint::kSquare_Cap;
   1069         break;
   1070     default:
   1071         SkDEBUGF(("GraphicsContext::setLineCap: unknown LineCap %d\n", cap));
   1072         break;
   1073     }
   1074 }
   1075 
   1076 #if ENABLE(SVG)
   1077 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
   1078 {
   1079     if (paintingDisabled())
   1080         return;
   1081 
   1082     size_t dashLength = dashes.size();
   1083     if (!dashLength)
   1084         return;
   1085 
   1086     size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
   1087     SkScalar* intervals = new SkScalar[count];
   1088 
   1089     for (unsigned int i = 0; i < count; i++)
   1090         intervals[i] = SkFloatToScalar(dashes[i % dashLength]);
   1091     SkPathEffect **effectPtr = &m_data->getState()->pathEffect;
   1092     SkSafeUnref(*effectPtr);
   1093     *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset));
   1094 
   1095     delete[] intervals;
   1096 }
   1097 #endif
   1098 
   1099 void GraphicsContext::setLineJoin(LineJoin join)
   1100 {
   1101     switch (join) {
   1102     case MiterJoin:
   1103         m_data->getState()->lineJoin = SkPaint::kMiter_Join;
   1104         break;
   1105     case RoundJoin:
   1106         m_data->getState()->lineJoin = SkPaint::kRound_Join;
   1107         break;
   1108     case BevelJoin:
   1109         m_data->getState()->lineJoin = SkPaint::kBevel_Join;
   1110         break;
   1111     default:
   1112         SkDEBUGF(("GraphicsContext::setLineJoin: unknown LineJoin %d\n", join));
   1113         break;
   1114     }
   1115 }
   1116 
   1117 void GraphicsContext::scale(const FloatSize& size)
   1118 {
   1119     if (paintingDisabled())
   1120         return;
   1121     GC2CANVAS(this)->scale(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
   1122 }
   1123 
   1124 void GraphicsContext::rotate(float angleInRadians)
   1125 {
   1126     if (paintingDisabled())
   1127         return;
   1128     GC2CANVAS(this)->rotate(SkFloatToScalar(angleInRadians * (180.0f / 3.14159265f)));
   1129 }
   1130 
   1131 void GraphicsContext::translate(float x, float y)
   1132 {
   1133     if (paintingDisabled())
   1134         return;
   1135     GC2CANVAS(this)->translate(SkFloatToScalar(x), SkFloatToScalar(y));
   1136 }
   1137 
   1138 void GraphicsContext::concatCTM(const AffineTransform& affine)
   1139 {
   1140     if (paintingDisabled())
   1141         return;
   1142     GC2CANVAS(this)->concat(affine);
   1143 }
   1144 
   1145 // This is intended to round the rect to device pixels (through the CTM)
   1146 // and then invert the result back into source space, with the hope that when
   1147 // it is drawn (through the matrix), it will land in the "right" place (i.e.
   1148 // on pixel boundaries).
   1149 
   1150 // For android, we record this geometry once and then draw it though various
   1151 // scale factors as the user zooms, without re-recording. Thus this routine
   1152 // should just leave the original geometry alone.
   1153 
   1154 // If we instead draw into bitmap tiles, we should then perform this
   1155 // transform -> round -> inverse step.
   1156 
   1157 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
   1158 {
   1159     return rect;
   1160 }
   1161 
   1162 //////////////////////////////////////////////////////////////////////////////////////////////////
   1163 
   1164 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
   1165 {
   1166 // Appears to be PDF specific, so we ignore it
   1167 #if 0
   1168 if (paintingDisabled())
   1169     return;
   1170 
   1171 CFURLRef urlRef = link.createCFURL();
   1172 if (urlRef) {
   1173     CGContextRef context = platformContext();
   1174 
   1175     // Get the bounding box to handle clipping.
   1176     CGRect box = CGContextGetClipBoundingBox(context);
   1177 
   1178     IntRect intBox((int)box.origin.x, (int)box.origin.y, (int)box.size.width, (int)box.size.height);
   1179     IntRect rect = destRect;
   1180     rect.intersect(intBox);
   1181 
   1182     CGPDFContextSetURLForRect(context, urlRef,
   1183         CGRectApplyAffineTransform(rect, CGContextGetCTM(context)));
   1184 
   1185     CFRelease(urlRef);
   1186 }
   1187 #endif
   1188 }
   1189 
   1190 void GraphicsContext::setPlatformShouldAntialias(bool useAA)
   1191 {
   1192     if (paintingDisabled())
   1193         return;
   1194     m_data->getState()->useAA = useAA;
   1195 }
   1196 
   1197 AffineTransform GraphicsContext::getCTM() const
   1198 {
   1199     const SkMatrix& m = GC2CANVAS(this)->getTotalMatrix();
   1200     return AffineTransform(SkScalarToDouble(m.getScaleX()), // a
   1201                            SkScalarToDouble(m.getSkewY()), // b
   1202                            SkScalarToDouble(m.getSkewX()), // c
   1203                            SkScalarToDouble(m.getScaleY()), // d
   1204                            SkScalarToDouble(m.getTranslateX()), // e
   1205                            SkScalarToDouble(m.getTranslateY())); // f
   1206 }
   1207 
   1208 void GraphicsContext::setCTM(const AffineTransform& transform)
   1209 {
   1210     // The SkPicture mode of Skia does not support SkCanvas::setMatrix(), so we
   1211     // can not simply use that method here. We could calculate the transform
   1212     // required to achieve the desired matrix and use SkCanvas::concat(), but
   1213     // there's currently no need for this.
   1214     ASSERT_NOT_REACHED();
   1215 }
   1216 
   1217 ///////////////////////////////////////////////////////////////////////////////
   1218 
   1219 void GraphicsContext::fillPath(const Path& pathToFill)
   1220 {
   1221     SkPath* path = pathToFill.platformPath();
   1222     if (paintingDisabled() || !path)
   1223         return;
   1224 
   1225     switch (this->fillRule()) {
   1226     case RULE_NONZERO:
   1227         path->setFillType(SkPath::kWinding_FillType);
   1228         break;
   1229     case RULE_EVENODD:
   1230         path->setFillType(SkPath::kEvenOdd_FillType);
   1231         break;
   1232     }
   1233 
   1234     SkPaint paint;
   1235     m_data->setupPaintFill(&paint);
   1236 
   1237     extactShader(&paint,
   1238                  m_state.fillPattern.get(),
   1239                  m_state.fillGradient.get());
   1240 
   1241     GC2CANVAS(this)->drawPath(*path, paint);
   1242 }
   1243 
   1244 void GraphicsContext::strokePath(const Path& pathToStroke)
   1245 {
   1246     const SkPath* path = pathToStroke.platformPath();
   1247     if (paintingDisabled() || !path)
   1248         return;
   1249 
   1250     SkPaint paint;
   1251     m_data->setupPaintStroke(&paint, 0);
   1252 
   1253     extactShader(&paint,
   1254                  m_state.strokePattern.get(),
   1255                  m_state.strokeGradient.get());
   1256 
   1257     GC2CANVAS(this)->drawPath(*path, paint);
   1258 }
   1259 
   1260 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
   1261 {
   1262     notImplemented();
   1263     return InterpolationDefault;
   1264 }
   1265 
   1266 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
   1267 {
   1268 #if 0
   1269     enum InterpolationQuality {
   1270         InterpolationDefault,
   1271         InterpolationNone,
   1272         InterpolationLow,
   1273         InterpolationMedium,
   1274         InterpolationHigh
   1275     };
   1276 #endif
   1277     // TODO: record this, so we can know when to use bitmap-filtering when we draw
   1278     // ... not sure how meaningful this will be given our playback model.
   1279 
   1280     // Certainly safe to do nothing for the present.
   1281 }
   1282 
   1283 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias)
   1284 {
   1285     if (paintingDisabled())
   1286         return;
   1287 
   1288     if (numPoints <= 1)
   1289         return;
   1290 
   1291     // FIXME: IMPLEMENT!
   1292 }
   1293 
   1294 } // namespace WebCore
   1295 
   1296 ///////////////////////////////////////////////////////////////////////////////
   1297 
   1298 SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc)
   1299 {
   1300     return gc->platformContext()->mCanvas;
   1301 }
   1302