Changeset 177658 in webkit


Ignore:
Timestamp:
Dec 22, 2014 3:18:59 PM (9 years ago)
Author:
Alan Bujtas
Message:

Incorrect dashed and dotted border painting.
https://bugs.webkit.org/show_bug.cgi?id=139872
rdar://problem/18024205

Reviewed by Simon Fraser.

This patch makes dashed/dotted border painting symmetric and consistent.
It also works with subpixel positioning.

Source/WebCore:

Tests: fast/borders/border-painting-correctness-dashed.html

fast/borders/border-painting-correctness-dotted.html

  • platform/graphics/cg/GraphicsContextCG.cpp:

(WebCore::GraphicsContext::drawLine):

  • rendering/RenderObject.cpp:

(WebCore::RenderObject::drawLineForBoxSide):

LayoutTests:

  • fast/borders/border-painting-correctness-dashed-expected.html: Added.
  • fast/borders/border-painting-correctness-dashed.html: Added.
  • fast/borders/border-painting-correctness-dotted-expected.html: Added.
  • fast/borders/border-painting-correctness-dotted.html: Added.
  • fast/borders/resources/border-painting-correctness-dashed-expected.png: Added.
  • fast/borders/resources/border-painting-correctness-dotted-expected.png: Added.
Location:
trunk
Files:
6 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r177653 r177658  
     12014-12-22  Zalan Bujtas  <zalan@apple.com>
     2
     3        Incorrect dashed and dotted border painting.
     4        https://bugs.webkit.org/show_bug.cgi?id=139872
     5        rdar://problem/18024205
     6
     7        Reviewed by Simon Fraser.
     8
     9        This patch makes dashed/dotted border painting symmetric and consistent.
     10        It also works with subpixel positioning.
     11
     12        * fast/borders/border-painting-correctness-dashed-expected.html: Added.
     13        * fast/borders/border-painting-correctness-dashed.html: Added.
     14        * fast/borders/border-painting-correctness-dotted-expected.html: Added.
     15        * fast/borders/border-painting-correctness-dotted.html: Added.
     16        * fast/borders/resources/border-painting-correctness-dashed-expected.png: Added.
     17        * fast/borders/resources/border-painting-correctness-dotted-expected.png: Added.
     18
    1192014-12-22  Alexey Proskuryakov  <ap@apple.com>
    220
  • trunk/Source/WebCore/ChangeLog

    r177656 r177658  
     12014-12-22  Zalan Bujtas  <zalan@apple.com>
     2
     3        Incorrect dashed and dotted border painting.
     4        https://bugs.webkit.org/show_bug.cgi?id=139872
     5        rdar://problem/18024205
     6
     7        Reviewed by Simon Fraser.
     8
     9        This patch makes dashed/dotted border painting symmetric and consistent.
     10        It also works with subpixel positioning.
     11
     12        Tests: fast/borders/border-painting-correctness-dashed.html
     13               fast/borders/border-painting-correctness-dotted.html
     14
     15        * platform/graphics/cg/GraphicsContextCG.cpp:
     16        (WebCore::GraphicsContext::drawLine):
     17        * rendering/RenderObject.cpp:
     18        (WebCore::RenderObject::drawLineForBoxSide):
     19
    1202014-12-22  Timothy Horton  <timothy_horton@apple.com>
    221
  • trunk/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp

    r176140 r177658  
    286286        return;
    287287
    288     float width = strokeThickness();
     288    CGContextRef context = platformContext();
     289    float thickness = strokeThickness();
     290    ASSERT(thickness);
     291    bool isVerticalLine = (point1.x() + thickness == point2.x());
     292    StrokeStyle strokeStyle = this->strokeStyle();
     293    float strokeWidth = isVerticalLine ? point2.y() - point1.y() : point2.x() - point1.x();
     294    ASSERT(strokeWidth);
     295    float cornerWidth = 0;
     296
     297    if (strokeStyle == DottedStroke || strokeStyle == DashedStroke) {
     298        // Figure out end points to ensure we always paint corners.
     299        cornerWidth = strokeStyle == DottedStroke ? thickness : std::min(2 * thickness, std::max(thickness, strokeWidth / 3));
     300        CGContextSaveGState(context);
     301        setCGFillColor(context, strokeColor(), strokeColorSpace());
     302        if (isVerticalLine) {
     303            CGContextFillRect(context, FloatRect(point1.x(), point1.y(), thickness, cornerWidth));
     304            CGContextFillRect(context, FloatRect(point1.x(), point2.y() - cornerWidth, thickness, cornerWidth));
     305        } else {
     306            CGContextFillRect(context, FloatRect(point1.x(), point1.y(), cornerWidth, thickness));
     307            CGContextFillRect(context, FloatRect(point2.x() - cornerWidth, point1.y(), cornerWidth, thickness));
     308        }
     309        CGContextRestoreGState(context);
     310        strokeWidth -= 2 * cornerWidth;
     311        float patternWidth = strokeStyle == DottedStroke ? thickness : std::min(3 * thickness, std::max(thickness, strokeWidth / 3));
     312        // Check if corner drawing sufficiently covers the line.
     313        if (strokeWidth <= patternWidth + 1)
     314            return;
     315
     316        // Pattern starts with full fill and ends with the empty fill.
     317        // 1. Let's start with the empty phase after the corner.
     318        // 2. Check if we've got odd or even number of patterns and whether they fully cover the line.
     319        // 3. In case of even number of patterns and/or remainder, move the pattern start position
     320        // so that the pattern is balanced between the corners.
     321        float patternOffset = patternWidth;
     322        int numberOfSegments = floorf(strokeWidth / patternWidth);
     323        bool oddNumberOfSegments = numberOfSegments % 2;
     324        float remainder = strokeWidth - (numberOfSegments * patternWidth);
     325        if (oddNumberOfSegments && remainder)
     326            patternOffset -= remainder / 2;
     327        else if (!oddNumberOfSegments) {
     328            if (remainder)
     329                patternOffset += patternOffset - (patternWidth + remainder)  / 2;
     330            else
     331                patternOffset += patternWidth  / 2;
     332        }
     333        const CGFloat dashedLine[2] = { static_cast<CGFloat>(patternWidth), static_cast<CGFloat>(patternWidth) };
     334        CGContextSetLineDash(context, patternOffset, dashedLine, 2);
     335    }
    289336
    290337    FloatPoint p1 = point1;
    291338    FloatPoint p2 = point2;
    292     bool isVerticalLine = (p1.x() == p2.x());
    293    
    294     // For odd widths, we add in 0.5 to the appropriate x/y so that the float arithmetic
    295     // works out.  For example, with a border width of 3, KHTML will pass us (y1+y2)/2, e.g.,
    296     // (50+53)/2 = 103/2 = 51 when we want 51.5. It is always true that an even width gave
    297     // us a perfect position, but an odd width gave us a position that is off by exactly 0.5.
    298     if (strokeStyle() == DottedStroke || strokeStyle() == DashedStroke) {
    299         if (isVerticalLine) {
    300             p1.move(0, width);
    301             p2.move(0, -width);
    302         } else {
    303             p1.move(width, 0);
    304             p2.move(-width, 0);
    305         }
    306     }
    307    
    308     if (((int)width) % 2) {
    309         if (isVerticalLine) {
    310             // We're a vertical line.  Adjust our x.
    311             p1.move(0.5f, 0.0f);
    312             p2.move(0.5f, 0.0f);
    313         } else {
    314             // We're a horizontal line. Adjust our y.
    315             p1.move(0.0f, 0.5f);
    316             p2.move(0.0f, 0.5f);
    317         }
    318     }
    319    
    320     int patWidth = 0;
    321     switch (strokeStyle()) {
    322     case NoStroke:
    323     case SolidStroke:
    324     case DoubleStroke:
    325     case WavyStroke: // FIXME: https://bugs.webkit.org/show_bug.cgi?id=94112 - Needs platform support.
    326         break;
    327     case DottedStroke:
    328         patWidth = (int)width;
    329         break;
    330     case DashedStroke:
    331         patWidth = 3 * (int)width;
    332         break;
    333     }
    334 
    335     CGContextRef context = platformContext();
     339    // Center line and cut off corners for pattern patining.
     340    if (isVerticalLine) {
     341        float centerOffset = (p2.x() - p1.x()) / 2;
     342        p1.move(centerOffset, cornerWidth);
     343        p2.move(-centerOffset, -cornerWidth);
     344    } else {
     345        float centerOffset = (p2.y() - p1.y()) / 2;
     346        p1.move(cornerWidth, centerOffset);
     347        p2.move(-cornerWidth, -centerOffset);
     348    }
    336349
    337350    if (shouldAntialias()) {
    338         bool willAntialias = false;
    339351#if PLATFORM(IOS)
    340352        // Force antialiasing on for line patterns as they don't look good with it turned off (<rdar://problem/5459772>).
    341         willAntialias = patWidth;
     353        CGContextSetShouldAntialias(context, strokeStyle == DottedStroke || strokeStyle == DashedStroke);
     354#else
     355        CGContextSetShouldAntialias(context, false);
    342356#endif
    343         CGContextSetShouldAntialias(context, willAntialias);
    344     }
    345 
    346     if (patWidth) {
    347         CGContextSaveGState(context);
    348 
    349         // Do a rect fill of our endpoints.  This ensures we always have the
    350         // appearance of being a border.  We then draw the actual dotted/dashed line.
    351         setCGFillColor(context, strokeColor(), strokeColorSpace());  // The save/restore make it safe to mutate the fill color here without setting it back to the old color.
    352         if (isVerticalLine) {
    353             CGContextFillRect(context, FloatRect(p1.x() - width / 2, p1.y() - width, width, width));
    354             CGContextFillRect(context, FloatRect(p2.x() - width / 2, p2.y(), width, width));
    355         } else {
    356             CGContextFillRect(context, FloatRect(p1.x() - width, p1.y() - width / 2, width, width));
    357             CGContextFillRect(context, FloatRect(p2.x(), p2.y() - width / 2, width, width));
    358         }
    359 
    360         // Example: 80 pixels with a width of 30 pixels.
    361         // Remainder is 20.  The maximum pixels of line we could paint
    362         // will be 50 pixels.
    363         int distance = (isVerticalLine ? (int)(point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width;
    364         int remainder = distance % patWidth;
    365         int coverage = distance - remainder;
    366         int numSegments = coverage / patWidth;
    367 
    368         float patternOffset = 0.0f;
    369         // Special case 1px dotted borders for speed.
    370         if (patWidth == 1)
    371             patternOffset = 1.0f;
    372         else {
    373             bool evenNumberOfSegments = !(numSegments % 2);
    374             if (remainder)
    375                 evenNumberOfSegments = !evenNumberOfSegments;
    376             if (evenNumberOfSegments) {
    377                 if (remainder) {
    378                     patternOffset += patWidth - remainder;
    379                     patternOffset += remainder / 2;
    380                 } else
    381                     patternOffset = patWidth / 2;
    382             } else {
    383                 if (remainder)
    384                     patternOffset = (patWidth - remainder)/2;
    385             }
    386         }
    387 
    388         const CGFloat dottedLine[2] = { static_cast<CGFloat>(patWidth), static_cast<CGFloat>(patWidth) };
    389         CGContextSetLineDash(context, patternOffset, dottedLine, 2);
    390     }
    391 
     357    }
    392358    CGContextBeginPath(context);
    393359    CGContextMoveToPoint(context, p1.x(), p1.y());
    394360    CGContextAddLineToPoint(context, p2.x(), p2.y());
    395 
    396361    CGContextStrokePath(context);
    397 
    398     if (patWidth)
    399         CGContextRestoreGState(context);
    400 
    401362    if (shouldAntialias())
    402363        CGContextSetShouldAntialias(context, true);
  • trunk/Source/WebCore/rendering/RenderObject.cpp

    r177200 r177658  
    734734        case DOTTED:
    735735        case DASHED: {
    736             if (thickness > 0) {
    737                 bool wasAntialiased = graphicsContext->shouldAntialias();
    738                 StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
    739                 graphicsContext->setShouldAntialias(antialias);
    740                 graphicsContext->setStrokeColor(color, style.colorSpace());
    741                 graphicsContext->setStrokeThickness(thickness);
    742                 graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
    743 
    744                 // FIXME: There's some odd adjustment in GraphicsContext::drawLine() that disables device pixel precision line drawing.
    745                 int adjustedX = floorToInt((x1 + x2) / 2);
    746                 int adjustedY = floorToInt((y1 + y2) / 2);
    747 
    748                 switch (side) {
    749                     case BSBottom:
    750                     case BSTop:
    751                         graphicsContext->drawLine(FloatPoint(x1, adjustedY), FloatPoint(x2, adjustedY));
    752                         break;
    753                     case BSRight:
    754                     case BSLeft:
    755                         graphicsContext->drawLine(FloatPoint(adjustedX, y1), FloatPoint(adjustedX, y2));
    756                         break;
    757                 }
    758                 graphicsContext->setShouldAntialias(wasAntialiased);
    759                 graphicsContext->setStrokeStyle(oldStrokeStyle);
    760             }
     736            bool wasAntialiased = graphicsContext->shouldAntialias();
     737            StrokeStyle oldStrokeStyle = graphicsContext->strokeStyle();
     738            graphicsContext->setShouldAntialias(antialias);
     739            graphicsContext->setStrokeColor(color, style.colorSpace());
     740            graphicsContext->setStrokeThickness(thickness);
     741            graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : DottedStroke);
     742            graphicsContext->drawLine(roundPointToDevicePixels(LayoutPoint(x1, y1), deviceScaleFactor), roundPointToDevicePixels(LayoutPoint(x2, y2), deviceScaleFactor));
     743            graphicsContext->setShouldAntialias(wasAntialiased);
     744            graphicsContext->setStrokeStyle(oldStrokeStyle);
    761745            break;
    762746        }
Note: See TracChangeset for help on using the changeset viewer.