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