Home | History | Annotate | Download | only in paint
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "config.h"
      6 #include "core/paint/ObjectPainter.h"
      7 
      8 #include "core/rendering/PaintInfo.h"
      9 #include "core/rendering/RenderObject.h"
     10 #include "core/rendering/RenderTheme.h"
     11 #include "core/rendering/style/RenderStyle.h"
     12 #include "platform/geometry/LayoutPoint.h"
     13 #include "platform/graphics/GraphicsContextStateSaver.h"
     14 
     15 namespace blink {
     16 
     17 void ObjectPainter::paintFocusRing(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderStyle* style)
     18 {
     19     Vector<LayoutRect> focusRingRects;
     20     m_renderObject.addFocusRingRects(focusRingRects, paintOffset, paintInfo.paintContainer());
     21     ASSERT(style->outlineStyleIsAuto());
     22     Vector<IntRect> focusRingIntRects;
     23     for (size_t i = 0; i < focusRingRects.size(); ++i)
     24         focusRingIntRects.append(pixelSnappedIntRect(focusRingRects[i]));
     25     paintInfo.context->drawFocusRing(focusRingIntRects, style->outlineWidth(), style->outlineOffset(), m_renderObject.resolveColor(style, CSSPropertyOutlineColor));
     26 }
     27 
     28 void ObjectPainter::paintOutline(PaintInfo& paintInfo, const LayoutRect& paintRect)
     29 {
     30     RenderStyle* styleToUse = m_renderObject.style();
     31     if (!styleToUse->hasOutline())
     32         return;
     33 
     34     LayoutUnit outlineWidth = styleToUse->outlineWidth();
     35 
     36     int outlineOffset = styleToUse->outlineOffset();
     37 
     38     if (styleToUse->outlineStyleIsAuto()) {
     39         if (RenderTheme::theme().shouldDrawDefaultFocusRing(&m_renderObject)) {
     40             // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
     41             paintFocusRing(paintInfo, paintRect.location(), styleToUse);
     42         }
     43         return;
     44     }
     45 
     46     if (styleToUse->outlineStyle() == BNONE)
     47         return;
     48 
     49     IntRect inner = pixelSnappedIntRect(paintRect);
     50     inner.inflate(outlineOffset);
     51 
     52     IntRect outer = pixelSnappedIntRect(inner);
     53     outer.inflate(outlineWidth);
     54 
     55     // FIXME: This prevents outlines from painting inside the object. See bug 12042
     56     if (outer.isEmpty())
     57         return;
     58 
     59     EBorderStyle outlineStyle = styleToUse->outlineStyle();
     60     Color outlineColor = m_renderObject.resolveColor(styleToUse, CSSPropertyOutlineColor);
     61 
     62     GraphicsContext* graphicsContext = paintInfo.context;
     63     bool useTransparencyLayer = outlineColor.hasAlpha();
     64     if (useTransparencyLayer) {
     65         if (outlineStyle == SOLID) {
     66             Path path;
     67             path.addRect(outer);
     68             path.addRect(inner);
     69             graphicsContext->setFillRule(RULE_EVENODD);
     70             graphicsContext->setFillColor(outlineColor);
     71             graphicsContext->fillPath(path);
     72             return;
     73         }
     74         graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
     75         outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
     76     }
     77 
     78     int leftOuter = outer.x();
     79     int leftInner = inner.x();
     80     int rightOuter = outer.maxX();
     81     int rightInner = inner.maxX();
     82     int topOuter = outer.y();
     83     int topInner = inner.y();
     84     int bottomOuter = outer.maxY();
     85     int bottomInner = inner.maxY();
     86 
     87     drawLineForBoxSide(graphicsContext, leftOuter, topOuter, leftInner, bottomOuter, BSLeft, outlineColor, outlineStyle, outlineWidth, outlineWidth);
     88     drawLineForBoxSide(graphicsContext, leftOuter, topOuter, rightOuter, topInner, BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth);
     89     drawLineForBoxSide(graphicsContext, rightInner, topOuter, rightOuter, bottomOuter, BSRight, outlineColor, outlineStyle, outlineWidth, outlineWidth);
     90     drawLineForBoxSide(graphicsContext, leftOuter, bottomInner, rightOuter, bottomOuter, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth);
     91 
     92     if (useTransparencyLayer)
     93         graphicsContext->endLayer();
     94 }
     95 
     96 void ObjectPainter::drawLineForBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
     97     BoxSide side, Color color, EBorderStyle style,
     98     int adjacentWidth1, int adjacentWidth2, bool antialias)
     99 {
    100     int thickness;
    101     int length;
    102     if (side == BSTop || side == BSBottom) {
    103         thickness = y2 - y1;
    104         length = x2 - x1;
    105     } else {
    106         thickness = x2 - x1;
    107         length = y2 - y1;
    108     }
    109 
    110     // FIXME: We really would like this check to be an ASSERT as we don't want to draw empty borders. However
    111     // nothing guarantees that the following recursive calls to drawLineForBoxSide will have non-null dimensions.
    112     if (!thickness || !length)
    113         return;
    114 
    115     if (style == DOUBLE && thickness < 3)
    116         style = SOLID;
    117 
    118     switch (style) {
    119     case BNONE:
    120     case BHIDDEN:
    121         return;
    122     case DOTTED:
    123     case DASHED:
    124         drawDashedOrDottedBoxSide(graphicsContext, x1, y1, x2, y2, side,
    125             color, thickness, style, antialias);
    126         break;
    127     case DOUBLE:
    128         drawDoubleBoxSide(graphicsContext, x1, y1, x2, y2, length, side, color,
    129             thickness, adjacentWidth1, adjacentWidth2, antialias);
    130         break;
    131     case RIDGE:
    132     case GROOVE:
    133         drawRidgeOrGrooveBoxSide(graphicsContext, x1, y1, x2, y2, side, color,
    134             style, adjacentWidth1, adjacentWidth2, antialias);
    135         break;
    136     case INSET:
    137         // FIXME: Maybe we should lighten the colors on one side like Firefox.
    138         // https://bugs.webkit.org/show_bug.cgi?id=58608
    139         if (side == BSTop || side == BSLeft)
    140             color = color.dark();
    141         // fall through
    142     case OUTSET:
    143         if (style == OUTSET && (side == BSBottom || side == BSRight))
    144             color = color.dark();
    145         // fall through
    146     case SOLID:
    147         drawSolidBoxSide(graphicsContext, x1, y1, x2, y2, side, color, adjacentWidth1, adjacentWidth2, antialias);
    148         break;
    149     }
    150 }
    151 
    152 void ObjectPainter::drawDashedOrDottedBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
    153     BoxSide side, Color color, int thickness, EBorderStyle style, bool antialias)
    154 {
    155     if (thickness <= 0)
    156         return;
    157 
    158     bool wasAntialiased = graphicsContext->shouldAntialias();
    159     StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
    160     graphicsContext->setShouldAntialias(antialias);
    161     graphicsContext->setStrokeColor(color);
    162     graphicsContext->setStrokeThickness(thickness);
    163     graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke);
    164 
    165     switch (side) {
    166     case BSBottom:
    167     case BSTop:
    168         graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2));
    169         break;
    170     case BSRight:
    171     case BSLeft:
    172         graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2));
    173         break;
    174     }
    175     graphicsContext->setShouldAntialias(wasAntialiased);
    176     graphicsContext->setStrokeStyle(oldStrokeStyle);
    177 }
    178 
    179 void ObjectPainter::drawDoubleBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
    180     int length, BoxSide side, Color color, int thickness, int adjacentWidth1, int adjacentWidth2, bool antialias)
    181 {
    182     int thirdOfThickness = (thickness + 1) / 3;
    183     ASSERT(thirdOfThickness);
    184 
    185     if (!adjacentWidth1 && !adjacentWidth2) {
    186         StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
    187         graphicsContext->setStrokeStyle(NoStroke);
    188         graphicsContext->setFillColor(color);
    189 
    190         bool wasAntialiased = graphicsContext->shouldAntialias();
    191         graphicsContext->setShouldAntialias(antialias);
    192 
    193         switch (side) {
    194         case BSTop:
    195         case BSBottom:
    196             graphicsContext->drawRect(IntRect(x1, y1, length, thirdOfThickness));
    197             graphicsContext->drawRect(IntRect(x1, y2 - thirdOfThickness, length, thirdOfThickness));
    198             break;
    199         case BSLeft:
    200         case BSRight:
    201             // FIXME: Why do we offset the border by 1 in this case but not the other one?
    202             if (length > 1) {
    203                 graphicsContext->drawRect(IntRect(x1, y1 + 1, thirdOfThickness, length - 1));
    204                 graphicsContext->drawRect(IntRect(x2 - thirdOfThickness, y1 + 1, thirdOfThickness, length - 1));
    205             }
    206             break;
    207         }
    208 
    209         graphicsContext->setShouldAntialias(wasAntialiased);
    210         graphicsContext->setStrokeStyle(oldStrokeStyle);
    211         return;
    212     }
    213 
    214     int adjacent1BigThird = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 3;
    215     int adjacent2BigThird = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 3;
    216 
    217     switch (side) {
    218     case BSTop:
    219         drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
    220             y1, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness,
    221             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    222         drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
    223             y2 - thirdOfThickness, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y2,
    224             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    225         break;
    226     case BSLeft:
    227         drawLineForBoxSide(graphicsContext, x1, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
    228             x1 + thirdOfThickness, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0),
    229             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    230         drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
    231             x2, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0),
    232             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    233         break;
    234     case BSBottom:
    235         drawLineForBoxSide(graphicsContext, x1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
    236             y1, x2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0), y1 + thirdOfThickness,
    237             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    238         drawLineForBoxSide(graphicsContext, x1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
    239             y2 - thirdOfThickness, x2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0), y2,
    240             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    241         break;
    242     case BSRight:
    243         drawLineForBoxSide(graphicsContext, x1, y1 + std::max((adjacentWidth1 * 2 + 1) / 3, 0),
    244             x1 + thirdOfThickness, y2 - std::max((adjacentWidth2 * 2 + 1) / 3, 0),
    245             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    246         drawLineForBoxSide(graphicsContext, x2 - thirdOfThickness, y1 + std::max((-adjacentWidth1 * 2 + 1) / 3, 0),
    247             x2, y2 - std::max((-adjacentWidth2 * 2 + 1) / 3, 0),
    248             side, color, SOLID, adjacent1BigThird, adjacent2BigThird, antialias);
    249         break;
    250     default:
    251         break;
    252     }
    253 }
    254 
    255 void ObjectPainter::drawRidgeOrGrooveBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
    256     BoxSide side, Color color, EBorderStyle style, int adjacentWidth1, int adjacentWidth2, bool antialias)
    257 {
    258     EBorderStyle s1;
    259     EBorderStyle s2;
    260     if (style == GROOVE) {
    261         s1 = INSET;
    262         s2 = OUTSET;
    263     } else {
    264         s1 = OUTSET;
    265         s2 = INSET;
    266     }
    267 
    268     int adjacent1BigHalf = ((adjacentWidth1 > 0) ? adjacentWidth1 + 1 : adjacentWidth1 - 1) / 2;
    269     int adjacent2BigHalf = ((adjacentWidth2 > 0) ? adjacentWidth2 + 1 : adjacentWidth2 - 1) / 2;
    270 
    271     switch (side) {
    272     case BSTop:
    273         drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1, 0) / 2, y1, x2 - std::max(-adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2,
    274             side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias);
    275         drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(adjacentWidth2 + 1, 0) / 2, y2,
    276             side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
    277         break;
    278     case BSLeft:
    279         drawLineForBoxSide(graphicsContext, x1, y1 + std::max(-adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(-adjacentWidth2, 0) / 2,
    280             side, color, s1, adjacent1BigHalf, adjacent2BigHalf, antialias);
    281         drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(adjacentWidth2 + 1, 0) / 2,
    282             side, color, s2, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
    283         break;
    284     case BSBottom:
    285         drawLineForBoxSide(graphicsContext, x1 + std::max(adjacentWidth1, 0) / 2, y1, x2 - std::max(adjacentWidth2, 0) / 2, (y1 + y2 + 1) / 2,
    286             side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias);
    287         drawLineForBoxSide(graphicsContext, x1 + std::max(-adjacentWidth1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - std::max(-adjacentWidth2 + 1, 0) / 2, y2,
    288             side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
    289         break;
    290     case BSRight:
    291         drawLineForBoxSide(graphicsContext, x1, y1 + std::max(adjacentWidth1, 0) / 2, (x1 + x2 + 1) / 2, y2 - std::max(adjacentWidth2, 0) / 2,
    292             side, color, s2, adjacent1BigHalf, adjacent2BigHalf, antialias);
    293         drawLineForBoxSide(graphicsContext, (x1 + x2 + 1) / 2, y1 + std::max(-adjacentWidth1 + 1, 0) / 2, x2, y2 - std::max(-adjacentWidth2 + 1, 0) / 2,
    294             side, color, s1, adjacentWidth1 / 2, adjacentWidth2 / 2, antialias);
    295         break;
    296     }
    297 }
    298 
    299 void ObjectPainter::drawSolidBoxSide(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2,
    300     BoxSide side, Color color, int adjacentWidth1, int adjacentWidth2, bool antialias)
    301 {
    302     StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
    303     graphicsContext->setStrokeStyle(NoStroke);
    304     graphicsContext->setFillColor(color);
    305     ASSERT(x2 >= x1);
    306     ASSERT(y2 >= y1);
    307     if (!adjacentWidth1 && !adjacentWidth2) {
    308         // Turn off antialiasing to match the behavior of drawConvexPolygon();
    309         // this matters for rects in transformed contexts.
    310         bool wasAntialiased = graphicsContext->shouldAntialias();
    311         graphicsContext->setShouldAntialias(antialias);
    312         graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1));
    313         graphicsContext->setShouldAntialias(wasAntialiased);
    314         graphicsContext->setStrokeStyle(oldStrokeStyle);
    315         return;
    316     }
    317     FloatPoint quad[4];
    318     switch (side) {
    319     case BSTop:
    320         quad[0] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y1);
    321         quad[1] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y2);
    322         quad[2] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y2);
    323         quad[3] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y1);
    324         break;
    325     case BSBottom:
    326         quad[0] = FloatPoint(x1 + std::max(adjacentWidth1, 0), y1);
    327         quad[1] = FloatPoint(x1 + std::max(-adjacentWidth1, 0), y2);
    328         quad[2] = FloatPoint(x2 - std::max(-adjacentWidth2, 0), y2);
    329         quad[3] = FloatPoint(x2 - std::max(adjacentWidth2, 0), y1);
    330         break;
    331     case BSLeft:
    332         quad[0] = FloatPoint(x1, y1 + std::max(-adjacentWidth1, 0));
    333         quad[1] = FloatPoint(x1, y2 - std::max(-adjacentWidth2, 0));
    334         quad[2] = FloatPoint(x2, y2 - std::max(adjacentWidth2, 0));
    335         quad[3] = FloatPoint(x2, y1 + std::max(adjacentWidth1, 0));
    336         break;
    337     case BSRight:
    338         quad[0] = FloatPoint(x1, y1 + std::max(adjacentWidth1, 0));
    339         quad[1] = FloatPoint(x1, y2 - std::max(adjacentWidth2, 0));
    340         quad[2] = FloatPoint(x2, y2 - std::max(-adjacentWidth2, 0));
    341         quad[3] = FloatPoint(x2, y1 + std::max(-adjacentWidth1, 0));
    342         break;
    343     }
    344 
    345     graphicsContext->drawConvexPolygon(4, quad, antialias);
    346     graphicsContext->setStrokeStyle(oldStrokeStyle);
    347 }
    348 
    349 } // namespace blink
    350