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