blob: e092280e3819bdc33931fafa76a0fb50cc9d9ffe [file] [log] [blame]
/*
* Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core/layout/shapes/Shape.h"
#include <memory>
#include "core/css/BasicShapeFunctions.h"
#include "core/dom/DOMArrayBuffer.h"
#include "core/dom/DOMTypedArray.h"
#include "core/layout/shapes/BoxShape.h"
#include "core/layout/shapes/PolygonShape.h"
#include "core/layout/shapes/RasterShape.h"
#include "core/layout/shapes/RectangleShape.h"
#include "core/svg/graphics/SVGImage.h"
#include "platform/LengthFunctions.h"
#include "platform/geometry/FloatRoundedRect.h"
#include "platform/geometry/FloatSize.h"
#include "platform/graphics/GraphicsTypes.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/paint/PaintFlags.h"
#include "platform/wtf/MathExtras.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/typed_arrays/ArrayBufferContents.h"
namespace blink {
static std::unique_ptr<Shape> CreateInsetShape(const FloatRoundedRect& bounds) {
DCHECK_GE(bounds.Rect().Width(), 0);
DCHECK_GE(bounds.Rect().Height(), 0);
return WTF::MakeUnique<BoxShape>(bounds);
}
static std::unique_ptr<Shape> CreateCircleShape(const FloatPoint& center,
float radius) {
DCHECK_GE(radius, 0);
return WTF::WrapUnique(
new RectangleShape(FloatRect(center.X() - radius, center.Y() - radius,
radius * 2, radius * 2),
FloatSize(radius, radius)));
}
static std::unique_ptr<Shape> CreateEllipseShape(const FloatPoint& center,
const FloatSize& radii) {
DCHECK_GE(radii.Width(), 0);
DCHECK_GE(radii.Height(), 0);
return WTF::WrapUnique(new RectangleShape(
FloatRect(center.X() - radii.Width(), center.Y() - radii.Height(),
radii.Width() * 2, radii.Height() * 2),
radii));
}
static std::unique_ptr<Shape> CreatePolygonShape(
std::unique_ptr<Vector<FloatPoint>> vertices,
WindRule fill_rule) {
return WTF::WrapUnique(new PolygonShape(std::move(vertices), fill_rule));
}
static inline FloatRect PhysicalRectToLogical(const FloatRect& rect,
float logical_box_height,
WritingMode writing_mode) {
if (IsHorizontalWritingMode(writing_mode))
return rect;
if (IsFlippedBlocksWritingMode(writing_mode))
return FloatRect(rect.Y(), logical_box_height - rect.MaxX(), rect.Height(),
rect.Width());
return rect.TransposedRect();
}
static inline FloatPoint PhysicalPointToLogical(const FloatPoint& point,
float logical_box_height,
WritingMode writing_mode) {
if (IsHorizontalWritingMode(writing_mode))
return point;
if (IsFlippedBlocksWritingMode(writing_mode))
return FloatPoint(point.Y(), logical_box_height - point.X());
return point.TransposedPoint();
}
static inline FloatSize PhysicalSizeToLogical(const FloatSize& size,
WritingMode writing_mode) {
if (IsHorizontalWritingMode(writing_mode))
return size;
return size.TransposedSize();
}
std::unique_ptr<Shape> Shape::CreateShape(const BasicShape* basic_shape,
const LayoutSize& logical_box_size,
WritingMode writing_mode,
float margin) {
DCHECK(basic_shape);
bool horizontal_writing_mode = IsHorizontalWritingMode(writing_mode);
float box_width = horizontal_writing_mode
? logical_box_size.Width().ToFloat()
: logical_box_size.Height().ToFloat();
float box_height = horizontal_writing_mode
? logical_box_size.Height().ToFloat()
: logical_box_size.Width().ToFloat();
std::unique_ptr<Shape> shape;
switch (basic_shape->GetType()) {
case BasicShape::kBasicShapeCircleType: {
const BasicShapeCircle* circle = ToBasicShapeCircle(basic_shape);
FloatPoint center =
FloatPointForCenterCoordinate(circle->CenterX(), circle->CenterY(),
FloatSize(box_width, box_height));
float radius =
circle->FloatValueForRadiusInBox(FloatSize(box_width, box_height));
FloatPoint logical_center = PhysicalPointToLogical(
center, logical_box_size.Height().ToFloat(), writing_mode);
shape = CreateCircleShape(logical_center, radius);
break;
}
case BasicShape::kBasicShapeEllipseType: {
const BasicShapeEllipse* ellipse = ToBasicShapeEllipse(basic_shape);
FloatPoint center =
FloatPointForCenterCoordinate(ellipse->CenterX(), ellipse->CenterY(),
FloatSize(box_width, box_height));
float radius_x = ellipse->FloatValueForRadiusInBox(ellipse->RadiusX(),
center.X(), box_width);
float radius_y = ellipse->FloatValueForRadiusInBox(
ellipse->RadiusY(), center.Y(), box_height);
FloatPoint logical_center = PhysicalPointToLogical(
center, logical_box_size.Height().ToFloat(), writing_mode);
shape = CreateEllipseShape(logical_center, FloatSize(radius_x, radius_y));
break;
}
case BasicShape::kBasicShapePolygonType: {
const BasicShapePolygon* polygon = ToBasicShapePolygon(basic_shape);
const Vector<Length>& values = polygon->Values();
size_t values_size = values.size();
DCHECK(!(values_size % 2));
std::unique_ptr<Vector<FloatPoint>> vertices =
WTF::WrapUnique(new Vector<FloatPoint>(values_size / 2));
for (unsigned i = 0; i < values_size; i += 2) {
FloatPoint vertex(FloatValueForLength(values.at(i), box_width),
FloatValueForLength(values.at(i + 1), box_height));
(*vertices)[i / 2] = PhysicalPointToLogical(
vertex, logical_box_size.Height().ToFloat(), writing_mode);
}
shape = CreatePolygonShape(std::move(vertices), polygon->GetWindRule());
break;
}
case BasicShape::kBasicShapeInsetType: {
const BasicShapeInset& inset = *ToBasicShapeInset(basic_shape);
float left = FloatValueForLength(inset.Left(), box_width);
float top = FloatValueForLength(inset.Top(), box_height);
float right = FloatValueForLength(inset.Right(), box_width);
float bottom = FloatValueForLength(inset.Bottom(), box_height);
FloatRect rect(left, top, std::max<float>(box_width - left - right, 0),
std::max<float>(box_height - top - bottom, 0));
FloatRect logical_rect = PhysicalRectToLogical(
rect, logical_box_size.Height().ToFloat(), writing_mode);
FloatSize box_size(box_width, box_height);
FloatSize top_left_radius = PhysicalSizeToLogical(
FloatSizeForLengthSize(inset.TopLeftRadius(), box_size),
writing_mode);
FloatSize top_right_radius = PhysicalSizeToLogical(
FloatSizeForLengthSize(inset.TopRightRadius(), box_size),
writing_mode);
FloatSize bottom_left_radius = PhysicalSizeToLogical(
FloatSizeForLengthSize(inset.BottomLeftRadius(), box_size),
writing_mode);
FloatSize bottom_right_radius = PhysicalSizeToLogical(
FloatSizeForLengthSize(inset.BottomRightRadius(), box_size),
writing_mode);
FloatRoundedRect::Radii corner_radii(top_left_radius, top_right_radius,
bottom_left_radius,
bottom_right_radius);
FloatRoundedRect final_rect(logical_rect, corner_radii);
final_rect.ConstrainRadii();
shape = CreateInsetShape(final_rect);
break;
}
default:
NOTREACHED();
}
shape->writing_mode_ = writing_mode;
shape->margin_ = margin;
return shape;
}
std::unique_ptr<Shape> Shape::CreateEmptyRasterShape(WritingMode writing_mode,
float margin) {
std::unique_ptr<RasterShapeIntervals> intervals =
WTF::MakeUnique<RasterShapeIntervals>(0, 0);
std::unique_ptr<RasterShape> raster_shape =
WTF::WrapUnique(new RasterShape(std::move(intervals), IntSize()));
raster_shape->writing_mode_ = writing_mode;
raster_shape->margin_ = margin;
return std::move(raster_shape);
}
std::unique_ptr<Shape> Shape::CreateRasterShape(Image* image,
float threshold,
const LayoutRect& image_r,
const LayoutRect& margin_r,
WritingMode writing_mode,
float margin) {
IntRect image_rect = PixelSnappedIntRect(image_r);
IntRect margin_rect = PixelSnappedIntRect(margin_r);
std::unique_ptr<RasterShapeIntervals> intervals = WTF::WrapUnique(
new RasterShapeIntervals(margin_rect.Height(), -margin_rect.Y()));
std::unique_ptr<ImageBuffer> image_buffer =
ImageBuffer::Create(image_rect.Size());
if (image && image_buffer) {
// FIXME: This is not totally correct but it is needed to prevent shapes
// that loads SVG Images during paint invalidations to mark layoutObjects
// for layout, which is not allowed. See https://crbug.com/429346
ImageObserverDisabler disabler(image);
PaintFlags flags;
IntRect image_source_rect(IntPoint(), image->Size());
IntRect image_dest_rect(IntPoint(), image_rect.Size());
// TODO(ccameron): No color conversion is required here.
image->Draw(image_buffer->Canvas(), flags, image_dest_rect,
image_source_rect, kDoNotRespectImageOrientation,
Image::kDoNotClampImageToSourceRect);
WTF::ArrayBufferContents contents;
image_buffer->GetImageData(
kUnmultiplied, IntRect(IntPoint(), image_rect.Size()), contents);
DOMArrayBuffer* array_buffer = DOMArrayBuffer::Create(contents);
DOMUint8ClampedArray* pixel_array = DOMUint8ClampedArray::Create(
array_buffer, 0, array_buffer->ByteLength());
unsigned pixel_array_offset = 3; // Each pixel is four bytes: RGBA.
uint8_t alpha_pixel_threshold = threshold * 255;
DCHECK_EQ(
static_cast<unsigned>(image_rect.Width() * image_rect.Height() * 4),
pixel_array->length());
int min_buffer_y = std::max(0, margin_rect.Y() - image_rect.Y());
int max_buffer_y =
std::min(image_rect.Height(), margin_rect.MaxY() - image_rect.Y());
for (int y = min_buffer_y; y < max_buffer_y; ++y) {
int start_x = -1;
for (int x = 0; x < image_rect.Width(); ++x, pixel_array_offset += 4) {
uint8_t alpha = pixel_array->Item(pixel_array_offset);
bool alpha_above_threshold = alpha > alpha_pixel_threshold;
if (start_x == -1 && alpha_above_threshold) {
start_x = x;
} else if (start_x != -1 &&
(!alpha_above_threshold || x == image_rect.Width() - 1)) {
int end_x = alpha_above_threshold ? x + 1 : x;
intervals->IntervalAt(y + image_rect.Y())
.Unite(IntShapeInterval(start_x + image_rect.X(),
end_x + image_rect.X()));
start_x = -1;
}
}
}
}
std::unique_ptr<RasterShape> raster_shape = WTF::WrapUnique(
new RasterShape(std::move(intervals), margin_rect.Size()));
raster_shape->writing_mode_ = writing_mode;
raster_shape->margin_ = margin;
return std::move(raster_shape);
}
std::unique_ptr<Shape> Shape::CreateLayoutBoxShape(
const FloatRoundedRect& rounded_rect,
WritingMode writing_mode,
float margin) {
FloatRect rect(0, 0, rounded_rect.Rect().Width(),
rounded_rect.Rect().Height());
FloatRoundedRect bounds(rect, rounded_rect.GetRadii());
std::unique_ptr<Shape> shape = CreateInsetShape(bounds);
shape->writing_mode_ = writing_mode;
shape->margin_ = margin;
return shape;
}
} // namespace blink