OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) | 4 * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) |
5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) | 5 * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) |
6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. | 6 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
7 * Copyright (C) 2010 Google Inc. All rights reserved. | 7 * Copyright (C) 2010 Google Inc. All rights reserved. |
8 * | 8 * |
9 * This library is free software; you can redistribute it and/or | 9 * This library is free software; you can redistribute it and/or |
10 * modify it under the terms of the GNU Library General Public | 10 * modify it under the terms of the GNU Library General Public |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 } | 304 } |
305 | 305 |
306 LayoutUnit RenderBoxModelObject::computedCSSPadding(const Length& padding) const | 306 LayoutUnit RenderBoxModelObject::computedCSSPadding(const Length& padding) const |
307 { | 307 { |
308 LayoutUnit w = 0; | 308 LayoutUnit w = 0; |
309 if (padding.isPercent()) | 309 if (padding.isPercent()) |
310 w = containingBlockLogicalWidthForContent(); | 310 w = containingBlockLogicalWidthForContent(); |
311 return minimumValueForLength(padding, w); | 311 return minimumValueForLength(padding, w); |
312 } | 312 } |
313 | 313 |
314 // FIXME: See crbug.com/382491. The use of getCTM in this context is incorrect b
ecause the matrix returned does not | |
315 // include scales applied at raster time, such as the device zoom. | |
316 static LayoutRect shrinkRectByOnePixel(GraphicsContext* context, const LayoutRec
t& rect) | |
317 { | |
318 LayoutRect shrunkRect = rect; | |
319 AffineTransform transform = context->getCTM(); | |
320 shrunkRect.inflateX(-static_cast<LayoutUnit>(ceil(1 / transform.xScale()))); | |
321 shrunkRect.inflateY(-static_cast<LayoutUnit>(ceil(1 / transform.yScale()))); | |
322 return shrunkRect; | |
323 } | |
324 | |
325 LayoutRect RenderBoxModelObject::borderInnerRectAdjustedForBleedAvoidance(Graphi
csContext* context, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoida
nce) const | |
326 { | |
327 // We shrink the rectangle by one pixel on each side to make it fully overla
p the anti-aliased background border | |
328 return (bleedAvoidance == BackgroundBleedBackgroundOverBorder) ? shrinkRectB
yOnePixel(context, rect) : rect; | |
329 } | |
330 | |
331 static inline int resolveWidthForRatio(int height, const FloatSize& intrinsicRat
io) | 314 static inline int resolveWidthForRatio(int height, const FloatSize& intrinsicRat
io) |
332 { | 315 { |
333 return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height()); | 316 return ceilf(height * intrinsicRatio.width() / intrinsicRatio.height()); |
334 } | 317 } |
335 | 318 |
336 static inline int resolveHeightForRatio(int width, const FloatSize& intrinsicRat
io) | 319 static inline int resolveHeightForRatio(int width, const FloatSize& intrinsicRat
io) |
337 { | 320 { |
338 return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width()); | 321 return ceilf(width * intrinsicRatio.height() / intrinsicRatio.width()); |
339 } | 322 } |
340 | 323 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
411 // largest dimensions at that ratio such that neither dimension exceeds the
dimensions of the rectangle that | 394 // largest dimensions at that ratio such that neither dimension exceeds the
dimensions of the rectangle that |
412 // establishes the coordinate system for the 'background-position' property. | 395 // establishes the coordinate system for the 'background-position' property. |
413 if (!intrinsicRatio.isEmpty()) | 396 if (!intrinsicRatio.isEmpty()) |
414 return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio)
; | 397 return resolveAgainstIntrinsicRatio(positioningAreaSize, intrinsicRatio)
; |
415 | 398 |
416 // If the image has no intrinsic ratio either, then the dimensions must be a
ssumed to be the rectangle that | 399 // If the image has no intrinsic ratio either, then the dimensions must be a
ssumed to be the rectangle that |
417 // establishes the coordinate system for the 'background-position' property. | 400 // establishes the coordinate system for the 'background-position' property. |
418 return positioningAreaSize; | 401 return positioningAreaSize; |
419 } | 402 } |
420 | 403 |
421 static LayoutUnit computeBorderImageSide(const BorderImageLength& borderSlice, L
ayoutUnit borderSide, LayoutUnit imageSide, LayoutUnit boxExtent) | |
422 { | |
423 if (borderSlice.isNumber()) | |
424 return borderSlice.number() * borderSide; | |
425 if (borderSlice.length().isAuto()) | |
426 return imageSide; | |
427 return valueForLength(borderSlice.length(), boxExtent); | |
428 } | |
429 | |
430 bool RenderBoxModelObject::paintNinePieceImage(GraphicsContext* graphicsContext,
const LayoutRect& rect, const RenderStyle* style, | |
431 const NinePieceImage& ninePieceIm
age, CompositeOperator op) | |
432 { | |
433 StyleImage* styleImage = ninePieceImage.image(); | |
434 if (!styleImage) | |
435 return false; | |
436 | |
437 if (!styleImage->isLoaded()) | |
438 return true; // Never paint a nine-piece image incrementally, but don't
paint the fallback borders either. | |
439 | |
440 if (!styleImage->canRender(*this, style->effectiveZoom())) | |
441 return false; | |
442 | |
443 // FIXME: border-image is broken with full page zooming when tiling has to h
appen, since the tiling function | |
444 // doesn't have any understanding of the zoom that is in effect on the tile. | |
445 LayoutRect rectWithOutsets = rect; | |
446 rectWithOutsets.expand(style->imageOutsets(ninePieceImage)); | |
447 IntRect borderImageRect = pixelSnappedIntRect(rectWithOutsets); | |
448 | |
449 IntSize imageSize = calculateImageIntrinsicDimensions(styleImage, borderImag
eRect.size(), DoNotScaleByEffectiveZoom); | |
450 | |
451 // If both values are ‘auto’ then the intrinsic width and/or height of the i
mage should be used, if any. | |
452 styleImage->setContainerSizeForRenderer(this, imageSize, style->effectiveZoo
m()); | |
453 | |
454 int imageWidth = imageSize.width(); | |
455 int imageHeight = imageSize.height(); | |
456 | |
457 float imageScaleFactor = styleImage->imageScaleFactor(); | |
458 int topSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.imag
eSlices().top(), imageHeight)) * imageScaleFactor; | |
459 int rightSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.ima
geSlices().right(), imageWidth)) * imageScaleFactor; | |
460 int bottomSlice = std::min<int>(imageHeight, valueForLength(ninePieceImage.i
mageSlices().bottom(), imageHeight)) * imageScaleFactor; | |
461 int leftSlice = std::min<int>(imageWidth, valueForLength(ninePieceImage.imag
eSlices().left(), imageWidth)) * imageScaleFactor; | |
462 | |
463 ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); | |
464 ENinePieceImageRule vRule = ninePieceImage.verticalRule(); | |
465 | |
466 int topWidth = computeBorderImageSide(ninePieceImage.borderSlices().top(), s
tyle->borderTopWidth(), topSlice, borderImageRect.height()); | |
467 int rightWidth = computeBorderImageSide(ninePieceImage.borderSlices().right(
), style->borderRightWidth(), rightSlice, borderImageRect.width()); | |
468 int bottomWidth = computeBorderImageSide(ninePieceImage.borderSlices().botto
m(), style->borderBottomWidth(), bottomSlice, borderImageRect.height()); | |
469 int leftWidth = computeBorderImageSide(ninePieceImage.borderSlices().left(),
style->borderLeftWidth(), leftSlice, borderImageRect.width()); | |
470 | |
471 // Reduce the widths if they're too large. | |
472 // The spec says: Given Lwidth as the width of the border image area, Lheigh
t as its height, and Wside as the border image width | |
473 // offset for the side, let f = min(Lwidth/(Wleft+Wright), Lheight/(Wtop+Wbo
ttom)). If f < 1, then all W are reduced by | |
474 // multiplying them by f. | |
475 int borderSideWidth = std::max(1, leftWidth + rightWidth); | |
476 int borderSideHeight = std::max(1, topWidth + bottomWidth); | |
477 float borderSideScaleFactor = std::min((float)borderImageRect.width() / bord
erSideWidth, (float)borderImageRect.height() / borderSideHeight); | |
478 if (borderSideScaleFactor < 1) { | |
479 topWidth *= borderSideScaleFactor; | |
480 rightWidth *= borderSideScaleFactor; | |
481 bottomWidth *= borderSideScaleFactor; | |
482 leftWidth *= borderSideScaleFactor; | |
483 } | |
484 | |
485 bool drawLeft = leftSlice > 0 && leftWidth > 0; | |
486 bool drawTop = topSlice > 0 && topWidth > 0; | |
487 bool drawRight = rightSlice > 0 && rightWidth > 0; | |
488 bool drawBottom = bottomSlice > 0 && bottomWidth > 0; | |
489 bool drawMiddle = ninePieceImage.fill() && (imageWidth - leftSlice - rightSl
ice) > 0 && (borderImageRect.width() - leftWidth - rightWidth) > 0 | |
490 && (imageHeight - topSlice - bottomSlice) > 0 && (borderIm
ageRect.height() - topWidth - bottomWidth) > 0; | |
491 | |
492 RefPtr<Image> image = styleImage->image(this, imageSize); | |
493 | |
494 float destinationWidth = borderImageRect.width() - leftWidth - rightWidth; | |
495 float destinationHeight = borderImageRect.height() - topWidth - bottomWidth; | |
496 | |
497 float sourceWidth = imageWidth - leftSlice - rightSlice; | |
498 float sourceHeight = imageHeight - topSlice - bottomSlice; | |
499 | |
500 float leftSideScale = drawLeft ? (float)leftWidth / leftSlice : 1; | |
501 float rightSideScale = drawRight ? (float)rightWidth / rightSlice : 1; | |
502 float topSideScale = drawTop ? (float)topWidth / topSlice : 1; | |
503 float bottomSideScale = drawBottom ? (float)bottomWidth / bottomSlice : 1; | |
504 | |
505 if (drawLeft) { | |
506 // Paint the top and bottom left corners. | |
507 | |
508 // The top left corner rect is (tx, ty, leftWidth, topWidth) | |
509 // The rect to use from within the image is obtained from our slice, and
is (0, 0, leftSlice, topSlice) | |
510 if (drawTop) | |
511 graphicsContext->drawImage(image.get(), IntRect(borderImageRect.loca
tion(), IntSize(leftWidth, topWidth)), | |
512 LayoutRect(0, 0, leftSlice, topSlice), op
); | |
513 | |
514 // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth,
bottomWidth) | |
515 // The rect to use from within the image is (0, imageHeight - bottomSlic
e, leftSlice, botomSlice) | |
516 if (drawBottom) | |
517 graphicsContext->drawImage(image.get(), IntRect(borderImageRect.x(),
borderImageRect.maxY() - bottomWidth, leftWidth, bottomWidth), | |
518 LayoutRect(0, imageHeight - bottomSlice,
leftSlice, bottomSlice), op); | |
519 | |
520 // Paint the left edge. | |
521 // Have to scale and tile into the border rect. | |
522 if (sourceHeight > 0) | |
523 graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect
.x(), borderImageRect.y() + topWidth, leftWidth, destinationHeight), | |
524 IntRect(0, topSlice, leftSlice, sour
ceHeight), | |
525 FloatSize(leftSideScale, leftSideSca
le), Image::StretchTile, (Image::TileRule)vRule, op); | |
526 } | |
527 | |
528 if (drawRight) { | |
529 // Paint the top and bottom right corners | |
530 // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, to
pWidth) | |
531 // The rect to use from within the image is obtained from our slice, and
is (imageWidth - rightSlice, 0, rightSlice, topSlice) | |
532 if (drawTop) | |
533 graphicsContext->drawImage(image.get(), IntRect(borderImageRect.maxX
() - rightWidth, borderImageRect.y(), rightWidth, topWidth), | |
534 LayoutRect(imageWidth - rightSlice, 0, ri
ghtSlice, topSlice), op); | |
535 | |
536 // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottom
Width, rightWidth, bottomWidth) | |
537 // The rect to use from within the image is (imageWidth - rightSlice, im
ageHeight - bottomSlice, rightSlice, bottomSlice) | |
538 if (drawBottom) | |
539 graphicsContext->drawImage(image.get(), IntRect(borderImageRect.maxX
() - rightWidth, borderImageRect.maxY() - bottomWidth, rightWidth, bottomWidth), | |
540 LayoutRect(imageWidth - rightSlice, image
Height - bottomSlice, rightSlice, bottomSlice), op); | |
541 | |
542 // Paint the right edge. | |
543 if (sourceHeight > 0) | |
544 graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect
.maxX() - rightWidth, borderImageRect.y() + topWidth, rightWidth, | |
545 destinationHeight), | |
546 IntRect(imageWidth - rightSlice, top
Slice, rightSlice, sourceHeight), | |
547 FloatSize(rightSideScale, rightSideS
cale), | |
548 Image::StretchTile, (Image::TileRule
)vRule, op); | |
549 } | |
550 | |
551 // Paint the top edge. | |
552 if (drawTop && sourceWidth > 0) | |
553 graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x()
+ leftWidth, borderImageRect.y(), destinationWidth, topWidth), | |
554 IntRect(leftSlice, 0, sourceWidth, topSl
ice), | |
555 FloatSize(topSideScale, topSideScale), (
Image::TileRule)hRule, Image::StretchTile, op); | |
556 | |
557 // Paint the bottom edge. | |
558 if (drawBottom && sourceWidth > 0) | |
559 graphicsContext->drawTiledImage(image.get(), IntRect(borderImageRect.x()
+ leftWidth, borderImageRect.maxY() - bottomWidth, | |
560 destinationWidth, bottomWidth), | |
561 IntRect(leftSlice, imageHeight - bottomS
lice, sourceWidth, bottomSlice), | |
562 FloatSize(bottomSideScale, bottomSideSca
le), | |
563 (Image::TileRule)hRule, Image::StretchTi
le, op); | |
564 | |
565 // Paint the middle. | |
566 if (drawMiddle) { | |
567 FloatSize middleScaleFactor(1, 1); | |
568 if (drawTop) | |
569 middleScaleFactor.setWidth(topSideScale); | |
570 else if (drawBottom) | |
571 middleScaleFactor.setWidth(bottomSideScale); | |
572 if (drawLeft) | |
573 middleScaleFactor.setHeight(leftSideScale); | |
574 else if (drawRight) | |
575 middleScaleFactor.setHeight(rightSideScale); | |
576 | |
577 // For "stretch" rules, just override the scale factor and replace. We o
nly had to do this for the | |
578 // center tile, since sides don't even use the scale factor unless they
have a rule other than "stretch". | |
579 // The middle however can have "stretch" specified in one axis but not t
he other, so we have to | |
580 // correct the scale here. | |
581 if (hRule == StretchImageRule) | |
582 middleScaleFactor.setWidth(destinationWidth / sourceWidth); | |
583 | |
584 if (vRule == StretchImageRule) | |
585 middleScaleFactor.setHeight(destinationHeight / sourceHeight); | |
586 | |
587 graphicsContext->drawTiledImage(image.get(), | |
588 IntRect(borderImageRect.x() + leftWidth, borderImageRect.y() + topWi
dth, destinationWidth, destinationHeight), | |
589 IntRect(leftSlice, topSlice, sourceWidth, sourceHeight), | |
590 middleScaleFactor, (Image::TileRule)hRule, (Image::TileRule)vRule, o
p); | |
591 } | |
592 | |
593 return true; | |
594 } | |
595 | |
596 static bool allCornersClippedOut(const RoundedRect& border, const LayoutRect& cl
ipRect) | |
597 { | |
598 LayoutRect boundingRect = border.rect(); | |
599 if (clipRect.contains(boundingRect)) | |
600 return false; | |
601 | |
602 RoundedRect::Radii radii = border.radii(); | |
603 | |
604 LayoutRect topLeftRect(boundingRect.location(), radii.topLeft()); | |
605 if (clipRect.intersects(topLeftRect)) | |
606 return false; | |
607 | |
608 LayoutRect topRightRect(boundingRect.location(), radii.topRight()); | |
609 topRightRect.setX(boundingRect.maxX() - topRightRect.width()); | |
610 if (clipRect.intersects(topRightRect)) | |
611 return false; | |
612 | |
613 LayoutRect bottomLeftRect(boundingRect.location(), radii.bottomLeft()); | |
614 bottomLeftRect.setY(boundingRect.maxY() - bottomLeftRect.height()); | |
615 if (clipRect.intersects(bottomLeftRect)) | |
616 return false; | |
617 | |
618 LayoutRect bottomRightRect(boundingRect.location(), radii.bottomRight()); | |
619 bottomRightRect.setX(boundingRect.maxX() - bottomRightRect.width()); | |
620 bottomRightRect.setY(boundingRect.maxY() - bottomRightRect.height()); | |
621 if (clipRect.intersects(bottomRightRect)) | |
622 return false; | |
623 | |
624 return true; | |
625 } | |
626 | |
627 static bool borderWillArcInnerEdge(const LayoutSize& firstRadius, const FloatSiz
e& secondRadius) | |
628 { | |
629 return !firstRadius.isZero() || !secondRadius.isZero(); | |
630 } | |
631 | |
632 enum BorderEdgeFlag { | |
633 TopBorderEdge = 1 << BSTop, | |
634 RightBorderEdge = 1 << BSRight, | |
635 BottomBorderEdge = 1 << BSBottom, | |
636 LeftBorderEdge = 1 << BSLeft, | |
637 AllBorderEdges = TopBorderEdge | BottomBorderEdge | LeftBorderEdge | RightBo
rderEdge | |
638 }; | |
639 | |
640 static inline BorderEdgeFlag edgeFlagForSide(BoxSide side) | |
641 { | |
642 return static_cast<BorderEdgeFlag>(1 << side); | |
643 } | |
644 | |
645 static inline bool includesEdge(BorderEdgeFlags flags, BoxSide side) | |
646 { | |
647 return flags & edgeFlagForSide(side); | |
648 } | |
649 | |
650 static inline bool includesAdjacentEdges(BorderEdgeFlags flags) | |
651 { | |
652 return (flags & (TopBorderEdge | RightBorderEdge)) == (TopBorderEdge | Right
BorderEdge) | |
653 || (flags & (RightBorderEdge | BottomBorderEdge)) == (RightBorderEdge |
BottomBorderEdge) | |
654 || (flags & (BottomBorderEdge | LeftBorderEdge)) == (BottomBorderEdge |
LeftBorderEdge) | |
655 || (flags & (LeftBorderEdge | TopBorderEdge)) == (LeftBorderEdge | TopBo
rderEdge); | |
656 } | |
657 | |
658 inline bool styleRequiresClipPolygon(EBorderStyle style) | |
659 { | |
660 return style == DOTTED || style == DASHED; // These are drawn with a stroke,
so we have to clip to get corner miters. | |
661 } | |
662 | |
663 static bool borderStyleFillsBorderArea(EBorderStyle style) | |
664 { | |
665 return !(style == DOTTED || style == DASHED || style == DOUBLE); | |
666 } | |
667 | |
668 static bool borderStyleHasInnerDetail(EBorderStyle style) | |
669 { | |
670 return style == GROOVE || style == RIDGE || style == DOUBLE; | |
671 } | |
672 | |
673 static bool borderStyleIsDottedOrDashed(EBorderStyle style) | |
674 { | |
675 return style == DOTTED || style == DASHED; | |
676 } | |
677 | |
678 // OUTSET darkens the bottom and right (and maybe lightens the top and left) | |
679 // INSET darkens the top and left (and maybe lightens the bottom and right) | |
680 static inline bool borderStyleHasUnmatchedColorsAtCorner(EBorderStyle style, Box
Side side, BoxSide adjacentSide) | |
681 { | |
682 // These styles match at the top/left and bottom/right. | |
683 if (style == INSET || style == GROOVE || style == RIDGE || style == OUTSET)
{ | |
684 const BorderEdgeFlags topRightFlags = edgeFlagForSide(BSTop) | edgeFlagF
orSide(BSRight); | |
685 const BorderEdgeFlags bottomLeftFlags = edgeFlagForSide(BSBottom) | edge
FlagForSide(BSLeft); | |
686 | |
687 BorderEdgeFlags flags = edgeFlagForSide(side) | edgeFlagForSide(adjacent
Side); | |
688 return flags == topRightFlags || flags == bottomLeftFlags; | |
689 } | |
690 return false; | |
691 } | |
692 | |
693 static inline bool colorsMatchAtCorner(BoxSide side, BoxSide adjacentSide, const
BorderEdge edges[]) | |
694 { | |
695 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) | |
696 return false; | |
697 | |
698 if (!edges[side].sharesColorWith(edges[adjacentSide])) | |
699 return false; | |
700 | |
701 return !borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), sid
e, adjacentSide); | |
702 } | |
703 | |
704 | |
705 static inline bool colorNeedsAntiAliasAtCorner(BoxSide side, BoxSide adjacentSid
e, const BorderEdge edges[]) | |
706 { | |
707 if (!edges[side].color.hasAlpha()) | |
708 return false; | |
709 | |
710 if (edges[side].shouldRender() != edges[adjacentSide].shouldRender()) | |
711 return false; | |
712 | |
713 if (!edges[side].sharesColorWith(edges[adjacentSide])) | |
714 return true; | |
715 | |
716 return borderStyleHasUnmatchedColorsAtCorner(edges[side].borderStyle(), side
, adjacentSide); | |
717 } | |
718 | |
719 // This assumes that we draw in order: top, bottom, left, right. | |
720 static inline bool willBeOverdrawn(BoxSide side, BoxSide adjacentSide, const Bor
derEdge edges[]) | |
721 { | |
722 switch (side) { | |
723 case BSTop: | |
724 case BSBottom: | |
725 if (edges[adjacentSide].presentButInvisible()) | |
726 return false; | |
727 | |
728 if (!edges[side].sharesColorWith(edges[adjacentSide]) && edges[adjacentS
ide].color.hasAlpha()) | |
729 return false; | |
730 | |
731 if (!borderStyleFillsBorderArea(edges[adjacentSide].borderStyle())) | |
732 return false; | |
733 | |
734 return true; | |
735 | |
736 case BSLeft: | |
737 case BSRight: | |
738 // These draw last, so are never overdrawn. | |
739 return false; | |
740 } | |
741 return false; | |
742 } | |
743 | |
744 static inline bool borderStylesRequireMitre(BoxSide side, BoxSide adjacentSide,
EBorderStyle style, EBorderStyle adjacentStyle) | |
745 { | |
746 if (style == DOUBLE || adjacentStyle == DOUBLE || adjacentStyle == GROOVE ||
adjacentStyle == RIDGE) | |
747 return true; | |
748 | |
749 if (borderStyleIsDottedOrDashed(style) != borderStyleIsDottedOrDashed(adjace
ntStyle)) | |
750 return true; | |
751 | |
752 if (style != adjacentStyle) | |
753 return true; | |
754 | |
755 return borderStyleHasUnmatchedColorsAtCorner(style, side, adjacentSide); | |
756 } | |
757 | |
758 static bool joinRequiresMitre(BoxSide side, BoxSide adjacentSide, const BorderEd
ge edges[], bool allowOverdraw) | |
759 { | |
760 if ((edges[side].isTransparent && edges[adjacentSide].isTransparent) || !edg
es[adjacentSide].isPresent) | |
761 return false; | |
762 | |
763 if (allowOverdraw && willBeOverdrawn(side, adjacentSide, edges)) | |
764 return false; | |
765 | |
766 if (!edges[side].sharesColorWith(edges[adjacentSide])) | |
767 return true; | |
768 | |
769 if (borderStylesRequireMitre(side, adjacentSide, edges[side].borderStyle(),
edges[adjacentSide].borderStyle())) | |
770 return true; | |
771 | |
772 return false; | |
773 } | |
774 | |
775 void RenderBoxModelObject::paintOneBorderSide(GraphicsContext* graphicsContext,
const RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& inn
erBorder, | |
776 const IntRect& sideRect, BoxSide side, BoxSide adjacentSide1, BoxSide adjace
ntSide2, const BorderEdge edges[], const Path* path, | |
777 BackgroundBleedAvoidance bleedAvoidance, bool includeLogicalLeftEdge, bool i
ncludeLogicalRightEdge, bool antialias, const Color* overrideColor) | |
778 { | |
779 const BorderEdge& edgeToRender = edges[side]; | |
780 ASSERT(edgeToRender.width); | |
781 const BorderEdge& adjacentEdge1 = edges[adjacentSide1]; | |
782 const BorderEdge& adjacentEdge2 = edges[adjacentSide2]; | |
783 | |
784 bool mitreAdjacentSide1 = joinRequiresMitre(side, adjacentSide1, edges, !ant
ialias); | |
785 bool mitreAdjacentSide2 = joinRequiresMitre(side, adjacentSide2, edges, !ant
ialias); | |
786 | |
787 bool adjacentSide1StylesMatch = colorsMatchAtCorner(side, adjacentSide1, edg
es); | |
788 bool adjacentSide2StylesMatch = colorsMatchAtCorner(side, adjacentSide2, edg
es); | |
789 | |
790 const Color& colorToPaint = overrideColor ? *overrideColor : edgeToRender.co
lor; | |
791 | |
792 if (path) { | |
793 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
794 if (innerBorder.isRenderable()) | |
795 clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, sid
e, adjacentSide1StylesMatch, adjacentSide2StylesMatch); | |
796 else | |
797 clipBorderSideForComplexInnerPath(graphicsContext, outerBorder, inne
rBorder, side, edges); | |
798 float thickness = std::max(std::max(edgeToRender.width, adjacentEdge1.wi
dth), adjacentEdge2.width); | |
799 drawBoxSideFromPath(graphicsContext, outerBorder.rect(), *path, edges, e
dgeToRender.width, thickness, side, style, | |
800 colorToPaint, edgeToRender.borderStyle(), bleedAvoidance, includeLog
icalLeftEdge, includeLogicalRightEdge); | |
801 } else { | |
802 bool clipForStyle = styleRequiresClipPolygon(edgeToRender.borderStyle())
&& (mitreAdjacentSide1 || mitreAdjacentSide2); | |
803 bool clipAdjacentSide1 = colorNeedsAntiAliasAtCorner(side, adjacentSide1
, edges) && mitreAdjacentSide1; | |
804 bool clipAdjacentSide2 = colorNeedsAntiAliasAtCorner(side, adjacentSide2
, edges) && mitreAdjacentSide2; | |
805 bool shouldClip = clipForStyle || clipAdjacentSide1 || clipAdjacentSide2
; | |
806 | |
807 GraphicsContextStateSaver clipStateSaver(*graphicsContext, shouldClip); | |
808 if (shouldClip) { | |
809 bool aliasAdjacentSide1 = clipAdjacentSide1 || (clipForStyle && mitr
eAdjacentSide1); | |
810 bool aliasAdjacentSide2 = clipAdjacentSide2 || (clipForStyle && mitr
eAdjacentSide2); | |
811 clipBorderSidePolygon(graphicsContext, outerBorder, innerBorder, sid
e, !aliasAdjacentSide1, !aliasAdjacentSide2); | |
812 // Since we clipped, no need to draw with a mitre. | |
813 mitreAdjacentSide1 = false; | |
814 mitreAdjacentSide2 = false; | |
815 } | |
816 | |
817 drawLineForBoxSide(graphicsContext, sideRect.x(), sideRect.y(), sideRect
.maxX(), sideRect.maxY(), side, colorToPaint, edgeToRender.borderStyle(), | |
818 mitreAdjacentSide1 ? adjacentEdge1.width : 0, mitreAdjacentSide2
? adjacentEdge2.width : 0, antialias); | |
819 } | |
820 } | |
821 | |
822 static IntRect calculateSideRect(const RoundedRect& outerBorder, const BorderEdg
e edges[], int side) | |
823 { | |
824 IntRect sideRect = outerBorder.rect(); | |
825 int width = edges[side].width; | |
826 | |
827 if (side == BSTop) | |
828 sideRect.setHeight(width); | |
829 else if (side == BSBottom) | |
830 sideRect.shiftYEdgeTo(sideRect.maxY() - width); | |
831 else if (side == BSLeft) | |
832 sideRect.setWidth(width); | |
833 else | |
834 sideRect.shiftXEdgeTo(sideRect.maxX() - width); | |
835 | |
836 return sideRect; | |
837 } | |
838 | |
839 void RenderBoxModelObject::paintBorderSides(GraphicsContext* graphicsContext, co
nst RenderStyle* style, const RoundedRect& outerBorder, const RoundedRect& inner
Border, | |
840 const IntPoint& innerBorderAdjustment, const BorderEdge edges[], BorderEdgeF
lags edgeSet, BackgroundBleedAvoidance bleedAvoidance, | |
841 bool includeLogicalLeftEdge, bool includeLogicalRightEdge, bool antialias, c
onst Color* overrideColor) | |
842 { | |
843 bool renderRadii = outerBorder.isRounded(); | |
844 | |
845 Path roundedPath; | |
846 if (renderRadii) | |
847 roundedPath.addRoundedRect(outerBorder); | |
848 | |
849 // The inner border adjustment for bleed avoidance mode BackgroundBleedBackg
roundOverBorder | |
850 // is only applied to sideRect, which is okay since BackgroundBleedBackgroun
dOverBorder | |
851 // is only to be used for solid borders and the shape of the border painted
by drawBoxSideFromPath | |
852 // only depends on sideRect when painting solid borders. | |
853 | |
854 if (edges[BSTop].shouldRender() && includesEdge(edgeSet, BSTop)) { | |
855 IntRect sideRect = outerBorder.rect(); | |
856 sideRect.setHeight(edges[BSTop].width + innerBorderAdjustment.y()); | |
857 | |
858 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSTop].bo
rderStyle()) || borderWillArcInnerEdge(innerBorder.radii().topLeft(), innerBorde
r.radii().topRight())); | |
859 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSTop, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoidance
, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
860 } | |
861 | |
862 if (edges[BSBottom].shouldRender() && includesEdge(edgeSet, BSBottom)) { | |
863 IntRect sideRect = outerBorder.rect(); | |
864 sideRect.shiftYEdgeTo(sideRect.maxY() - edges[BSBottom].width - innerBor
derAdjustment.y()); | |
865 | |
866 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSBottom]
.borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), inne
rBorder.radii().bottomRight())); | |
867 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSBottom, BSLeft, BSRight, edges, usePath ? &roundedPath : 0, bleedAvoida
nce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
868 } | |
869 | |
870 if (edges[BSLeft].shouldRender() && includesEdge(edgeSet, BSLeft)) { | |
871 IntRect sideRect = outerBorder.rect(); | |
872 sideRect.setWidth(edges[BSLeft].width + innerBorderAdjustment.x()); | |
873 | |
874 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSLeft].b
orderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomLeft(), innerB
order.radii().topLeft())); | |
875 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSLeft, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidanc
e, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
876 } | |
877 | |
878 if (edges[BSRight].shouldRender() && includesEdge(edgeSet, BSRight)) { | |
879 IntRect sideRect = outerBorder.rect(); | |
880 sideRect.shiftXEdgeTo(sideRect.maxX() - edges[BSRight].width - innerBord
erAdjustment.x()); | |
881 | |
882 bool usePath = renderRadii && (borderStyleHasInnerDetail(edges[BSRight].
borderStyle()) || borderWillArcInnerEdge(innerBorder.radii().bottomRight(), inne
rBorder.radii().topRight())); | |
883 paintOneBorderSide(graphicsContext, style, outerBorder, innerBorder, sid
eRect, BSRight, BSTop, BSBottom, edges, usePath ? &roundedPath : 0, bleedAvoidan
ce, includeLogicalLeftEdge, includeLogicalRightEdge, antialias, overrideColor); | |
884 } | |
885 } | |
886 | |
887 void RenderBoxModelObject::paintTranslucentBorderSides(GraphicsContext* graphics
Context, const RenderStyle* style, const RoundedRect& outerBorder, const Rounded
Rect& innerBorder, const IntPoint& innerBorderAdjustment, | |
888 const BorderEdge edges[], BorderEdgeFlags edgesToDraw, BackgroundBleedAvoida
nce bleedAvoidance, bool includeLogicalLeftEdge, bool includeLogicalRightEdge, b
ool antialias) | |
889 { | |
890 // willBeOverdrawn assumes that we draw in order: top, bottom, left, right. | |
891 // This is different from BoxSide enum order. | |
892 static const BoxSide paintOrder[] = { BSTop, BSBottom, BSLeft, BSRight }; | |
893 | |
894 while (edgesToDraw) { | |
895 // Find undrawn edges sharing a color. | |
896 Color commonColor; | |
897 | |
898 BorderEdgeFlags commonColorEdgeSet = 0; | |
899 for (size_t i = 0; i < sizeof(paintOrder) / sizeof(paintOrder[0]); ++i)
{ | |
900 BoxSide currSide = paintOrder[i]; | |
901 if (!includesEdge(edgesToDraw, currSide)) | |
902 continue; | |
903 | |
904 bool includeEdge; | |
905 if (!commonColorEdgeSet) { | |
906 commonColor = edges[currSide].color; | |
907 includeEdge = true; | |
908 } else | |
909 includeEdge = edges[currSide].color == commonColor; | |
910 | |
911 if (includeEdge) | |
912 commonColorEdgeSet |= edgeFlagForSide(currSide); | |
913 } | |
914 | |
915 bool useTransparencyLayer = includesAdjacentEdges(commonColorEdgeSet) &&
commonColor.hasAlpha(); | |
916 if (useTransparencyLayer) { | |
917 graphicsContext->beginTransparencyLayer(static_cast<float>(commonCol
or.alpha()) / 255); | |
918 commonColor = Color(commonColor.red(), commonColor.green(), commonCo
lor.blue()); | |
919 } | |
920 | |
921 paintBorderSides(graphicsContext, style, outerBorder, innerBorder, inner
BorderAdjustment, edges, commonColorEdgeSet, bleedAvoidance, includeLogicalLeftE
dge, includeLogicalRightEdge, antialias, &commonColor); | |
922 | |
923 if (useTransparencyLayer) | |
924 graphicsContext->endLayer(); | |
925 | |
926 edgesToDraw &= ~commonColorEdgeSet; | |
927 } | |
928 } | |
929 | |
930 void RenderBoxModelObject::paintBorder(const PaintInfo& info, const LayoutRect&
rect, const RenderStyle* style, | |
931 BackgroundBleedAvoidance bleedAvoidance,
bool includeLogicalLeftEdge, bool includeLogicalRightEdge) | |
932 { | |
933 GraphicsContext* graphicsContext = info.context; | |
934 // border-image is not affected by border-radius. | |
935 if (paintNinePieceImage(graphicsContext, rect, style, style->borderImage())) | |
936 return; | |
937 | |
938 BorderEdge edges[4]; | |
939 style->getBorderEdgeInfo(edges, includeLogicalLeftEdge, includeLogicalRightE
dge); | |
940 RoundedRect outerBorder = style->getRoundedBorderFor(rect, includeLogicalLef
tEdge, includeLogicalRightEdge); | |
941 RoundedRect innerBorder = style->getRoundedInnerBorderFor(borderInnerRectAdj
ustedForBleedAvoidance(graphicsContext, rect, bleedAvoidance), includeLogicalLef
tEdge, includeLogicalRightEdge); | |
942 | |
943 if (outerBorder.rect().isEmpty()) | |
944 return; | |
945 | |
946 bool haveAlphaColor = false; | |
947 bool haveAllSolidEdges = true; | |
948 bool haveAllDoubleEdges = true; | |
949 int numEdgesVisible = 4; | |
950 bool allEdgesShareColor = true; | |
951 bool allEdgesShareWidth = true; | |
952 int firstVisibleEdge = -1; | |
953 BorderEdgeFlags edgesToDraw = 0; | |
954 | |
955 for (int i = BSTop; i <= BSLeft; ++i) { | |
956 const BorderEdge& currEdge = edges[i]; | |
957 | |
958 if (edges[i].shouldRender()) | |
959 edgesToDraw |= edgeFlagForSide(static_cast<BoxSide>(i)); | |
960 | |
961 if (currEdge.presentButInvisible()) { | |
962 --numEdgesVisible; | |
963 allEdgesShareColor = false; | |
964 allEdgesShareWidth = false; | |
965 continue; | |
966 } | |
967 | |
968 if (!currEdge.shouldRender()) { | |
969 --numEdgesVisible; | |
970 continue; | |
971 } | |
972 | |
973 if (firstVisibleEdge == -1) { | |
974 firstVisibleEdge = i; | |
975 } else { | |
976 if (currEdge.color != edges[firstVisibleEdge].color) | |
977 allEdgesShareColor = false; | |
978 if (currEdge.width != edges[firstVisibleEdge].width) | |
979 allEdgesShareWidth = false; | |
980 } | |
981 | |
982 if (currEdge.color.hasAlpha()) | |
983 haveAlphaColor = true; | |
984 | |
985 if (currEdge.borderStyle() != SOLID) | |
986 haveAllSolidEdges = false; | |
987 | |
988 if (currEdge.borderStyle() != DOUBLE) | |
989 haveAllDoubleEdges = false; | |
990 } | |
991 | |
992 // If no corner intersects the clip region, we can pretend outerBorder is | |
993 // rectangular to improve performance. | |
994 if (haveAllSolidEdges && outerBorder.isRounded() && allCornersClippedOut(out
erBorder, info.rect)) | |
995 outerBorder.setRadii(RoundedRect::Radii()); | |
996 | |
997 // isRenderable() check avoids issue described in https://bugs.webkit.org/sh
ow_bug.cgi?id=38787 | |
998 if ((haveAllSolidEdges || haveAllDoubleEdges) && allEdgesShareColor && inner
Border.isRenderable()) { | |
999 // Fast path for drawing all solid edges and all unrounded double edges | |
1000 | |
1001 if (numEdgesVisible == 4 && (outerBorder.isRounded() || haveAlphaColor) | |
1002 && (haveAllSolidEdges || (!outerBorder.isRounded() && !innerBorder.i
sRounded()))) { | |
1003 Path path; | |
1004 | |
1005 if (outerBorder.isRounded() && allEdgesShareWidth) { | |
1006 | |
1007 // Very fast path for single stroked round rect with circular co
rners | |
1008 | |
1009 graphicsContext->fillBetweenRoundedRects(outerBorder, innerBorde
r, edges[firstVisibleEdge].color); | |
1010 return; | |
1011 } | |
1012 if (outerBorder.isRounded() && bleedAvoidance != BackgroundBleedClip
Background) | |
1013 path.addRoundedRect(outerBorder); | |
1014 else | |
1015 path.addRect(outerBorder.rect()); | |
1016 | |
1017 if (haveAllDoubleEdges) { | |
1018 IntRect innerThirdRect = outerBorder.rect(); | |
1019 IntRect outerThirdRect = outerBorder.rect(); | |
1020 for (int side = BSTop; side <= BSLeft; ++side) { | |
1021 int outerWidth; | |
1022 int innerWidth; | |
1023 edges[side].getDoubleBorderStripeWidths(outerWidth, innerWid
th); | |
1024 | |
1025 if (side == BSTop) { | |
1026 innerThirdRect.shiftYEdgeTo(innerThirdRect.y() + innerWi
dth); | |
1027 outerThirdRect.shiftYEdgeTo(outerThirdRect.y() + outerWi
dth); | |
1028 } else if (side == BSBottom) { | |
1029 innerThirdRect.setHeight(innerThirdRect.height() - inner
Width); | |
1030 outerThirdRect.setHeight(outerThirdRect.height() - outer
Width); | |
1031 } else if (side == BSLeft) { | |
1032 innerThirdRect.shiftXEdgeTo(innerThirdRect.x() + innerWi
dth); | |
1033 outerThirdRect.shiftXEdgeTo(outerThirdRect.x() + outerWi
dth); | |
1034 } else { | |
1035 innerThirdRect.setWidth(innerThirdRect.width() - innerWi
dth); | |
1036 outerThirdRect.setWidth(outerThirdRect.width() - outerWi
dth); | |
1037 } | |
1038 } | |
1039 | |
1040 RoundedRect outerThird = outerBorder; | |
1041 RoundedRect innerThird = innerBorder; | |
1042 innerThird.setRect(innerThirdRect); | |
1043 outerThird.setRect(outerThirdRect); | |
1044 | |
1045 if (outerThird.isRounded() && bleedAvoidance != BackgroundBleedC
lipBackground) | |
1046 path.addRoundedRect(outerThird); | |
1047 else | |
1048 path.addRect(outerThird.rect()); | |
1049 | |
1050 if (innerThird.isRounded() && bleedAvoidance != BackgroundBleedC
lipBackground) | |
1051 path.addRoundedRect(innerThird); | |
1052 else | |
1053 path.addRect(innerThird.rect()); | |
1054 } | |
1055 | |
1056 if (innerBorder.isRounded()) | |
1057 path.addRoundedRect(innerBorder); | |
1058 else | |
1059 path.addRect(innerBorder.rect()); | |
1060 | |
1061 graphicsContext->setFillRule(RULE_EVENODD); | |
1062 graphicsContext->setFillColor(edges[firstVisibleEdge].color); | |
1063 graphicsContext->fillPath(path); | |
1064 return; | |
1065 } | |
1066 // Avoid creating transparent layers | |
1067 if (haveAllSolidEdges && numEdgesVisible != 4 && !outerBorder.isRounded(
) && haveAlphaColor) { | |
1068 Path path; | |
1069 | |
1070 for (int i = BSTop; i <= BSLeft; ++i) { | |
1071 const BorderEdge& currEdge = edges[i]; | |
1072 if (currEdge.shouldRender()) { | |
1073 IntRect sideRect = calculateSideRect(outerBorder, edges, i); | |
1074 path.addRect(sideRect); | |
1075 } | |
1076 } | |
1077 | |
1078 graphicsContext->setFillRule(RULE_NONZERO); | |
1079 graphicsContext->setFillColor(edges[firstVisibleEdge].color); | |
1080 graphicsContext->fillPath(path); | |
1081 return; | |
1082 } | |
1083 } | |
1084 | |
1085 bool clipToOuterBorder = outerBorder.isRounded(); | |
1086 GraphicsContextStateSaver stateSaver(*graphicsContext, clipToOuterBorder); | |
1087 if (clipToOuterBorder) { | |
1088 // Clip to the inner and outer radii rects. | |
1089 if (bleedAvoidance != BackgroundBleedClipBackground) | |
1090 graphicsContext->clipRoundedRect(outerBorder); | |
1091 // isRenderable() check avoids issue described in https://bugs.webkit.or
g/show_bug.cgi?id=38787 | |
1092 // The inside will be clipped out later (in clipBorderSideForComplexInne
rPath) | |
1093 if (innerBorder.isRenderable() && !innerBorder.isEmpty()) | |
1094 graphicsContext->clipOutRoundedRect(innerBorder); | |
1095 } | |
1096 | |
1097 // If only one edge visible antialiasing doesn't create seams | |
1098 bool antialias = shouldAntialiasLines(graphicsContext) || numEdgesVisible ==
1; | |
1099 RoundedRect unadjustedInnerBorder = (bleedAvoidance == BackgroundBleedBackgr
oundOverBorder) ? style->getRoundedInnerBorderFor(rect, includeLogicalLeftEdge,
includeLogicalRightEdge) : innerBorder; | |
1100 IntPoint innerBorderAdjustment(innerBorder.rect().x() - unadjustedInnerBorde
r.rect().x(), innerBorder.rect().y() - unadjustedInnerBorder.rect().y()); | |
1101 if (haveAlphaColor) | |
1102 paintTranslucentBorderSides(graphicsContext, style, outerBorder, unadjus
tedInnerBorder, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, inclu
deLogicalLeftEdge, includeLogicalRightEdge, antialias); | |
1103 else | |
1104 paintBorderSides(graphicsContext, style, outerBorder, unadjustedInnerBor
der, innerBorderAdjustment, edges, edgesToDraw, bleedAvoidance, includeLogicalLe
ftEdge, includeLogicalRightEdge, antialias); | |
1105 } | |
1106 | |
1107 void RenderBoxModelObject::drawBoxSideFromPath(GraphicsContext* graphicsContext,
const LayoutRect& borderRect, const Path& borderPath, const BorderEdge edges[], | |
1108 float thickness, float drawThickness, BoxSide side, const RenderStyle* style
, Color color, EBorderStyle borderStyle, BackgroundBleedAvoidance bleedAvoidance
, | |
1109 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) | |
1110 { | |
1111 if (thickness <= 0) | |
1112 return; | |
1113 | |
1114 if (borderStyle == DOUBLE && thickness < 3) | |
1115 borderStyle = SOLID; | |
1116 | |
1117 switch (borderStyle) { | |
1118 case BNONE: | |
1119 case BHIDDEN: | |
1120 return; | |
1121 case DOTTED: | |
1122 case DASHED: { | |
1123 graphicsContext->setStrokeColor(color); | |
1124 | |
1125 // The stroke is doubled here because the provided path is the | |
1126 // outside edge of the border so half the stroke is clipped off. | |
1127 // The extra multiplier is so that the clipping mask can antialias | |
1128 // the edges to prevent jaggies. | |
1129 graphicsContext->setStrokeThickness(drawThickness * 2 * 1.1f); | |
1130 graphicsContext->setStrokeStyle(borderStyle == DASHED ? DashedStroke : D
ottedStroke); | |
1131 | |
1132 // If the number of dashes that fit in the path is odd and non-integral
then we | |
1133 // will have an awkwardly-sized dash at the end of the path. To try to a
void that | |
1134 // here, we simply make the whitespace dashes ever so slightly bigger. | |
1135 // FIXME: This could be even better if we tried to manipulate the dash o
ffset | |
1136 // and possibly the gapLength to get the corners dash-symmetrical. | |
1137 float dashLength = thickness * ((borderStyle == DASHED) ? 3.0f : 1.0f); | |
1138 float gapLength = dashLength; | |
1139 float numberOfDashes = borderPath.length() / dashLength; | |
1140 // Don't try to show dashes if we have less than 2 dashes + 2 gaps. | |
1141 // FIXME: should do this test per side. | |
1142 if (numberOfDashes >= 4) { | |
1143 bool evenNumberOfFullDashes = !((int)numberOfDashes % 2); | |
1144 bool integralNumberOfDashes = !(numberOfDashes - (int)numberOfDashes
); | |
1145 if (!evenNumberOfFullDashes && !integralNumberOfDashes) { | |
1146 float numberOfGaps = numberOfDashes / 2; | |
1147 gapLength += (dashLength / numberOfGaps); | |
1148 } | |
1149 | |
1150 DashArray lineDash; | |
1151 lineDash.append(dashLength); | |
1152 lineDash.append(gapLength); | |
1153 graphicsContext->setLineDash(lineDash, dashLength); | |
1154 } | |
1155 | |
1156 // FIXME: stroking the border path causes issues with tight corners: | |
1157 // https://bugs.webkit.org/show_bug.cgi?id=58711 | |
1158 // Also, to get the best appearance we should stroke a path between the
two borders. | |
1159 graphicsContext->strokePath(borderPath); | |
1160 return; | |
1161 } | |
1162 case DOUBLE: { | |
1163 // Get the inner border rects for both the outer border line and the inn
er border line | |
1164 int outerBorderTopWidth; | |
1165 int innerBorderTopWidth; | |
1166 edges[BSTop].getDoubleBorderStripeWidths(outerBorderTopWidth, innerBorde
rTopWidth); | |
1167 | |
1168 int outerBorderRightWidth; | |
1169 int innerBorderRightWidth; | |
1170 edges[BSRight].getDoubleBorderStripeWidths(outerBorderRightWidth, innerB
orderRightWidth); | |
1171 | |
1172 int outerBorderBottomWidth; | |
1173 int innerBorderBottomWidth; | |
1174 edges[BSBottom].getDoubleBorderStripeWidths(outerBorderBottomWidth, inne
rBorderBottomWidth); | |
1175 | |
1176 int outerBorderLeftWidth; | |
1177 int innerBorderLeftWidth; | |
1178 edges[BSLeft].getDoubleBorderStripeWidths(outerBorderLeftWidth, innerBor
derLeftWidth); | |
1179 | |
1180 // Draw inner border line | |
1181 { | |
1182 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
1183 RoundedRect innerClip = style->getRoundedInnerBorderFor(borderRect, | |
1184 innerBorderTopWidth, innerBorderBottomWidth, innerBorderLeftWidt
h, innerBorderRightWidth, | |
1185 includeLogicalLeftEdge, includeLogicalRightEdge); | |
1186 | |
1187 graphicsContext->clipRoundedRect(innerClip); | |
1188 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges,
thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogi
calLeftEdge, includeLogicalRightEdge); | |
1189 } | |
1190 | |
1191 // Draw outer border line | |
1192 { | |
1193 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
1194 LayoutRect outerRect = borderRect; | |
1195 if (bleedAvoidance == BackgroundBleedClipBackground) { | |
1196 outerRect.inflate(1); | |
1197 ++outerBorderTopWidth; | |
1198 ++outerBorderBottomWidth; | |
1199 ++outerBorderLeftWidth; | |
1200 ++outerBorderRightWidth; | |
1201 } | |
1202 | |
1203 RoundedRect outerClip = style->getRoundedInnerBorderFor(outerRect, | |
1204 outerBorderTopWidth, outerBorderBottomWidth, outerBorderLeftWidt
h, outerBorderRightWidth, | |
1205 includeLogicalLeftEdge, includeLogicalRightEdge); | |
1206 graphicsContext->clipOutRoundedRect(outerClip); | |
1207 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges,
thickness, drawThickness, side, style, color, SOLID, bleedAvoidance, includeLogi
calLeftEdge, includeLogicalRightEdge); | |
1208 } | |
1209 return; | |
1210 } | |
1211 case RIDGE: | |
1212 case GROOVE: | |
1213 { | |
1214 EBorderStyle s1; | |
1215 EBorderStyle s2; | |
1216 if (borderStyle == GROOVE) { | |
1217 s1 = INSET; | |
1218 s2 = OUTSET; | |
1219 } else { | |
1220 s1 = OUTSET; | |
1221 s2 = INSET; | |
1222 } | |
1223 | |
1224 // Paint full border | |
1225 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thic
kness, drawThickness, side, style, color, s1, bleedAvoidance, includeLogicalLeft
Edge, includeLogicalRightEdge); | |
1226 | |
1227 // Paint inner only | |
1228 GraphicsContextStateSaver stateSaver(*graphicsContext); | |
1229 LayoutUnit topWidth = edges[BSTop].usedWidth() / 2; | |
1230 LayoutUnit bottomWidth = edges[BSBottom].usedWidth() / 2; | |
1231 LayoutUnit leftWidth = edges[BSLeft].usedWidth() / 2; | |
1232 LayoutUnit rightWidth = edges[BSRight].usedWidth() / 2; | |
1233 | |
1234 RoundedRect clipRect = style->getRoundedInnerBorderFor(borderRect, | |
1235 topWidth, bottomWidth, leftWidth, rightWidth, | |
1236 includeLogicalLeftEdge, includeLogicalRightEdge); | |
1237 | |
1238 graphicsContext->clipRoundedRect(clipRect); | |
1239 drawBoxSideFromPath(graphicsContext, borderRect, borderPath, edges, thic
kness, drawThickness, side, style, color, s2, bleedAvoidance, includeLogicalLeft
Edge, includeLogicalRightEdge); | |
1240 return; | |
1241 } | |
1242 case INSET: | |
1243 if (side == BSTop || side == BSLeft) | |
1244 color = color.dark(); | |
1245 break; | |
1246 case OUTSET: | |
1247 if (side == BSBottom || side == BSRight) | |
1248 color = color.dark(); | |
1249 break; | |
1250 default: | |
1251 break; | |
1252 } | |
1253 | |
1254 graphicsContext->setStrokeStyle(NoStroke); | |
1255 graphicsContext->setFillColor(color); | |
1256 graphicsContext->drawRect(pixelSnappedIntRect(borderRect)); | |
1257 } | |
1258 | |
1259 void RenderBoxModelObject::clipBorderSidePolygon(GraphicsContext* graphicsContex
t, const RoundedRect& outerBorder, const RoundedRect& innerBorder, | |
1260 BoxSide side, bool firstEdgeMat
ches, bool secondEdgeMatches) | |
1261 { | |
1262 FloatPoint quad[4]; | |
1263 | |
1264 const LayoutRect& outerRect = outerBorder.rect(); | |
1265 const LayoutRect& innerRect = innerBorder.rect(); | |
1266 | |
1267 FloatPoint centerPoint(innerRect.location().x().toFloat() + innerRect.width(
).toFloat() / 2, innerRect.location().y().toFloat() + innerRect.height().toFloat
() / 2); | |
1268 | |
1269 // For each side, create a quad that encompasses all parts of that side that
may draw, | |
1270 // including areas inside the innerBorder. | |
1271 // | |
1272 // 0----------------3 | |
1273 // 0 \ / 0 | |
1274 // |\ 1----------- 2 /| | |
1275 // | 1 1 | | |
1276 // | | | | | |
1277 // | | | | | |
1278 // | 2 2 | | |
1279 // |/ 1------------2 \| | |
1280 // 3 / \ 3 | |
1281 // 0----------------3 | |
1282 // | |
1283 switch (side) { | |
1284 case BSTop: | |
1285 quad[0] = outerRect.minXMinYCorner(); | |
1286 quad[1] = innerRect.minXMinYCorner(); | |
1287 quad[2] = innerRect.maxXMinYCorner(); | |
1288 quad[3] = outerRect.maxXMinYCorner(); | |
1289 | |
1290 if (!innerBorder.radii().topLeft().isZero()) { | |
1291 findIntersection(quad[0], quad[1], | |
1292 FloatPoint( | |
1293 quad[1].x() + innerBorder.radii().topLeft().width(), | |
1294 quad[1].y()), | |
1295 FloatPoint( | |
1296 quad[1].x(), | |
1297 quad[1].y() + innerBorder.radii().topLeft().height()), | |
1298 quad[1]); | |
1299 } | |
1300 | |
1301 if (!innerBorder.radii().topRight().isZero()) { | |
1302 findIntersection(quad[3], quad[2], | |
1303 FloatPoint( | |
1304 quad[2].x() - innerBorder.radii().topRight().width(), | |
1305 quad[2].y()), | |
1306 FloatPoint( | |
1307 quad[2].x(), | |
1308 quad[2].y() + innerBorder.radii().topRight().height()), | |
1309 quad[2]); | |
1310 } | |
1311 break; | |
1312 | |
1313 case BSLeft: | |
1314 quad[0] = outerRect.minXMinYCorner(); | |
1315 quad[1] = innerRect.minXMinYCorner(); | |
1316 quad[2] = innerRect.minXMaxYCorner(); | |
1317 quad[3] = outerRect.minXMaxYCorner(); | |
1318 | |
1319 if (!innerBorder.radii().topLeft().isZero()) { | |
1320 findIntersection(quad[0], quad[1], | |
1321 FloatPoint( | |
1322 quad[1].x() + innerBorder.radii().topLeft().width(), | |
1323 quad[1].y()), | |
1324 FloatPoint( | |
1325 quad[1].x(), | |
1326 quad[1].y() + innerBorder.radii().topLeft().height()), | |
1327 quad[1]); | |
1328 } | |
1329 | |
1330 if (!innerBorder.radii().bottomLeft().isZero()) { | |
1331 findIntersection(quad[3], quad[2], | |
1332 FloatPoint( | |
1333 quad[2].x() + innerBorder.radii().bottomLeft().width(), | |
1334 quad[2].y()), | |
1335 FloatPoint( | |
1336 quad[2].x(), | |
1337 quad[2].y() - innerBorder.radii().bottomLeft().height()), | |
1338 quad[2]); | |
1339 } | |
1340 break; | |
1341 | |
1342 case BSBottom: | |
1343 quad[0] = outerRect.minXMaxYCorner(); | |
1344 quad[1] = innerRect.minXMaxYCorner(); | |
1345 quad[2] = innerRect.maxXMaxYCorner(); | |
1346 quad[3] = outerRect.maxXMaxYCorner(); | |
1347 | |
1348 if (!innerBorder.radii().bottomLeft().isZero()) { | |
1349 findIntersection(quad[0], quad[1], | |
1350 FloatPoint( | |
1351 quad[1].x() + innerBorder.radii().bottomLeft().width(), | |
1352 quad[1].y()), | |
1353 FloatPoint( | |
1354 quad[1].x(), | |
1355 quad[1].y() - innerBorder.radii().bottomLeft().height()), | |
1356 quad[1]); | |
1357 } | |
1358 | |
1359 if (!innerBorder.radii().bottomRight().isZero()) { | |
1360 findIntersection(quad[3], quad[2], | |
1361 FloatPoint( | |
1362 quad[2].x() - innerBorder.radii().bottomRight().width(), | |
1363 quad[2].y()), | |
1364 FloatPoint( | |
1365 quad[2].x(), | |
1366 quad[2].y() - innerBorder.radii().bottomRight().height()), | |
1367 quad[2]); | |
1368 } | |
1369 break; | |
1370 | |
1371 case BSRight: | |
1372 quad[0] = outerRect.maxXMinYCorner(); | |
1373 quad[1] = innerRect.maxXMinYCorner(); | |
1374 quad[2] = innerRect.maxXMaxYCorner(); | |
1375 quad[3] = outerRect.maxXMaxYCorner(); | |
1376 | |
1377 if (!innerBorder.radii().topRight().isZero()) { | |
1378 findIntersection(quad[0], quad[1], | |
1379 FloatPoint( | |
1380 quad[1].x() - innerBorder.radii().topRight().width(), | |
1381 quad[1].y()), | |
1382 FloatPoint( | |
1383 quad[1].x(), | |
1384 quad[1].y() + innerBorder.radii().topRight().height()), | |
1385 quad[1]); | |
1386 } | |
1387 | |
1388 if (!innerBorder.radii().bottomRight().isZero()) { | |
1389 findIntersection(quad[3], quad[2], | |
1390 FloatPoint( | |
1391 quad[2].x() - innerBorder.radii().bottomRight().width(), | |
1392 quad[2].y()), | |
1393 FloatPoint( | |
1394 quad[2].x(), | |
1395 quad[2].y() - innerBorder.radii().bottomRight().height()), | |
1396 quad[2]); | |
1397 } | |
1398 break; | |
1399 } | |
1400 | |
1401 // If the border matches both of its adjacent sides, don't anti-alias the cl
ip, and | |
1402 // if neither side matches, anti-alias the clip. | |
1403 if (firstEdgeMatches == secondEdgeMatches) { | |
1404 graphicsContext->clipConvexPolygon(4, quad, !firstEdgeMatches); | |
1405 return; | |
1406 } | |
1407 | |
1408 // If antialiasing settings for the first edge and second edge is different, | |
1409 // they have to be addressed separately. We do this by breaking the quad int
o | |
1410 // two parallelograms, made by moving quad[1] and quad[2]. | |
1411 float ax = quad[1].x() - quad[0].x(); | |
1412 float ay = quad[1].y() - quad[0].y(); | |
1413 float bx = quad[2].x() - quad[1].x(); | |
1414 float by = quad[2].y() - quad[1].y(); | |
1415 float cx = quad[3].x() - quad[2].x(); | |
1416 float cy = quad[3].y() - quad[2].y(); | |
1417 | |
1418 const static float kEpsilon = 1e-2f; | |
1419 float r1, r2; | |
1420 if (fabsf(bx) < kEpsilon && fabsf(by) < kEpsilon) { | |
1421 // The quad was actually a triangle. | |
1422 r1 = r2 = 1.0f; | |
1423 } else { | |
1424 // Extend parallelogram a bit to hide calculation error | |
1425 const static float kExtendFill = 1e-2f; | |
1426 | |
1427 r1 = (-ax * by + ay * bx) / (cx * by - cy * bx) + kExtendFill; | |
1428 r2 = (-cx * by + cy * bx) / (ax * by - ay * bx) + kExtendFill; | |
1429 } | |
1430 | |
1431 FloatPoint firstQuad[4]; | |
1432 firstQuad[0] = quad[0]; | |
1433 firstQuad[1] = quad[1]; | |
1434 firstQuad[2] = FloatPoint(quad[3].x() + r2 * ax, quad[3].y() + r2 * ay); | |
1435 firstQuad[3] = quad[3]; | |
1436 graphicsContext->clipConvexPolygon(4, firstQuad, !firstEdgeMatches); | |
1437 | |
1438 FloatPoint secondQuad[4]; | |
1439 secondQuad[0] = quad[0]; | |
1440 secondQuad[1] = FloatPoint(quad[0].x() - r1 * cx, quad[0].y() - r1 * cy); | |
1441 secondQuad[2] = quad[2]; | |
1442 secondQuad[3] = quad[3]; | |
1443 graphicsContext->clipConvexPolygon(4, secondQuad, !secondEdgeMatches); | |
1444 } | |
1445 | |
1446 static IntRect calculateSideRectIncludingInner(const RoundedRect& outerBorder, c
onst BorderEdge edges[], BoxSide side) | |
1447 { | |
1448 IntRect sideRect = outerBorder.rect(); | |
1449 int width; | |
1450 | |
1451 switch (side) { | |
1452 case BSTop: | |
1453 width = sideRect.height() - edges[BSBottom].width; | |
1454 sideRect.setHeight(width); | |
1455 break; | |
1456 case BSBottom: | |
1457 width = sideRect.height() - edges[BSTop].width; | |
1458 sideRect.shiftYEdgeTo(sideRect.maxY() - width); | |
1459 break; | |
1460 case BSLeft: | |
1461 width = sideRect.width() - edges[BSRight].width; | |
1462 sideRect.setWidth(width); | |
1463 break; | |
1464 case BSRight: | |
1465 width = sideRect.width() - edges[BSLeft].width; | |
1466 sideRect.shiftXEdgeTo(sideRect.maxX() - width); | |
1467 break; | |
1468 } | |
1469 | |
1470 return sideRect; | |
1471 } | |
1472 | |
1473 static RoundedRect calculateAdjustedInnerBorder(const RoundedRect&innerBorder, B
oxSide side) | |
1474 { | |
1475 // Expand the inner border as necessary to make it a rounded rect (i.e. radi
i contained within each edge). | |
1476 // This function relies on the fact we only get radii not contained within e
ach edge if one of the radii | |
1477 // for an edge is zero, so we can shift the arc towards the zero radius corn
er. | |
1478 RoundedRect::Radii newRadii = innerBorder.radii(); | |
1479 IntRect newRect = innerBorder.rect(); | |
1480 | |
1481 float overshoot; | |
1482 float maxRadii; | |
1483 | |
1484 switch (side) { | |
1485 case BSTop: | |
1486 overshoot = newRadii.topLeft().width() + newRadii.topRight().width() - n
ewRect.width(); | |
1487 if (overshoot > 0) { | |
1488 ASSERT(!(newRadii.topLeft().width() && newRadii.topRight().width()))
; | |
1489 newRect.setWidth(newRect.width() + overshoot); | |
1490 if (!newRadii.topLeft().width()) | |
1491 newRect.move(-overshoot, 0); | |
1492 } | |
1493 newRadii.setBottomLeft(IntSize(0, 0)); | |
1494 newRadii.setBottomRight(IntSize(0, 0)); | |
1495 maxRadii = std::max(newRadii.topLeft().height(), newRadii.topRight().hei
ght()); | |
1496 if (maxRadii > newRect.height()) | |
1497 newRect.setHeight(maxRadii); | |
1498 break; | |
1499 | |
1500 case BSBottom: | |
1501 overshoot = newRadii.bottomLeft().width() + newRadii.bottomRight().width
() - newRect.width(); | |
1502 if (overshoot > 0) { | |
1503 ASSERT(!(newRadii.bottomLeft().width() && newRadii.bottomRight().wid
th())); | |
1504 newRect.setWidth(newRect.width() + overshoot); | |
1505 if (!newRadii.bottomLeft().width()) | |
1506 newRect.move(-overshoot, 0); | |
1507 } | |
1508 newRadii.setTopLeft(IntSize(0, 0)); | |
1509 newRadii.setTopRight(IntSize(0, 0)); | |
1510 maxRadii = std::max(newRadii.bottomLeft().height(), newRadii.bottomRight
().height()); | |
1511 if (maxRadii > newRect.height()) { | |
1512 newRect.move(0, newRect.height() - maxRadii); | |
1513 newRect.setHeight(maxRadii); | |
1514 } | |
1515 break; | |
1516 | |
1517 case BSLeft: | |
1518 overshoot = newRadii.topLeft().height() + newRadii.bottomLeft().height()
- newRect.height(); | |
1519 if (overshoot > 0) { | |
1520 ASSERT(!(newRadii.topLeft().height() && newRadii.bottomLeft().height
())); | |
1521 newRect.setHeight(newRect.height() + overshoot); | |
1522 if (!newRadii.topLeft().height()) | |
1523 newRect.move(0, -overshoot); | |
1524 } | |
1525 newRadii.setTopRight(IntSize(0, 0)); | |
1526 newRadii.setBottomRight(IntSize(0, 0)); | |
1527 maxRadii = std::max(newRadii.topLeft().width(), newRadii.bottomLeft().wi
dth()); | |
1528 if (maxRadii > newRect.width()) | |
1529 newRect.setWidth(maxRadii); | |
1530 break; | |
1531 | |
1532 case BSRight: | |
1533 overshoot = newRadii.topRight().height() + newRadii.bottomRight().height
() - newRect.height(); | |
1534 if (overshoot > 0) { | |
1535 ASSERT(!(newRadii.topRight().height() && newRadii.bottomRight().heig
ht())); | |
1536 newRect.setHeight(newRect.height() + overshoot); | |
1537 if (!newRadii.topRight().height()) | |
1538 newRect.move(0, -overshoot); | |
1539 } | |
1540 newRadii.setTopLeft(IntSize(0, 0)); | |
1541 newRadii.setBottomLeft(IntSize(0, 0)); | |
1542 maxRadii = std::max(newRadii.topRight().width(), newRadii.bottomRight().
width()); | |
1543 if (maxRadii > newRect.width()) { | |
1544 newRect.move(newRect.width() - maxRadii, 0); | |
1545 newRect.setWidth(maxRadii); | |
1546 } | |
1547 break; | |
1548 } | |
1549 | |
1550 return RoundedRect(newRect, newRadii); | |
1551 } | |
1552 | |
1553 void RenderBoxModelObject::clipBorderSideForComplexInnerPath(GraphicsContext* gr
aphicsContext, const RoundedRect& outerBorder, const RoundedRect& innerBorder, | |
1554 BoxSide side, const BorderEdge edges[]) | |
1555 { | |
1556 graphicsContext->clip(calculateSideRectIncludingInner(outerBorder, edges, si
de)); | |
1557 RoundedRect adjustedInnerRect = calculateAdjustedInnerBorder(innerBorder, si
de); | |
1558 if (!adjustedInnerRect.isEmpty()) | |
1559 graphicsContext->clipOutRoundedRect(adjustedInnerRect); | |
1560 } | |
1561 | |
1562 bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedA
voidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const | 404 bool RenderBoxModelObject::boxShadowShouldBeAppliedToBackground(BackgroundBleedA
voidance bleedAvoidance, InlineFlowBox* inlineFlowBox) const |
1563 { | 405 { |
1564 if (bleedAvoidance != BackgroundBleedNone) | 406 if (bleedAvoidance != BackgroundBleedNone) |
1565 return false; | 407 return false; |
1566 | 408 |
1567 if (style()->hasAppearance()) | 409 if (style()->hasAppearance()) |
1568 return false; | 410 return false; |
1569 | 411 |
1570 const ShadowList* shadowList = style()->boxShadow(); | 412 const ShadowList* shadowList = style()->boxShadow(); |
1571 if (!shadowList) | 413 if (!shadowList) |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1605 | 447 |
1606 if (inlineFlowBox && !inlineFlowBox->boxShadowCanBeAppliedToBackground(*last
BackgroundLayer)) | 448 if (inlineFlowBox && !inlineFlowBox->boxShadowCanBeAppliedToBackground(*last
BackgroundLayer)) |
1607 return false; | 449 return false; |
1608 | 450 |
1609 if (hasOverflowClip() && lastBackgroundLayer->attachment() == LocalBackgroun
dAttachment) | 451 if (hasOverflowClip() && lastBackgroundLayer->attachment() == LocalBackgroun
dAttachment) |
1610 return false; | 452 return false; |
1611 | 453 |
1612 return true; | 454 return true; |
1613 } | 455 } |
1614 | 456 |
1615 void RenderBoxModelObject::paintBoxShadow(const PaintInfo& info, const LayoutRec
t& paintRect, const RenderStyle* s, ShadowStyle shadowStyle, bool includeLogical
LeftEdge, bool includeLogicalRightEdge) | |
1616 { | |
1617 // FIXME: Deal with border-image. Would be great to use border-image as a m
ask. | |
1618 GraphicsContext* context = info.context; | |
1619 if (!s->boxShadow()) | |
1620 return; | |
1621 | 457 |
1622 RoundedRect border = (shadowStyle == Inset) ? s->getRoundedInnerBorderFor(pa
intRect, includeLogicalLeftEdge, includeLogicalRightEdge) | |
1623 : s->getRoundedBorderFor(paintRect, includeLogicalLeftEdge, includeLogic
alRightEdge); | |
1624 | |
1625 bool hasBorderRadius = s->hasBorderRadius(); | |
1626 bool isHorizontal = s->isHorizontalWritingMode(); | |
1627 bool hasOpaqueBackground = s->visitedDependentColor(CSSPropertyBackgroundCol
or).alpha() == 255; | |
1628 | |
1629 GraphicsContextStateSaver stateSaver(*context, false); | |
1630 | |
1631 const ShadowList* shadowList = s->boxShadow(); | |
1632 for (size_t i = shadowList->shadows().size(); i--; ) { | |
1633 const ShadowData& shadow = shadowList->shadows()[i]; | |
1634 if (shadow.style() != shadowStyle) | |
1635 continue; | |
1636 | |
1637 FloatSize shadowOffset(shadow.x(), shadow.y()); | |
1638 float shadowBlur = shadow.blur(); | |
1639 float shadowSpread = shadow.spread(); | |
1640 | |
1641 if (shadowOffset.isZero() && !shadowBlur && !shadowSpread) | |
1642 continue; | |
1643 | |
1644 const Color& shadowColor = shadow.color(); | |
1645 | |
1646 if (shadow.style() == Normal) { | |
1647 FloatRect fillRect = border.rect(); | |
1648 fillRect.inflate(shadowSpread); | |
1649 if (fillRect.isEmpty()) | |
1650 continue; | |
1651 | |
1652 FloatRect shadowRect(border.rect()); | |
1653 shadowRect.inflate(shadowBlur + shadowSpread); | |
1654 shadowRect.move(shadowOffset); | |
1655 | |
1656 // Save the state and clip, if not already done. | |
1657 // The clip does not depend on any shadow-specific properties. | |
1658 if (!stateSaver.saved()) { | |
1659 stateSaver.save(); | |
1660 if (hasBorderRadius) { | |
1661 RoundedRect rectToClipOut = border; | |
1662 | |
1663 // If the box is opaque, it is unnecessary to clip it out. H
owever, doing so saves time | |
1664 // when painting the shadow. On the other hand, it introduce
s subpixel gaps along the | |
1665 // corners. Those are avoided by insetting the clipping path
by one pixel. | |
1666 if (hasOpaqueBackground) | |
1667 rectToClipOut.inflateWithRadii(-1); | |
1668 | |
1669 if (!rectToClipOut.isEmpty()) { | |
1670 context->clipOutRoundedRect(rectToClipOut); | |
1671 } | |
1672 } else { | |
1673 // This IntRect is correct even with fractional shadows, bec
ause it is used for the rectangle | |
1674 // of the box itself, which is always pixel-aligned. | |
1675 IntRect rectToClipOut = border.rect(); | |
1676 | |
1677 // If the box is opaque, it is unnecessary to clip it out. H
owever, doing so saves time | |
1678 // when painting the shadow. On the other hand, it introduce
s subpixel gaps along the | |
1679 // edges if they are not pixel-aligned. Those are avoided by
insetting the clipping path | |
1680 // by one pixel. | |
1681 if (hasOpaqueBackground) { | |
1682 // FIXME: The function to decide on the policy based on
the transform should be a named function. | |
1683 // FIXME: It's not clear if this check is right. What ab
out integral scale factors? | |
1684 // FIXME: See crbug.com/382491. The use of getCTM may al
so be wrong because it does not include | |
1685 // device zoom applied at raster time. | |
1686 AffineTransform transform = context->getCTM(); | |
1687 if (transform.a() != 1 || (transform.d() != 1 && transfo
rm.d() != -1) || transform.b() || transform.c()) | |
1688 rectToClipOut.inflate(-1); | |
1689 } | |
1690 | |
1691 if (!rectToClipOut.isEmpty()) { | |
1692 context->clipOut(rectToClipOut); | |
1693 } | |
1694 } | |
1695 } | |
1696 | |
1697 // Draw only the shadow. | |
1698 OwnPtr<DrawLooperBuilder> drawLooperBuilder = DrawLooperBuilder::cre
ate(); | |
1699 drawLooperBuilder->addShadow(shadowOffset, shadowBlur, shadowColor, | |
1700 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::
ShadowIgnoresAlpha); | |
1701 context->setDrawLooper(drawLooperBuilder.release()); | |
1702 | |
1703 if (hasBorderRadius) { | |
1704 RoundedRect influenceRect(pixelSnappedIntRect(LayoutRect(shadowR
ect)), border.radii()); | |
1705 influenceRect.expandRadii(2 * shadowBlur + shadowSpread); | |
1706 if (allCornersClippedOut(influenceRect, info.rect)) | |
1707 context->fillRect(fillRect, Color::black); | |
1708 else { | |
1709 // TODO: support non-integer shadows - crbug.com/334829 | |
1710 RoundedRect roundedFillRect = border; | |
1711 roundedFillRect.inflate(shadowSpread); | |
1712 | |
1713 roundedFillRect.expandRadii(shadowSpread); | |
1714 if (!roundedFillRect.isRenderable()) | |
1715 roundedFillRect.adjustRadii(); | |
1716 context->fillRoundedRect(roundedFillRect, Color::black); | |
1717 } | |
1718 } else { | |
1719 context->fillRect(fillRect, Color::black); | |
1720 } | |
1721 } else { | |
1722 // The inset shadow case. | |
1723 GraphicsContext::Edges clippedEdges = GraphicsContext::NoEdge; | |
1724 if (!includeLogicalLeftEdge) { | |
1725 if (isHorizontal) | |
1726 clippedEdges |= GraphicsContext::LeftEdge; | |
1727 else | |
1728 clippedEdges |= GraphicsContext::TopEdge; | |
1729 } | |
1730 if (!includeLogicalRightEdge) { | |
1731 if (isHorizontal) | |
1732 clippedEdges |= GraphicsContext::RightEdge; | |
1733 else | |
1734 clippedEdges |= GraphicsContext::BottomEdge; | |
1735 } | |
1736 // TODO: support non-integer shadows - crbug.com/334828 | |
1737 context->drawInnerShadow(border, shadowColor, flooredIntSize(shadowO
ffset), shadowBlur, shadowSpread, clippedEdges); | |
1738 } | |
1739 } | |
1740 } | |
1741 | 458 |
1742 LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const | 459 LayoutUnit RenderBoxModelObject::containingBlockLogicalWidthForContent() const |
1743 { | 460 { |
1744 return containingBlock()->availableLogicalWidth(); | 461 return containingBlock()->availableLogicalWidth(); |
1745 } | 462 } |
1746 | 463 |
1747 RenderBoxModelObject* RenderBoxModelObject::continuation() const | 464 RenderBoxModelObject* RenderBoxModelObject::continuation() const |
1748 { | 465 { |
1749 if (!continuationMap) | 466 if (!continuationMap) |
1750 return 0; | 467 return 0; |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1852 break; | 569 break; |
1853 } | 570 } |
1854 x = std::min(x, std::max<LayoutUnit>(maxX - caretWidth, 0)); | 571 x = std::min(x, std::max<LayoutUnit>(maxX - caretWidth, 0)); |
1855 | 572 |
1856 LayoutUnit height = style()->fontMetrics().height(); | 573 LayoutUnit height = style()->fontMetrics().height(); |
1857 LayoutUnit verticalSpace = lineHeight(true, currentStyle->isHorizontalWritin
gMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) - height; | 574 LayoutUnit verticalSpace = lineHeight(true, currentStyle->isHorizontalWritin
gMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes) - height; |
1858 LayoutUnit y = paddingTop() + borderTop() + (verticalSpace / 2); | 575 LayoutUnit y = paddingTop() + borderTop() + (verticalSpace / 2); |
1859 return currentStyle->isHorizontalWritingMode() ? LayoutRect(x, y, caretWidth
, height) : LayoutRect(y, x, height, caretWidth); | 576 return currentStyle->isHorizontalWritingMode() ? LayoutRect(x, y, caretWidth
, height) : LayoutRect(y, x, height, caretWidth); |
1860 } | 577 } |
1861 | 578 |
1862 bool RenderBoxModelObject::shouldAntialiasLines(GraphicsContext* context) | |
1863 { | |
1864 // FIXME: We may want to not antialias when scaled by an integral value, | |
1865 // and we may want to antialias when translated by a non-integral value. | |
1866 // FIXME: See crbug.com/382491. getCTM does not include scale factors applie
d at raster time, such | |
1867 // as device zoom. | |
1868 return !context->getCTM().isIdentityOrTranslationOrFlipped(); | |
1869 } | |
1870 | |
1871 void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, Tra
nsformState& transformState) const | 579 void RenderBoxModelObject::mapAbsoluteToLocalPoint(MapCoordinatesFlags mode, Tra
nsformState& transformState) const |
1872 { | 580 { |
1873 RenderObject* o = container(); | 581 RenderObject* o = container(); |
1874 if (!o) | 582 if (!o) |
1875 return; | 583 return; |
1876 | 584 |
1877 if (o->isRenderFlowThread()) | 585 if (o->isRenderFlowThread()) |
1878 transformState.move(o->columnOffset(LayoutPoint(transformState.mappedPoi
nt()))); | 586 transformState.move(o->columnOffset(LayoutPoint(transformState.mappedPoi
nt()))); |
1879 | 587 |
1880 o->mapAbsoluteToLocalPoint(mode, transformState); | 588 o->mapAbsoluteToLocalPoint(mode, transformState); |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1965 ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); | 673 ASSERT(!beforeChild || toBoxModelObject == beforeChild->parent()); |
1966 for (RenderObject* child = startChild; child && child != endChild; ) { | 674 for (RenderObject* child = startChild; child && child != endChild; ) { |
1967 // Save our next sibling as moveChildTo will clear it. | 675 // Save our next sibling as moveChildTo will clear it. |
1968 RenderObject* nextSibling = child->nextSibling(); | 676 RenderObject* nextSibling = child->nextSibling(); |
1969 moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert); | 677 moveChildTo(toBoxModelObject, child, beforeChild, fullRemoveInsert); |
1970 child = nextSibling; | 678 child = nextSibling; |
1971 } | 679 } |
1972 } | 680 } |
1973 | 681 |
1974 } // namespace blink | 682 } // namespace blink |
OLD | NEW |