diff options
Diffstat (limited to 'Source/WebCore/rendering/style/NinePieceImage.cpp')
-rw-r--r-- | Source/WebCore/rendering/style/NinePieceImage.cpp | 240 |
1 files changed, 217 insertions, 23 deletions
diff --git a/Source/WebCore/rendering/style/NinePieceImage.cpp b/Source/WebCore/rendering/style/NinePieceImage.cpp index 43bd1b043..e8c9232c1 100644 --- a/Source/WebCore/rendering/style/NinePieceImage.cpp +++ b/Source/WebCore/rendering/style/NinePieceImage.cpp @@ -2,7 +2,7 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2005, 2006, 2007, 2008, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2003-2017 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -23,13 +23,19 @@ #include "config.h" #include "NinePieceImage.h" + +#include "GraphicsContext.h" +#include "LengthFunctions.h" +#include "RenderStyle.h" +#include "TextStream.h" #include <wtf/NeverDestroyed.h> +#include <wtf/PointerComparison.h> namespace WebCore { -static DataRef<NinePieceImageData>& defaultData() +inline DataRef<NinePieceImage::Data>& NinePieceImage::defaultData() { - static NeverDestroyed<DataRef<NinePieceImageData>> data(NinePieceImageData::create()); + static NeverDestroyed<DataRef<Data>> data { Data::create() }; return data.get(); } @@ -38,31 +44,203 @@ NinePieceImage::NinePieceImage() { } -NinePieceImage::NinePieceImage(PassRefPtr<StyleImage> image, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule horizontalRule, ENinePieceImageRule verticalRule) - : m_data(NinePieceImageData::create()) +NinePieceImage::NinePieceImage(RefPtr<StyleImage>&& image, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule horizontalRule, ENinePieceImageRule verticalRule) + : m_data(Data::create(WTFMove(image), imageSlices, fill, borderSlices, outset, horizontalRule, verticalRule)) +{ +} + +LayoutUnit NinePieceImage::computeSlice(Length length, LayoutUnit width, LayoutUnit slice, LayoutUnit extent) +{ + if (length.isRelative()) + return length.value() * width; + if (length.isAuto()) + return slice; + return valueForLength(length, extent); +} + +LayoutBoxExtent NinePieceImage::computeSlices(const LayoutSize& size, const LengthBox& lengths, int scaleFactor) +{ + return { + std::min(size.height(), valueForLength(lengths.top(), size.height())) * scaleFactor, + std::min(size.width(), valueForLength(lengths.right(), size.width())) * scaleFactor, + std::min(size.height(), valueForLength(lengths.bottom(), size.height())) * scaleFactor, + std::min(size.width(), valueForLength(lengths.left(), size.width())) * scaleFactor + }; +} + +LayoutBoxExtent NinePieceImage::computeSlices(const LayoutSize& size, const LengthBox& lengths, const FloatBoxExtent& widths, const LayoutBoxExtent& slices) { - m_data.access()->image = image; - m_data.access()->imageSlices = std::move(imageSlices); - m_data.access()->borderSlices = std::move(borderSlices); - m_data.access()->outset = std::move(outset); - m_data.access()->fill = fill; - m_data.access()->horizontalRule = horizontalRule; - m_data.access()->verticalRule = verticalRule; + return { + computeSlice(lengths.top(), widths.top(), slices.top(), size.height()), + computeSlice(lengths.right(), widths.right(), slices.right(), size.width()), + computeSlice(lengths.bottom(), widths.bottom(), slices.bottom(), size.height()), + computeSlice(lengths.left(), widths.left(), slices.left(), size.width()) + }; } -NinePieceImageData::NinePieceImageData() +void NinePieceImage::scaleSlicesIfNeeded(const LayoutSize& size, LayoutBoxExtent& slices, float deviceScaleFactor) +{ + LayoutUnit width = std::max<LayoutUnit>(1 / deviceScaleFactor, slices.left() + slices.right()); + LayoutUnit height = std::max<LayoutUnit>(1 / deviceScaleFactor, slices.top() + slices.bottom()); + + float sliceScaleFactor = std::min((float)size.width() / width, (float)size.height() / height); + + if (sliceScaleFactor >= 1) + return; + + // All slices are reduced by multiplying them by sliceScaleFactor. + slices.top() *= sliceScaleFactor; + slices.right() *= sliceScaleFactor; + slices.bottom() *= sliceScaleFactor; + slices.left() *= sliceScaleFactor; +} + +bool NinePieceImage::isEmptyPieceRect(ImagePiece piece, const LayoutBoxExtent& slices) +{ + if (piece == MiddlePiece) + return false; + + PhysicalBoxSide horizontalSide = imagePieceHorizontalSide(piece); + PhysicalBoxSide verticalSide = imagePieceVerticalSide(piece); + return !((horizontalSide == NilSide || slices.at(horizontalSide)) && (verticalSide == NilSide || slices.at(verticalSide))); +} + +bool NinePieceImage::isEmptyPieceRect(ImagePiece piece, const Vector<FloatRect>& destinationRects, const Vector<FloatRect>& sourceRects) +{ + return destinationRects[piece].isEmpty() || sourceRects[piece].isEmpty(); +} + +Vector<FloatRect> NinePieceImage::computeNineRects(const FloatRect& outer, const LayoutBoxExtent& slices, float deviceScaleFactor) +{ + FloatRect inner = outer; + inner.move(slices.left(), slices.top()); + inner.contract(slices.left() + slices.right(), slices.top() + slices.bottom()); + ASSERT(outer.contains(inner)); + + Vector<FloatRect> rects(MaxPiece); + + rects[TopLeftPiece] = snapRectToDevicePixels(outer.x(), outer.y(), slices.left(), slices.top(), deviceScaleFactor); + rects[BottomLeftPiece] = snapRectToDevicePixels(outer.x(), inner.maxY(), slices.left(), slices.bottom(), deviceScaleFactor); + rects[LeftPiece] = snapRectToDevicePixels(outer.x(), inner.y(), slices.left(), inner.height(), deviceScaleFactor); + + rects[TopRightPiece] = snapRectToDevicePixels(inner.maxX(), outer.y(), slices.right(), slices.top(), deviceScaleFactor); + rects[BottomRightPiece] = snapRectToDevicePixels(inner.maxX(), inner.maxY(), slices.right(), slices.bottom(), deviceScaleFactor); + rects[RightPiece] = snapRectToDevicePixels(inner.maxX(), inner.y(), slices.right(), inner.height(), deviceScaleFactor); + + rects[TopPiece] = snapRectToDevicePixels(inner.x(), outer.y(), inner.width(), slices.top(), deviceScaleFactor); + rects[BottomPiece] = snapRectToDevicePixels(inner.x(), inner.maxY(), inner.width(), slices.bottom(), deviceScaleFactor); + + rects[MiddlePiece] = snapRectToDevicePixels(inner.x(), inner.y(), inner.width(), inner.height(), deviceScaleFactor); + return rects; +} + +FloatSize NinePieceImage::computeSideTileScale(ImagePiece piece, const Vector<FloatRect>& destinationRects, const Vector<FloatRect>& sourceRects) +{ + ASSERT(!isCornerPiece(piece) && !isMiddlePiece(piece)); + if (isEmptyPieceRect(piece, destinationRects, sourceRects)) + return FloatSize(1, 1); + + float scale; + if (isHorizontalPiece(piece)) + scale = destinationRects[piece].height() / sourceRects[piece].height(); + else + scale = destinationRects[piece].width() / sourceRects[piece].width(); + + return FloatSize(scale, scale); +} + +FloatSize NinePieceImage::computeMiddleTileScale(const Vector<FloatSize>& scales, const Vector<FloatRect>& destinationRects, const Vector<FloatRect>& sourceRects, ENinePieceImageRule hRule, ENinePieceImageRule vRule) +{ + FloatSize scale(1, 1); + if (isEmptyPieceRect(MiddlePiece, destinationRects, sourceRects)) + return scale; + + // Unlike the side pieces, the middle piece can have "stretch" specified in one axis but not the other. + // In fact the side pieces don't even use the scale factor unless they have a rule other than "stretch". + if (hRule == StretchImageRule) + scale.setWidth(destinationRects[MiddlePiece].width() / sourceRects[MiddlePiece].width()); + else if (!isEmptyPieceRect(TopPiece, destinationRects, sourceRects)) + scale.setWidth(scales[TopPiece].width()); + else if (!isEmptyPieceRect(BottomPiece, destinationRects, sourceRects)) + scale.setWidth(scales[BottomPiece].width()); + + if (vRule == StretchImageRule) + scale.setHeight(destinationRects[MiddlePiece].height() / sourceRects[MiddlePiece].height()); + else if (!isEmptyPieceRect(LeftPiece, destinationRects, sourceRects)) + scale.setHeight(scales[LeftPiece].height()); + else if (!isEmptyPieceRect(RightPiece, destinationRects, sourceRects)) + scale.setHeight(scales[RightPiece].height()); + + return scale; +} + +Vector<FloatSize> NinePieceImage::computeTileScales(const Vector<FloatRect>& destinationRects, const Vector<FloatRect>& sourceRects, ENinePieceImageRule hRule, ENinePieceImageRule vRule) +{ + Vector<FloatSize> scales(MaxPiece, FloatSize(1, 1)); + + scales[TopPiece] = computeSideTileScale(TopPiece, destinationRects, sourceRects); + scales[RightPiece] = computeSideTileScale(RightPiece, destinationRects, sourceRects); + scales[BottomPiece] = computeSideTileScale(BottomPiece, destinationRects, sourceRects); + scales[LeftPiece] = computeSideTileScale(LeftPiece, destinationRects, sourceRects); + + scales[MiddlePiece] = computeMiddleTileScale(scales, destinationRects, sourceRects, hRule, vRule); + return scales; +} + +void NinePieceImage::paint(GraphicsContext& graphicsContext, RenderElement* renderer, const RenderStyle& style, const LayoutRect& destination, const LayoutSize& source, float deviceScaleFactor, CompositeOperator op) const +{ + StyleImage* styleImage = image(); + ASSERT(styleImage); + ASSERT(styleImage->isLoaded()); + + LayoutBoxExtent sourceSlices = computeSlices(source, imageSlices(), styleImage->imageScaleFactor()); + LayoutBoxExtent destinationSlices = computeSlices(destination.size(), borderSlices(), style.borderWidth(), sourceSlices); + + scaleSlicesIfNeeded(destination.size(), destinationSlices, deviceScaleFactor); + + Vector<FloatRect> destinationRects = computeNineRects(destination, destinationSlices, deviceScaleFactor); + Vector<FloatRect> sourceRects = computeNineRects(FloatRect(FloatPoint(), source), sourceSlices, deviceScaleFactor); + Vector<FloatSize> tileScales = computeTileScales(destinationRects, sourceRects, horizontalRule(), verticalRule()); + + RefPtr<Image> image = styleImage->image(renderer, source); + if (!image) + return; + + for (ImagePiece piece = MinPiece; piece < MaxPiece; ++piece) { + if ((piece == MiddlePiece && !fill()) || isEmptyPieceRect(piece, destinationRects, sourceRects)) + continue; + + if (isCornerPiece(piece)) { + graphicsContext.drawImage(*image, destinationRects[piece], sourceRects[piece], op); + continue; + } + + Image::TileRule hRule = isHorizontalPiece(piece) ? static_cast<Image::TileRule>(horizontalRule()) : Image::StretchTile; + Image::TileRule vRule = isVerticalPiece(piece) ? static_cast<Image::TileRule>(verticalRule()) : Image::StretchTile; + graphicsContext.drawTiledImage(*image, destinationRects[piece], sourceRects[piece], tileScales[piece], hRule, vRule, op); + } +} + +inline NinePieceImage::Data::Data() : fill(false) , horizontalRule(StretchImageRule) , verticalRule(StretchImageRule) - , image(0) - , imageSlices(Length(100, Percent), Length(100, Percent), Length(100, Percent), Length(100, Percent)) - , borderSlices(Length(1, Relative), Length(1, Relative), Length(1, Relative), Length(1, Relative)) - , outset(0) { } -inline NinePieceImageData::NinePieceImageData(const NinePieceImageData& other) - : RefCounted<NinePieceImageData>() +inline NinePieceImage::Data::Data(RefPtr<StyleImage>&& image, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule horizontalRule, ENinePieceImageRule verticalRule) + : fill(fill) + , horizontalRule(horizontalRule) + , verticalRule(verticalRule) + , image(WTFMove(image)) + , imageSlices(imageSlices) + , borderSlices(borderSlices) + , outset(outset) +{ +} + +inline NinePieceImage::Data::Data(const Data& other) + : RefCounted<Data>() , fill(other.fill) , horizontalRule(other.horizontalRule) , verticalRule(other.verticalRule) @@ -73,14 +251,24 @@ inline NinePieceImageData::NinePieceImageData(const NinePieceImageData& other) { } -PassRef<NinePieceImageData> NinePieceImageData::copy() const +inline Ref<NinePieceImage::Data> NinePieceImage::Data::create() { - return adoptRef(*new NinePieceImageData(*this)); + return adoptRef(*new Data); } -bool NinePieceImageData::operator==(const NinePieceImageData& other) const +inline Ref<NinePieceImage::Data> NinePieceImage::Data::create(RefPtr<StyleImage>&& image, LengthBox imageSlices, bool fill, LengthBox borderSlices, LengthBox outset, ENinePieceImageRule horizontalRule, ENinePieceImageRule verticalRule) { - return StyleImage::imagesEquivalent(image.get(), other.image.get()) + return adoptRef(*new Data(WTFMove(image), imageSlices, fill, borderSlices, outset, horizontalRule, verticalRule)); +} + +Ref<NinePieceImage::Data> NinePieceImage::Data::copy() const +{ + return adoptRef(*new Data(*this)); +} + +bool NinePieceImage::Data::operator==(const Data& other) const +{ + return arePointingToEqualData(image, other.image) && imageSlices == other.imageSlices && fill == other.fill && borderSlices == other.borderSlices @@ -89,4 +277,10 @@ bool NinePieceImageData::operator==(const NinePieceImageData& other) const && verticalRule == other.verticalRule; } +TextStream& operator<<(TextStream& ts, const NinePieceImage& image) +{ + ts << "style-image " << image.image() << " slices " << image.imageSlices(); + return ts; +} + } |