Home | History | Annotate | Download | only in graphics
      1 /*
      2  * Copyright (C) 2003, 2004, 2005, 2006, 2009 Apple Inc. All rights reserved.
      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  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. 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 APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "GraphicsContext.h"
     28 
     29 #include "BidiResolver.h"
     30 #include "Font.h"
     31 #include "Generator.h"
     32 #include "GraphicsContextPrivate.h"
     33 
     34 using namespace std;
     35 
     36 namespace WebCore {
     37 
     38 class TextRunIterator {
     39 public:
     40     TextRunIterator()
     41         : m_textRun(0)
     42         , m_offset(0)
     43     {
     44     }
     45 
     46     TextRunIterator(const TextRun* textRun, unsigned offset)
     47         : m_textRun(textRun)
     48         , m_offset(offset)
     49     {
     50     }
     51 
     52     TextRunIterator(const TextRunIterator& other)
     53         : m_textRun(other.m_textRun)
     54         , m_offset(other.m_offset)
     55     {
     56     }
     57 
     58     unsigned offset() const { return m_offset; }
     59     void increment() { m_offset++; }
     60     bool atEnd() const { return !m_textRun || m_offset >= m_textRun->length(); }
     61     UChar current() const { return (*m_textRun)[m_offset]; }
     62     WTF::Unicode::Direction direction() const { return atEnd() ? WTF::Unicode::OtherNeutral : WTF::Unicode::direction(current()); }
     63 
     64     bool operator==(const TextRunIterator& other)
     65     {
     66         return m_offset == other.m_offset && m_textRun == other.m_textRun;
     67     }
     68 
     69     bool operator!=(const TextRunIterator& other) { return !operator==(other); }
     70 
     71 private:
     72     const TextRun* m_textRun;
     73     int m_offset;
     74 };
     75 
     76 GraphicsContextPrivate* GraphicsContext::createGraphicsContextPrivate()
     77 {
     78     return new GraphicsContextPrivate;
     79 }
     80 
     81 void GraphicsContext::destroyGraphicsContextPrivate(GraphicsContextPrivate* deleteMe)
     82 {
     83     delete deleteMe;
     84 }
     85 
     86 void GraphicsContext::save()
     87 {
     88     if (paintingDisabled())
     89         return;
     90 
     91     m_common->stack.append(m_common->state);
     92 
     93     savePlatformState();
     94 }
     95 
     96 void GraphicsContext::restore()
     97 {
     98     if (paintingDisabled())
     99         return;
    100 
    101     if (m_common->stack.isEmpty()) {
    102         LOG_ERROR("ERROR void GraphicsContext::restore() stack is empty");
    103         return;
    104     }
    105     m_common->state = m_common->stack.last();
    106     m_common->stack.removeLast();
    107 
    108     restorePlatformState();
    109 }
    110 
    111 void GraphicsContext::setStrokeThickness(float thickness)
    112 {
    113     m_common->state.strokeThickness = thickness;
    114     setPlatformStrokeThickness(thickness);
    115 }
    116 
    117 void GraphicsContext::setStrokeStyle(const StrokeStyle& style)
    118 {
    119     m_common->state.strokeStyle = style;
    120     setPlatformStrokeStyle(style);
    121 }
    122 
    123 void GraphicsContext::setStrokeColor(const Color& color, ColorSpace colorSpace)
    124 {
    125     m_common->state.strokeColor = color;
    126     m_common->state.strokeColorSpace = colorSpace;
    127     m_common->state.strokeGradient.clear();
    128     m_common->state.strokePattern.clear();
    129     setPlatformStrokeColor(color, colorSpace);
    130 }
    131 
    132 void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color, ColorSpace colorSpace)
    133 {
    134     m_common->state.shadowSize = size;
    135     m_common->state.shadowBlur = blur;
    136     m_common->state.shadowColor = color;
    137     setPlatformShadow(size, blur, color, colorSpace);
    138 }
    139 
    140 void GraphicsContext::clearShadow()
    141 {
    142     m_common->state.shadowSize = IntSize();
    143     m_common->state.shadowBlur = 0;
    144     m_common->state.shadowColor = Color();
    145     clearPlatformShadow();
    146 }
    147 
    148 bool GraphicsContext::getShadow(IntSize& size, int& blur, Color& color) const
    149 {
    150     size = m_common->state.shadowSize;
    151     blur = m_common->state.shadowBlur;
    152     color = m_common->state.shadowColor;
    153 
    154     return color.isValid() && color.alpha() && (blur || size.width() || size.height());
    155 }
    156 
    157 float GraphicsContext::strokeThickness() const
    158 {
    159     return m_common->state.strokeThickness;
    160 }
    161 
    162 StrokeStyle GraphicsContext::strokeStyle() const
    163 {
    164     return m_common->state.strokeStyle;
    165 }
    166 
    167 Color GraphicsContext::strokeColor() const
    168 {
    169     return m_common->state.strokeColor;
    170 }
    171 
    172 ColorSpace GraphicsContext::strokeColorSpace() const
    173 {
    174     return m_common->state.strokeColorSpace;
    175 }
    176 
    177 WindRule GraphicsContext::fillRule() const
    178 {
    179     return m_common->state.fillRule;
    180 }
    181 
    182 void GraphicsContext::setFillRule(WindRule fillRule)
    183 {
    184     m_common->state.fillRule = fillRule;
    185 }
    186 
    187 void GraphicsContext::setFillColor(const Color& color, ColorSpace colorSpace)
    188 {
    189     m_common->state.fillColor = color;
    190     m_common->state.fillColorSpace = colorSpace;
    191     m_common->state.fillGradient.clear();
    192     m_common->state.fillPattern.clear();
    193     setPlatformFillColor(color, colorSpace);
    194 }
    195 
    196 Color GraphicsContext::fillColor() const
    197 {
    198     return m_common->state.fillColor;
    199 }
    200 
    201 ColorSpace GraphicsContext::fillColorSpace() const
    202 {
    203     return m_common->state.fillColorSpace;
    204 }
    205 
    206 void GraphicsContext::setShouldAntialias(bool b)
    207 {
    208     m_common->state.shouldAntialias = b;
    209     setPlatformShouldAntialias(b);
    210 }
    211 
    212 bool GraphicsContext::shouldAntialias() const
    213 {
    214     return m_common->state.shouldAntialias;
    215 }
    216 
    217 void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern)
    218 {
    219     ASSERT(pattern);
    220     if (!pattern) {
    221         setStrokeColor(Color::black, DeviceColorSpace);
    222         return;
    223     }
    224     m_common->state.strokeGradient.clear();
    225     m_common->state.strokePattern = pattern;
    226     setPlatformStrokePattern(m_common->state.strokePattern.get());
    227 }
    228 
    229 void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern)
    230 {
    231     ASSERT(pattern);
    232     if (!pattern) {
    233         setFillColor(Color::black, DeviceColorSpace);
    234         return;
    235     }
    236     m_common->state.fillGradient.clear();
    237     m_common->state.fillPattern = pattern;
    238     setPlatformFillPattern(m_common->state.fillPattern.get());
    239 }
    240 
    241 void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient)
    242 {
    243     ASSERT(gradient);
    244     if (!gradient) {
    245         setStrokeColor(Color::black, DeviceColorSpace);
    246         return;
    247     }
    248     m_common->state.strokeGradient = gradient;
    249     m_common->state.strokePattern.clear();
    250     setPlatformStrokeGradient(m_common->state.strokeGradient.get());
    251 }
    252 
    253 void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient)
    254 {
    255     ASSERT(gradient);
    256     if (!gradient) {
    257         setFillColor(Color::black, DeviceColorSpace);
    258         return;
    259     }
    260     m_common->state.fillGradient = gradient;
    261     m_common->state.fillPattern.clear();
    262     setPlatformFillGradient(m_common->state.fillGradient.get());
    263 }
    264 
    265 Gradient* GraphicsContext::fillGradient() const
    266 {
    267     return m_common->state.fillGradient.get();
    268 }
    269 
    270 Gradient* GraphicsContext::strokeGradient() const
    271 {
    272     return m_common->state.strokeGradient.get();
    273 }
    274 
    275 Pattern* GraphicsContext::fillPattern() const
    276 {
    277     return m_common->state.fillPattern.get();
    278 }
    279 
    280 Pattern* GraphicsContext::strokePattern() const
    281 {
    282     return m_common->state.strokePattern.get();
    283 }
    284 
    285 void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms)
    286 {
    287     m_common->state.shadowsIgnoreTransforms = ignoreTransforms;
    288 }
    289 
    290 bool GraphicsContext::updatingControlTints() const
    291 {
    292     return m_common->m_updatingControlTints;
    293 }
    294 
    295 void GraphicsContext::setUpdatingControlTints(bool b)
    296 {
    297     setPaintingDisabled(b);
    298     m_common->m_updatingControlTints = b;
    299 }
    300 
    301 void GraphicsContext::setPaintingDisabled(bool f)
    302 {
    303     m_common->state.paintingDisabled = f;
    304 }
    305 
    306 bool GraphicsContext::paintingDisabled() const
    307 {
    308     return m_common->state.paintingDisabled;
    309 }
    310 
    311 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& p, CompositeOperator op)
    312 {
    313     drawImage(image, styleColorSpace, p, IntRect(0, 0, -1, -1), op);
    314 }
    315 
    316 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& r, CompositeOperator op, bool useLowQualityScale)
    317 {
    318     drawImage(image, styleColorSpace, r, IntRect(0, 0, -1, -1), op, useLowQualityScale);
    319 }
    320 
    321 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntPoint& dest, const IntRect& srcRect, CompositeOperator op)
    322 {
    323     drawImage(image, styleColorSpace, IntRect(dest, srcRect.size()), srcRect, op);
    324 }
    325 
    326 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, CompositeOperator op, bool useLowQualityScale)
    327 {
    328     drawImage(image, styleColorSpace, FloatRect(dest), srcRect, op, useLowQualityScale);
    329 }
    330 
    331 #if !OS(WINCE) || PLATFORM(QT)
    332 void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPoint& point, int from, int to)
    333 {
    334     if (paintingDisabled())
    335         return;
    336 
    337     font.drawText(this, run, point, from, to);
    338 }
    339 #endif
    340 
    341 void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point)
    342 {
    343     if (paintingDisabled())
    344         return;
    345 
    346     BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver;
    347     WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft;
    348 
    349     bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, BidiContext::create(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride())));
    350 
    351     bidiResolver.setPosition(TextRunIterator(&run, 0));
    352     bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()));
    353 
    354     if (!bidiResolver.runCount())
    355         return;
    356 
    357     FloatPoint currPoint = point;
    358     BidiCharacterRun* bidiRun = bidiResolver.firstRun();
    359     while (bidiRun) {
    360 
    361         TextRun subrun = run;
    362         subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start());
    363         subrun.setRTL(bidiRun->level() % 2);
    364         subrun.setDirectionalOverride(bidiRun->dirOverride(false));
    365 
    366         font.drawText(this, subrun, currPoint);
    367 
    368         bidiRun = bidiRun->next();
    369         // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here.
    370         if (bidiRun)
    371             currPoint.move(font.floatWidth(subrun), 0.f);
    372     }
    373 
    374     bidiResolver.deleteRuns();
    375 }
    376 
    377 void GraphicsContext::drawHighlightForText(const Font& font, const TextRun& run, const IntPoint& point, int h, const Color& backgroundColor, ColorSpace colorSpace, int from, int to)
    378 {
    379     if (paintingDisabled())
    380         return;
    381 
    382     fillRect(font.selectionRectForText(run, point, h, from, to), backgroundColor, colorSpace);
    383 }
    384 
    385 void GraphicsContext::drawImage(Image* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op, bool useLowQualityScale)
    386 {
    387     if (paintingDisabled() || !image)
    388         return;
    389 
    390     float tsw = src.width();
    391     float tsh = src.height();
    392     float tw = dest.width();
    393     float th = dest.height();
    394 
    395     if (tsw == -1)
    396         tsw = image->width();
    397     if (tsh == -1)
    398         tsh = image->height();
    399 
    400     if (tw == -1)
    401         tw = image->width();
    402     if (th == -1)
    403         th = image->height();
    404 
    405     if (useLowQualityScale) {
    406         save();
    407         setImageInterpolationQuality(InterpolationNone);
    408     }
    409     image->draw(this, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), styleColorSpace, op);
    410     if (useLowQualityScale)
    411         restore();
    412 }
    413 
    414 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& rect, const IntPoint& srcPoint, const IntSize& tileSize, CompositeOperator op, bool useLowQualityScale)
    415 {
    416     if (paintingDisabled() || !image)
    417         return;
    418     if (useLowQualityScale) {
    419         save();
    420         setImageInterpolationQuality(InterpolationLow);
    421     }
    422     image->drawTiled(this, rect, srcPoint, tileSize, styleColorSpace, op);
    423     if (useLowQualityScale)
    424         restore();
    425 }
    426 
    427 void GraphicsContext::drawTiledImage(Image* image, ColorSpace styleColorSpace, const IntRect& dest, const IntRect& srcRect, Image::TileRule hRule, Image::TileRule vRule, CompositeOperator op, bool useLowQualityScale)
    428 {
    429     if (paintingDisabled() || !image)
    430         return;
    431 
    432     if (useLowQualityScale) {
    433         save();
    434         setImageInterpolationQuality(InterpolationLow);
    435     }
    436     if (hRule == Image::StretchTile && vRule == Image::StretchTile)
    437         // Just do a scale.
    438         drawImage(image, styleColorSpace, dest, srcRect, op);
    439     else
    440         image->drawTiled(this, dest, srcRect, hRule, vRule, styleColorSpace, op);
    441     if (useLowQualityScale)
    442         restore();
    443 }
    444 
    445 void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
    446     const IntSize& bottomLeft, const IntSize& bottomRight)
    447 {
    448     if (paintingDisabled())
    449         return;
    450 
    451     clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
    452 }
    453 
    454 void GraphicsContext::clipOutRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight,
    455                                          const IntSize& bottomLeft, const IntSize& bottomRight)
    456 {
    457     if (paintingDisabled())
    458         return;
    459 
    460     clipOut(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight));
    461 }
    462 
    463 int GraphicsContext::textDrawingMode()
    464 {
    465     return m_common->state.textDrawingMode;
    466 }
    467 
    468 void GraphicsContext::setTextDrawingMode(int mode)
    469 {
    470     m_common->state.textDrawingMode = mode;
    471     if (paintingDisabled())
    472         return;
    473     setPlatformTextDrawingMode(mode);
    474 }
    475 
    476 void GraphicsContext::fillRect(const FloatRect& rect, Generator& generator)
    477 {
    478     if (paintingDisabled())
    479         return;
    480     generator.fill(this, rect);
    481 }
    482 
    483 #if !(PLATFORM(SKIA) && !PLATFORM(ANDROID))
    484 void GraphicsContext::setPlatformFillGradient(Gradient*)
    485 {
    486 }
    487 
    488 void GraphicsContext::setPlatformFillPattern(Pattern*)
    489 {
    490 }
    491 
    492 void GraphicsContext::setPlatformStrokeGradient(Gradient*)
    493 {
    494 }
    495 
    496 void GraphicsContext::setPlatformStrokePattern(Pattern*)
    497 {
    498 }
    499 #endif
    500 
    501 #if !PLATFORM(CG) && !(PLATFORM(SKIA) && !PLATFORM(ANDROID))
    502 // Implement this if you want to go ahead and push the drawing mode into your native context
    503 // immediately.
    504 void GraphicsContext::setPlatformTextDrawingMode(int mode)
    505 {
    506 }
    507 #endif
    508 
    509 #if !PLATFORM(QT) && !PLATFORM(CAIRO) && !(PLATFORM(SKIA) && !PLATFORM(ANDROID)) && !PLATFORM(HAIKU) && !PLATFORM(OPENVG)
    510 void GraphicsContext::setPlatformStrokeStyle(const StrokeStyle&)
    511 {
    512 }
    513 #endif
    514 
    515 void GraphicsContext::adjustLineToPixelBoundaries(FloatPoint& p1, FloatPoint& p2, float strokeWidth, const StrokeStyle& penStyle)
    516 {
    517     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
    518     // works out.  For example, with a border width of 3, WebKit will pass us (y1+y2)/2, e.g.,
    519     // (50+53)/2 = 103/2 = 51 when we want 51.5.  It is always true that an even width gave
    520     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
    521     if (penStyle == DottedStroke || penStyle == DashedStroke) {
    522         if (p1.x() == p2.x()) {
    523             p1.setY(p1.y() + strokeWidth);
    524             p2.setY(p2.y() - strokeWidth);
    525         } else {
    526             p1.setX(p1.x() + strokeWidth);
    527             p2.setX(p2.x() - strokeWidth);
    528         }
    529     }
    530 
    531     if (static_cast<int>(strokeWidth) % 2) { //odd
    532         if (p1.x() == p2.x()) {
    533             // We're a vertical line.  Adjust our x.
    534             p1.setX(p1.x() + 0.5f);
    535             p2.setX(p2.x() + 0.5f);
    536         } else {
    537             // We're a horizontal line. Adjust our y.
    538             p1.setY(p1.y() + 0.5f);
    539             p2.setY(p2.y() + 0.5f);
    540         }
    541     }
    542 }
    543 
    544 }
    545