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