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