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 #include "config.h"
     26 #include "GraphicsContext.h"
     27 
     28 #include "AffineTransform.h"
     29 #include "Font.h"
     30 #include "Gradient.h"
     31 #include "NotImplemented.h"
     32 #include "Path.h"
     33 #include "Pattern.h"
     34 #include "PlatformGraphicsContext.h"
     35 #include "PlatformGraphicsContextSkia.h"
     36 #include "SkBitmapRef.h"
     37 #include "SkBlurDrawLooper.h"
     38 #include "SkBlurMaskFilter.h"
     39 #include "SkCanvas.h"
     40 #include "SkColorPriv.h"
     41 #include "SkCornerPathEffect.h"
     42 #include "SkDashPathEffect.h"
     43 #include "SkDevice.h"
     44 #include "SkGradientShader.h"
     45 #include "SkPaint.h"
     46 #include "SkString.h"
     47 #include "SkiaUtils.h"
     48 #include "TransformationMatrix.h"
     49 
     50 using namespace std;
     51 
     52 namespace WebCore {
     53 
     54 // This class just holds onto a PlatformContextSkia for GraphicsContext.
     55 class GraphicsContextPlatformPrivate {
     56     WTF_MAKE_NONCOPYABLE(GraphicsContextPlatformPrivate);
     57 public:
     58     GraphicsContextPlatformPrivate(PlatformGraphicsContext* platformContext)
     59         : m_context(platformContext) { }
     60     ~GraphicsContextPlatformPrivate()
     61     {
     62         if (m_context && m_context->deleteUs())
     63             delete m_context;
     64     }
     65 
     66 
     67     PlatformGraphicsContext* context() { return m_context; }
     68 
     69 private:
     70     // Non-owning pointer to the PlatformContext.
     71     PlatformGraphicsContext* m_context;
     72 };
     73 
     74 static void syncPlatformContext(GraphicsContext* gc)
     75 {
     76     // Stroke and fill sometimes reference each other, so always
     77     // sync them both to make sure our state is consistent.
     78 
     79     PlatformGraphicsContext* pgc = gc->platformContext();
     80     Gradient* grad = gc->state().fillGradient.get();
     81     Pattern* pat = gc->state().fillPattern.get();
     82 
     83     if (grad)
     84         pgc->setFillShader(grad->platformGradient());
     85     else if (pat)
     86         pgc->setFillShader(pat->platformPattern(AffineTransform()));
     87     else
     88         pgc->setFillColor(gc->state().fillColor);
     89 
     90     grad = gc->state().strokeGradient.get();
     91     pat = gc->state().strokePattern.get();
     92 
     93     if (grad)
     94         pgc->setStrokeShader(grad->platformGradient());
     95     else if (pat)
     96         pgc->setStrokeShader(pat->platformPattern(AffineTransform()));
     97     else
     98         pgc->setStrokeColor(gc->state().strokeColor);
     99 }
    100 
    101 ////////////////////////////////////////////////////////////////////////////////////////////////
    102 
    103 GraphicsContext* GraphicsContext::createOffscreenContext(int width, int height)
    104 {
    105     SkBitmap bitmap;
    106     bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
    107     bitmap.allocPixels();
    108     bitmap.eraseColor(0);
    109 
    110     PlatformGraphicsContextSkia* pgc =
    111         new PlatformGraphicsContextSkia(new SkCanvas(bitmap), true);
    112     GraphicsContext* ctx = new GraphicsContext(pgc);
    113     return ctx;
    114 }
    115 
    116 ////////////////////////////////////////////////////////////////////////////////////////////////
    117 
    118 void GraphicsContext::platformInit(PlatformGraphicsContext* gc)
    119 {
    120     if (gc)
    121         gc->setGraphicsContext(this);
    122     m_data = new GraphicsContextPlatformPrivate(gc);
    123     setPaintingDisabled(!gc || gc->isPaintingDisabled());
    124 }
    125 
    126 void GraphicsContext::platformDestroy()
    127 {
    128     delete m_data;
    129 }
    130 
    131 void GraphicsContext::savePlatformState()
    132 {
    133     if (paintingDisabled())
    134         return;
    135     platformContext()->save();
    136 }
    137 
    138 void GraphicsContext::restorePlatformState()
    139 {
    140     if (paintingDisabled())
    141         return;
    142     platformContext()->restore();
    143 }
    144 
    145 bool GraphicsContext::willFill() const
    146 {
    147     return m_state.fillColor.rgb();
    148 }
    149 
    150 bool GraphicsContext::willStroke() const
    151 {
    152     return m_state.strokeColor.rgb();
    153 }
    154 
    155 // Draws a filled rectangle with a stroked border.
    156 void GraphicsContext::drawRect(const IntRect& rect)
    157 {
    158     if (paintingDisabled())
    159         return;
    160 
    161     syncPlatformContext(this);
    162     platformContext()->drawRect(rect);
    163 }
    164 
    165 // This is only used to draw borders.
    166 void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2)
    167 {
    168     if (paintingDisabled())
    169         return;
    170 
    171     syncPlatformContext(this);
    172     platformContext()->drawLine(point1, point2);
    173 }
    174 
    175 void GraphicsContext::drawLineForText(const FloatPoint& pt, float width, bool /* printing */)
    176 {
    177     if (paintingDisabled())
    178         return;
    179 
    180     syncPlatformContext(this);
    181     platformContext()->drawLineForText(pt, width);
    182 }
    183 
    184 void GraphicsContext::drawLineForTextChecking(const FloatPoint& pt, float width,
    185                                               TextCheckingLineStyle style)
    186 {
    187     if (paintingDisabled())
    188         return;
    189 
    190     syncPlatformContext(this);
    191     platformContext()->drawLineForTextChecking(pt, width, style);
    192 }
    193 
    194 // This method is only used to draw the little circles used in lists.
    195 void GraphicsContext::drawEllipse(const IntRect& rect)
    196 {
    197     if (paintingDisabled())
    198         return;
    199 
    200     syncPlatformContext(this);
    201     platformContext()->drawEllipse(rect);
    202 }
    203 
    204 void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan)
    205 {
    206     if (paintingDisabled())
    207         return;
    208 
    209     syncPlatformContext(this);
    210     platformContext()->strokeArc(r, startAngle, angleSpan);
    211 }
    212 
    213 void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points,
    214                                         bool shouldAntialias)
    215 {
    216     if (paintingDisabled())
    217         return;
    218 
    219     syncPlatformContext(this);
    220     platformContext()->drawConvexPolygon(numPoints, points, shouldAntialias);
    221 }
    222 
    223 void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
    224                                       const IntSize& bottomLeft, const IntSize& bottomRight,
    225                                       const Color& color, ColorSpace colorSpace)
    226 {
    227     if (paintingDisabled())
    228         return;
    229 
    230     syncPlatformContext(this);
    231     platformContext()->fillRoundedRect(rect, topLeft, topRight,
    232             bottomLeft, bottomRight, color, colorSpace);
    233 }
    234 
    235 void GraphicsContext::fillRect(const FloatRect& rect)
    236 {
    237     if (paintingDisabled())
    238         return;
    239 
    240     syncPlatformContext(this);
    241     platformContext()->fillRect(rect);
    242 }
    243 
    244 void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace)
    245 {
    246     if (paintingDisabled())
    247         return;
    248 
    249     syncPlatformContext(this);
    250     platformContext()->fillRect(rect, color, colorSpace);
    251 }
    252 
    253 void GraphicsContext::clip(const FloatRect& rect)
    254 {
    255     if (paintingDisabled())
    256         return;
    257 
    258     platformContext()->clip(rect);
    259 }
    260 
    261 void GraphicsContext::clip(const Path& path)
    262 {
    263     if (paintingDisabled())
    264         return;
    265 
    266     platformContext()->clip(path);
    267 }
    268 
    269 void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness)
    270 {
    271     if (paintingDisabled())
    272         return;
    273 
    274     platformContext()->addInnerRoundedRectClip(rect, thickness);
    275 }
    276 
    277 void GraphicsContext::canvasClip(const Path& path)
    278 {
    279     if (paintingDisabled())
    280         return;
    281 
    282     platformContext()->canvasClip(path);
    283 }
    284 
    285 void GraphicsContext::clipOut(const IntRect& r)
    286 {
    287     if (paintingDisabled())
    288         return;
    289 
    290     platformContext()->clipOut(r);
    291 }
    292 
    293 #if ENABLE(SVG)
    294 void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule)
    295 {
    296     if (paintingDisabled())
    297         return;
    298 
    299     platformContext()->clipPath(pathToClip, clipRule);
    300 }
    301 #endif
    302 
    303 void GraphicsContext::clipOut(const Path& p)
    304 {
    305     if (paintingDisabled())
    306         return;
    307 
    308     platformContext()->clipOut(p);
    309 }
    310 
    311 //////////////////////////////////////////////////////////////////////////////////////////////////
    312 
    313 #if SVG_SUPPORT
    314 KRenderingDeviceContext* GraphicsContext::createRenderingDeviceContext()
    315 {
    316     return new KRenderingDeviceContextQuartz(platformContext());
    317 }
    318 #endif
    319 
    320 void GraphicsContext::beginTransparencyLayer(float opacity)
    321 {
    322     if (paintingDisabled())
    323         return;
    324 
    325     platformContext()->beginTransparencyLayer(opacity);
    326 }
    327 
    328 void GraphicsContext::endTransparencyLayer()
    329 {
    330     if (paintingDisabled())
    331         return;
    332 
    333     platformContext()->endTransparencyLayer();
    334 }
    335 
    336 ///////////////////////////////////////////////////////////////////////////
    337 
    338 void GraphicsContext::setupFillPaint(SkPaint* paint)
    339 {
    340     if (paintingDisabled())
    341         return;
    342     syncPlatformContext(this);
    343     platformContext()->setupPaintFill(paint);
    344 }
    345 
    346 void GraphicsContext::setupStrokePaint(SkPaint* paint)
    347 {
    348     if (paintingDisabled())
    349         return;
    350     syncPlatformContext(this);
    351     platformContext()->setupPaintStroke(paint, 0);
    352 }
    353 
    354 bool GraphicsContext::setupShadowPaint(SkPaint* paint, SkPoint* offset)
    355 {
    356     if (paintingDisabled())
    357         return false;
    358     syncPlatformContext(this);
    359     return platformContext()->setupPaintShadow(paint, offset);
    360 }
    361 
    362 void GraphicsContext::setPlatformStrokeColor(const Color& c, ColorSpace)
    363 {
    364 }
    365 
    366 void GraphicsContext::setPlatformStrokeThickness(float f)
    367 {
    368     if (paintingDisabled())
    369         return;
    370     platformContext()->setStrokeThickness(f);
    371 }
    372 
    373 void GraphicsContext::setPlatformStrokeStyle(StrokeStyle style)
    374 {
    375     if (paintingDisabled())
    376         return;
    377     platformContext()->setStrokeStyle(style);
    378 }
    379 
    380 void GraphicsContext::setPlatformFillColor(const Color& c, ColorSpace)
    381 {
    382 }
    383 
    384 void GraphicsContext::setPlatformShadow(const FloatSize& size, float blur, const Color& color, ColorSpace)
    385 {
    386     if (paintingDisabled())
    387         return;
    388 
    389     if (blur <= 0)
    390         this->clearPlatformShadow();
    391 
    392     SkColor c;
    393     if (color.isValid())
    394         c = color.rgb();
    395     else
    396         c = SkColorSetARGB(0xFF / 3, 0, 0, 0); // "std" Apple shadow color
    397     platformContext()->setShadow(blur, size.width(), size.height(), c);
    398 }
    399 
    400 void GraphicsContext::clearPlatformShadow()
    401 {
    402     if (paintingDisabled())
    403         return;
    404 
    405     platformContext()->setShadow(0, 0, 0, 0);
    406 }
    407 
    408 ///////////////////////////////////////////////////////////////////////////////
    409 
    410 void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color)
    411 {
    412     if (paintingDisabled())
    413         return;
    414 
    415     syncPlatformContext(this);
    416     platformContext()->drawFocusRing(rects, width, offset, color);
    417 }
    418 
    419 void GraphicsContext::drawFocusRing(const Path&, int, int, const Color&)
    420 {
    421     // Do nothing, since we draw the focus ring independently.
    422 }
    423 
    424 PlatformGraphicsContext* GraphicsContext::platformContext() const
    425 {
    426     ASSERT(!paintingDisabled());
    427     return m_data->context();
    428 }
    429 
    430 void GraphicsContext::setMiterLimit(float limit)
    431 {
    432     if (paintingDisabled())
    433         return;
    434     platformContext()->setMiterLimit(limit);
    435 }
    436 
    437 void GraphicsContext::setAlpha(float alpha)
    438 {
    439     if (paintingDisabled())
    440         return;
    441     platformContext()->setAlpha(alpha);
    442 }
    443 
    444 void GraphicsContext::setPlatformCompositeOperation(CompositeOperator op)
    445 {
    446     if (paintingDisabled())
    447         return;
    448     platformContext()->setCompositeOperation(op);
    449 }
    450 
    451 void GraphicsContext::clearRect(const FloatRect& rect)
    452 {
    453     if (paintingDisabled())
    454         return;
    455 
    456     syncPlatformContext(this);
    457     platformContext()->clearRect(rect);
    458 }
    459 
    460 void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth)
    461 {
    462     if (paintingDisabled())
    463         return;
    464 
    465     syncPlatformContext(this);
    466     platformContext()->strokeRect(rect, lineWidth);
    467 }
    468 
    469 void GraphicsContext::setLineCap(LineCap cap)
    470 {
    471     if (paintingDisabled())
    472         return;
    473     platformContext()->setLineCap(cap);
    474 }
    475 
    476 #if ENABLE(SVG)
    477 void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset)
    478 {
    479     if (paintingDisabled())
    480         return;
    481 
    482     platformContext()->setLineDash(dashes, dashOffset);
    483 }
    484 #endif
    485 
    486 void GraphicsContext::setLineJoin(LineJoin join)
    487 {
    488     if (paintingDisabled())
    489         return;
    490     platformContext()->setLineJoin(join);
    491 }
    492 
    493 void GraphicsContext::scale(const FloatSize& size)
    494 {
    495     if (paintingDisabled())
    496         return;
    497     platformContext()->scale(size);
    498 }
    499 
    500 void GraphicsContext::rotate(float angleInRadians)
    501 {
    502     if (paintingDisabled())
    503         return;
    504     platformContext()->rotate(angleInRadians);
    505 }
    506 
    507 void GraphicsContext::translate(float x, float y)
    508 {
    509     if (paintingDisabled())
    510         return;
    511     if (!x && !y)
    512         return;
    513     platformContext()->translate(x, y);
    514 }
    515 
    516 void GraphicsContext::concatCTM(const AffineTransform& affine)
    517 {
    518     if (paintingDisabled())
    519         return;
    520     platformContext()->concatCTM(affine);
    521 }
    522 
    523 // This is intended to round the rect to device pixels (through the CTM)
    524 // and then invert the result back into source space, with the hope that when
    525 // it is drawn (through the matrix), it will land in the "right" place (i.e.
    526 // on pixel boundaries).
    527 
    528 // For android, we record this geometry once and then draw it though various
    529 // scale factors as the user zooms, without re-recording. Thus this routine
    530 // should just leave the original geometry alone.
    531 
    532 // If we instead draw into bitmap tiles, we should then perform this
    533 // transform -> round -> inverse step.
    534 
    535 FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect, RoundingMode)
    536 {
    537     return rect;
    538 }
    539 
    540 //////////////////////////////////////////////////////////////////////////////////////////////////
    541 
    542 void GraphicsContext::setURLForRect(const KURL& link, const IntRect& destRect)
    543 {
    544 // Appears to be PDF specific, so we ignore it
    545 }
    546 
    547 void GraphicsContext::setPlatformShouldAntialias(bool useAA)
    548 {
    549     if (paintingDisabled())
    550         return;
    551     platformContext()->setShouldAntialias(useAA);
    552 }
    553 
    554 void GraphicsContext::setPlatformFillGradient(Gradient* fillGradient)
    555 {
    556 }
    557 
    558 void GraphicsContext::setPlatformFillPattern(Pattern* fillPattern)
    559 {
    560 }
    561 
    562 void GraphicsContext::setPlatformStrokeGradient(Gradient* strokeGradient)
    563 {
    564 }
    565 
    566 void GraphicsContext::setPlatformStrokePattern(Pattern* strokePattern)
    567 {
    568 }
    569 
    570 AffineTransform GraphicsContext::getCTM() const
    571 {
    572     if (paintingDisabled())
    573         return AffineTransform();
    574     const SkMatrix& m = platformContext()->getTotalMatrix();
    575     return AffineTransform(SkScalarToDouble(m.getScaleX()), // a
    576                            SkScalarToDouble(m.getSkewY()), // b
    577                            SkScalarToDouble(m.getSkewX()), // c
    578                            SkScalarToDouble(m.getScaleY()), // d
    579                            SkScalarToDouble(m.getTranslateX()), // e
    580                            SkScalarToDouble(m.getTranslateY())); // f
    581 }
    582 
    583 void GraphicsContext::setCTM(const AffineTransform& transform)
    584 {
    585     // The SkPicture mode of Skia does not support SkCanvas::setMatrix(), so we
    586     // can not simply use that method here. We could calculate the transform
    587     // required to achieve the desired matrix and use SkCanvas::concat(), but
    588     // there's currently no need for this.
    589     ASSERT_NOT_REACHED();
    590 }
    591 
    592 ///////////////////////////////////////////////////////////////////////////////
    593 
    594 void GraphicsContext::fillPath(const Path& pathToFill)
    595 {
    596     if (paintingDisabled())
    597         return;
    598 
    599     syncPlatformContext(this);
    600     platformContext()->fillPath(pathToFill, fillRule());
    601 }
    602 
    603 void GraphicsContext::strokePath(const Path& pathToStroke)
    604 {
    605     if (paintingDisabled())
    606         return;
    607 
    608     syncPlatformContext(this);
    609     platformContext()->strokePath(pathToStroke);
    610 }
    611 
    612 InterpolationQuality GraphicsContext::imageInterpolationQuality() const
    613 {
    614     notImplemented();
    615     return InterpolationDefault;
    616 }
    617 
    618 void GraphicsContext::setImageInterpolationQuality(InterpolationQuality mode)
    619 {
    620 #if 0
    621     enum InterpolationQuality {
    622         InterpolationDefault,
    623         InterpolationNone,
    624         InterpolationLow,
    625         InterpolationMedium,
    626         InterpolationHigh
    627     };
    628 #endif
    629     // TODO: record this, so we can know when to use bitmap-filtering when we draw
    630     // ... not sure how meaningful this will be given our playback model.
    631 
    632     // Certainly safe to do nothing for the present.
    633 }
    634 
    635 void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint*,
    636                                         bool antialias)
    637 {
    638     if (paintingDisabled())
    639         return;
    640 
    641     if (numPoints <= 1)
    642         return;
    643 
    644     // FIXME: IMPLEMENT!
    645 }
    646 
    647 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run,
    648                                            const FloatPoint& point, int h,
    649                                            const Color& backgroundColor,
    650                                            ColorSpace colorSpace, int from,
    651                                            int to, bool isActive)
    652 {
    653     if (paintingDisabled())
    654         return;
    655 
    656     syncPlatformContext(this);
    657     platformContext()->drawHighlightForText(font, run, point, h, backgroundColor,
    658             colorSpace, from, to, isActive);
    659 }
    660 
    661 } // namespace WebCore
    662