Home | History | Annotate | Download | only in context
      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 #define LOG_TAG "PlatformGraphicsContext"
     27 #define LOG_NDEBUG 1
     28 
     29 #include "config.h"
     30 #include "PlatformGraphicsContext.h"
     31 
     32 #include "AndroidLog.h"
     33 #include "SkBlurDrawLooper.h"
     34 #include "SkBlurMaskFilter.h"
     35 #include "SkColorPriv.h"
     36 #include "SkDashPathEffect.h"
     37 #include "SkPaint.h"
     38 #include "SkShader.h"
     39 #include "SkiaUtils.h"
     40 
     41 namespace WebCore {
     42 
     43 //**************************************
     44 // Helper functions
     45 //**************************************
     46 
     47 static int RoundToInt(float x)
     48 {
     49     return (int)roundf(x);
     50 }
     51 
     52 template <typename T> T* deepCopyPtr(const T* src)
     53 {
     54     return src ? new T(*src) : 0;
     55 }
     56 
     57 // Set a bitmap shader that mimics dashing by width-on, width-off.
     58 // Returns false if it could not succeed (e.g. there was an existing shader)
     59 static bool setBitmapDash(SkPaint* paint, int width) {
     60     if (width <= 0 || paint->getShader())
     61         return false;
     62 
     63     SkColor c = paint->getColor();
     64 
     65     SkBitmap bm;
     66     bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1);
     67     bm.allocPixels();
     68     bm.lockPixels();
     69 
     70     // set the ON pixel
     71     *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c),
     72                                             SkColorGetG(c), SkColorGetB(c));
     73     // set the OFF pixel
     74     *bm.getAddr32(1, 0) = 0;
     75     bm.unlockPixels();
     76 
     77     SkMatrix matrix;
     78     matrix.setScale(SkIntToScalar(width), SK_Scalar1);
     79 
     80     SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
     81                                                SkShader::kClamp_TileMode);
     82     s->setLocalMatrix(matrix);
     83 
     84     paint->setShader(s)->unref();
     85     paint->setFilterBitmap(true);
     86     paint->setAntiAlias(true);
     87     return true;
     88 }
     89 
     90 //**************************************
     91 // State implementation
     92 //**************************************
     93 
     94 PlatformGraphicsContext::State::State()
     95     : pathEffect(0)
     96     , miterLimit(4)
     97     , alpha(1)
     98     , strokeThickness(0) // Same as default in GraphicsContextPrivate.h
     99     , lineCap(SkPaint::kDefault_Cap)
    100     , lineJoin(SkPaint::kDefault_Join)
    101     , mode(SkXfermode::kSrcOver_Mode)
    102     , dashRatio(3)
    103     , fillColor(SK_ColorBLACK)
    104     , fillShader(0)
    105     , strokeColor(SK_ColorBLACK)
    106     , strokeShader(0)
    107     , useAA(true)
    108     , strokeStyle(SolidStroke)
    109 {
    110 }
    111 
    112 PlatformGraphicsContext::State::State(const State& other)
    113     : pathEffect(other.pathEffect)
    114     , miterLimit(other.miterLimit)
    115     , alpha(other.alpha)
    116     , strokeThickness(other.strokeThickness)
    117     , lineCap(other.lineCap)
    118     , lineJoin(other.lineJoin)
    119     , mode(other.mode)
    120     , dashRatio(other.dashRatio)
    121     , shadow(other.shadow)
    122     , fillColor(other.fillColor)
    123     , fillShader(other.fillShader)
    124     , strokeColor(other.strokeColor)
    125     , strokeShader(other.strokeShader)
    126     , useAA(other.useAA)
    127     , strokeStyle(other.strokeStyle)
    128 {
    129     SkSafeRef(pathEffect);
    130     SkSafeRef(fillShader);
    131     SkSafeRef(strokeShader);
    132 }
    133 
    134 PlatformGraphicsContext::State::~State()
    135 {
    136     SkSafeUnref(pathEffect);
    137     SkSafeUnref(fillShader);
    138     SkSafeUnref(strokeShader);
    139 }
    140 
    141 void PlatformGraphicsContext::State::setShadow(int radius, int dx, int dy, SkColor c)
    142 {
    143     // Cut the radius in half, to visually match the effect seen in
    144     // safari browser
    145     shadow.blur = SkScalarHalf(SkIntToScalar(radius));
    146     shadow.dx = SkIntToScalar(dx);
    147     shadow.dy = SkIntToScalar(dy);
    148     shadow.color = c;
    149 }
    150 
    151 bool PlatformGraphicsContext::State::setupShadowPaint(SkPaint* paint, SkPoint* offset,
    152                       bool shadowsIgnoreTransforms)
    153 {
    154     paint->setAntiAlias(true);
    155     paint->setDither(true);
    156     paint->setXfermodeMode(mode);
    157     paint->setColor(shadow.color);
    158     offset->set(shadow.dx, shadow.dy);
    159 
    160     // Currently, only GraphicsContexts associated with the
    161     // HTMLCanvasElement have shadows ignore transforms set.  This
    162     // allows us to distinguish between CSS and Canvas shadows which
    163     // have different rendering specifications.
    164     uint32_t flags = SkBlurMaskFilter::kNone_BlurFlag;
    165     if (shadowsIgnoreTransforms) {
    166         offset->fY = -offset->fY;
    167         flags |= SkBlurMaskFilter::kIgnoreTransform_BlurFlag;
    168     }
    169 
    170     if (shadow.blur > 0) {
    171         paint->setMaskFilter(SkBlurMaskFilter::Create(shadow.blur,
    172                              SkBlurMaskFilter::kNormal_BlurStyle, flags))->unref();
    173     }
    174     return SkColorGetA(shadow.color) && (shadow.blur || shadow.dx || shadow.dy);
    175 }
    176 
    177 SkColor PlatformGraphicsContext::State::applyAlpha(SkColor c) const
    178 {
    179     int s = RoundToInt(alpha * 256);
    180     if (s >= 256)
    181         return c;
    182     if (s < 0)
    183         return 0;
    184 
    185     int a = SkAlphaMul(SkColorGetA(c), s);
    186     return (c & 0x00FFFFFF) | (a << 24);
    187 }
    188 
    189 // Returns a new State with all of this object's inherited properties copied.
    190 PlatformGraphicsContext::State PlatformGraphicsContext::State::cloneInheritedProperties()
    191 {
    192     return PlatformGraphicsContext::State(*this);
    193 }
    194 
    195 //**************************************
    196 // PlatformGraphicsContext
    197 //**************************************
    198 
    199 PlatformGraphicsContext::PlatformGraphicsContext()
    200 {
    201     m_stateStack.append(State());
    202     m_state = &m_stateStack.last();
    203 }
    204 
    205 PlatformGraphicsContext::~PlatformGraphicsContext()
    206 {
    207 }
    208 
    209 //**************************************
    210 // State management
    211 //**************************************
    212 
    213 void PlatformGraphicsContext::save()
    214 {
    215     m_stateStack.append(m_state->cloneInheritedProperties());
    216     m_state = &m_stateStack.last();
    217 }
    218 
    219 void PlatformGraphicsContext::restore()
    220 {
    221     m_stateStack.removeLast();
    222     m_state = &m_stateStack.last();
    223 }
    224 
    225 //**************************************
    226 // State setters
    227 //**************************************
    228 
    229 void PlatformGraphicsContext::setAlpha(float alpha)
    230 {
    231     m_state->alpha = alpha;
    232 }
    233 
    234 int PlatformGraphicsContext::getNormalizedAlpha() const
    235 {
    236     int alpha = roundf(m_state->alpha * 256);
    237     if (alpha > 255)
    238         alpha = 255;
    239     else if (alpha < 0)
    240         alpha = 0;
    241     return alpha;
    242 }
    243 
    244 void PlatformGraphicsContext::setCompositeOperation(CompositeOperator op)
    245 {
    246     m_state->mode = WebCoreCompositeToSkiaComposite(op);
    247 }
    248 
    249 bool PlatformGraphicsContext::setFillColor(const Color& c)
    250 {
    251     bool dirty = false;
    252     if (m_state->fillColor != c.rgb()) {
    253         m_state->fillColor = c.rgb();
    254         dirty = true;
    255     }
    256     return setFillShader(0) || dirty;
    257 }
    258 
    259 bool PlatformGraphicsContext::setFillShader(SkShader* fillShader)
    260 {
    261     bool dirty = false;
    262     if (fillShader && m_state->fillColor != Color::black) {
    263         m_state->fillColor = Color::black;
    264         dirty = true;
    265     }
    266 
    267     if (fillShader != m_state->fillShader) {
    268         SkSafeUnref(m_state->fillShader);
    269         m_state->fillShader = fillShader;
    270         SkSafeRef(m_state->fillShader);
    271         dirty = true;
    272     }
    273     return dirty;
    274 }
    275 
    276 void PlatformGraphicsContext::setLineCap(LineCap cap)
    277 {
    278     switch (cap) {
    279     case ButtCap:
    280         m_state->lineCap = SkPaint::kButt_Cap;
    281         break;
    282     case RoundCap:
    283         m_state->lineCap = SkPaint::kRound_Cap;
    284         break;
    285     case SquareCap:
    286         m_state->lineCap = SkPaint::kSquare_Cap;
    287         break;
    288     default:
    289         ALOGD("PlatformGraphicsContextSkia::setLineCap: unknown LineCap %d\n", cap);
    290         break;
    291     }
    292 }
    293 
    294 void PlatformGraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
    295 {
    296     size_t dashLength = dashes.size();
    297     if (!dashLength)
    298         return;
    299 
    300     size_t count = !(dashLength % 2) ? dashLength : dashLength * 2;
    301     SkScalar* intervals = new SkScalar[count];
    302 
    303     for (unsigned int i = 0; i < count; i++)
    304         intervals[i] = SkFloatToScalar(dashes[i % dashLength]);
    305     SkPathEffect **effectPtr = &m_state->pathEffect;
    306     SkSafeUnref(*effectPtr);
    307     *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset));
    308 
    309     delete[] intervals;
    310 }
    311 
    312 void PlatformGraphicsContext::setLineJoin(LineJoin join)
    313 {
    314     switch (join) {
    315     case MiterJoin:
    316         m_state->lineJoin = SkPaint::kMiter_Join;
    317         break;
    318     case RoundJoin:
    319         m_state->lineJoin = SkPaint::kRound_Join;
    320         break;
    321     case BevelJoin:
    322         m_state->lineJoin = SkPaint::kBevel_Join;
    323         break;
    324     default:
    325         ALOGD("PlatformGraphicsContextSkia::setLineJoin: unknown LineJoin %d\n", join);
    326         break;
    327     }
    328 }
    329 
    330 void PlatformGraphicsContext::setMiterLimit(float limit)
    331 {
    332     m_state->miterLimit = limit;
    333 }
    334 
    335 void PlatformGraphicsContext::setShadow(int radius, int dx, int dy, SkColor c)
    336 {
    337     m_state->setShadow(radius, dx, dy, c);
    338 }
    339 
    340 void PlatformGraphicsContext::setShouldAntialias(bool useAA)
    341 {
    342     m_state->useAA = useAA;
    343 }
    344 
    345 bool PlatformGraphicsContext::setStrokeColor(const Color& c)
    346 {
    347     bool dirty = false;
    348     if (m_state->strokeColor != c.rgb()) {
    349         m_state->strokeColor = c.rgb();
    350         dirty = true;
    351     }
    352     return setStrokeShader(0) || dirty;
    353 }
    354 
    355 bool PlatformGraphicsContext::setStrokeShader(SkShader* strokeShader)
    356 {
    357     bool dirty = false;
    358     if (strokeShader && m_state->strokeColor != Color::black) {
    359         m_state->strokeColor = Color::black;
    360         dirty = true;
    361     }
    362 
    363     if (strokeShader != m_state->strokeShader) {
    364         SkSafeUnref(m_state->strokeShader);
    365         m_state->strokeShader = strokeShader;
    366         SkSafeRef(m_state->strokeShader);
    367         dirty = true;
    368     }
    369     return dirty;
    370 }
    371 
    372 void PlatformGraphicsContext::setStrokeStyle(StrokeStyle style)
    373 {
    374     m_state->strokeStyle = style;
    375 }
    376 
    377 void PlatformGraphicsContext::setStrokeThickness(float f)
    378 {
    379     m_state->strokeThickness = f;
    380 }
    381 
    382 //**************************************
    383 // Paint setup
    384 //**************************************
    385 
    386 void PlatformGraphicsContext::setupPaintCommon(SkPaint* paint) const
    387 {
    388     paint->setAntiAlias(m_state->useAA);
    389     paint->setDither(true);
    390     paint->setXfermodeMode(m_state->mode);
    391     if (SkColorGetA(m_state->shadow.color) > 0) {
    392 
    393         // Currently, only GraphicsContexts associated with the
    394         // HTMLCanvasElement have shadows ignore transforms set.  This
    395         // allows us to distinguish between CSS and Canvas shadows which
    396         // have different rendering specifications.
    397         SkScalar dy = m_state->shadow.dy;
    398         uint32_t flags = SkBlurDrawLooper::kNone_BlurFlag;
    399         if (shadowsIgnoreTransforms()) {
    400             dy = -dy;
    401             flags |= SkBlurDrawLooper::kIgnoreTransform_BlurFlag;
    402             flags |= SkBlurDrawLooper::kOverrideColor_BlurFlag;
    403         }
    404 
    405         SkDrawLooper* looper = new SkBlurDrawLooper(m_state->shadow.blur,
    406                                                     m_state->shadow.dx,
    407                                                     dy,
    408                                                     m_state->shadow.color,
    409                                                     flags);
    410         paint->setLooper(looper)->unref();
    411     }
    412     paint->setFilterBitmap(true);
    413 }
    414 
    415 void PlatformGraphicsContext::setupPaintFill(SkPaint* paint) const
    416 {
    417     this->setupPaintCommon(paint);
    418     paint->setColor(m_state->applyAlpha(m_state->fillColor));
    419     paint->setShader(m_state->fillShader);
    420 }
    421 
    422 bool PlatformGraphicsContext::setupPaintShadow(SkPaint* paint, SkPoint* offset) const
    423 {
    424     return m_state->setupShadowPaint(paint, offset, shadowsIgnoreTransforms());
    425 }
    426 
    427 bool PlatformGraphicsContext::setupPaintStroke(SkPaint* paint, SkRect* rect,
    428                                                bool isHLine)
    429 {
    430     this->setupPaintCommon(paint);
    431     paint->setColor(m_state->applyAlpha(m_state->strokeColor));
    432     paint->setShader(m_state->strokeShader);
    433 
    434     float width = m_state->strokeThickness;
    435 
    436     // This allows dashing and dotting to work properly for hairline strokes
    437     // FIXME: Should we only do this for dashed and dotted strokes?
    438     if (!width)
    439         width = 1;
    440 
    441     paint->setStyle(SkPaint::kStroke_Style);
    442     paint->setStrokeWidth(SkFloatToScalar(width));
    443     paint->setStrokeCap(m_state->lineCap);
    444     paint->setStrokeJoin(m_state->lineJoin);
    445     paint->setStrokeMiter(SkFloatToScalar(m_state->miterLimit));
    446 
    447     if (rect && (RoundToInt(width) & 1))
    448         rect->inset(-SK_ScalarHalf, -SK_ScalarHalf);
    449 
    450     SkPathEffect* pe = m_state->pathEffect;
    451     if (pe) {
    452         paint->setPathEffect(pe);
    453         return false;
    454     }
    455     switch (m_state->strokeStyle) {
    456     case NoStroke:
    457     case SolidStroke:
    458         width = 0;
    459         break;
    460     case DashedStroke:
    461         width = m_state->dashRatio * width;
    462         break;
    463         // No break
    464     case DottedStroke:
    465         break;
    466     }
    467 
    468     if (width > 0) {
    469         // Return true if we're basically a dotted dash of squares
    470         bool justSqrs = RoundToInt(width) == RoundToInt(paint->getStrokeWidth());
    471 
    472         if (justSqrs || !isHLine || !setBitmapDash(paint, width)) {
    473 #if 0
    474             // this is slow enough that we just skip it for now
    475             // see http://b/issue?id=4163023
    476             SkScalar intervals[] = { width, width };
    477             pe = new SkDashPathEffect(intervals, 2, 0);
    478             paint->setPathEffect(pe)->unref();
    479 #endif
    480         }
    481         return justSqrs;
    482     }
    483     return false;
    484 }
    485 
    486 }   // WebCore
    487