blob: f259ef9fc6f4e008306b50b42f6a61b5f4d532fa [file] [log] [blame]
/*
* Copyright (C) 2020-2021 Apple Inc. 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 APPLE INC. AND ITS 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 APPLE INC. OR ITS 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 "config.h"
#include <WebCore/DisplayList.h>
#include <WebCore/DisplayListItems.h>
#include <WebCore/DisplayListIterator.h>
#include <WebCore/Gradient.h>
#include <WebCore/InMemoryDisplayList.h>
namespace TestWebKitAPI {
using namespace WebCore;
using DisplayList::DisplayList;
using namespace DisplayList;
constexpr size_t globalItemBufferCapacity = 1 << 12;
static uint8_t globalItemBuffer[globalItemBufferCapacity];
static Ref<Gradient> createGradient()
{
auto gradient = Gradient::create(Gradient::ConicData { { 0., 0. }, 1.25 });
gradient->addColorStop({ 0.1, Color::red });
gradient->addColorStop({ 0.5, Color::green });
gradient->addColorStop({ 0.9, Color::blue });
return gradient;
}
static Path createComplexPath()
{
Path path;
path.moveTo({ 10., 10. });
path.addLineTo({ 50., 50. });
path.addQuadCurveTo({ 100., 100. }, { 0., 200. });
path.addLineTo({ 10., 10. });
return path;
}
TEST(DisplayListTests, AppendItems)
{
InMemoryDisplayList list;
EXPECT_TRUE(list.isEmpty());
auto gradient = createGradient();
auto path = createComplexPath();
for (int i = 0; i < 50; ++i) {
list.append<SetStrokeThickness>(1.5);
list.append<FillPath>(path);
list.append<FillRectWithGradient>(FloatRect { 1., 1., 10., 10. }, gradient);
list.append<SetInlineFillColor>(Color::red);
#if ENABLE(INLINE_PATH_DATA)
list.append<StrokeLine>(LineData {{ 0., 0. }, { 10., 15. }});
#endif
}
EXPECT_FALSE(list.isEmpty());
bool observedUnexpectedItem = false;
for (auto displayListItem : list) {
auto handle = displayListItem->item;
switch (handle.type()) {
case ItemType::SetStrokeThickness: {
EXPECT_FALSE(handle.isDrawingItem());
EXPECT_TRUE(handle.is<SetStrokeThickness>());
auto& item = handle.get<SetStrokeThickness>();
EXPECT_EQ(item.thickness(), 1.5);
break;
}
case ItemType::FillPath: {
EXPECT_TRUE(handle.isDrawingItem());
EXPECT_TRUE(handle.is<FillPath>());
auto& item = handle.get<FillPath>();
EXPECT_EQ(item.path().platformPath(), path.platformPath());
break;
}
case ItemType::FillRectWithGradient: {
EXPECT_TRUE(handle.isDrawingItem());
EXPECT_TRUE(handle.is<FillRectWithGradient>());
auto& item = handle.get<FillRectWithGradient>();
EXPECT_EQ(item.rect(), FloatRect(1., 1., 10., 10.));
EXPECT_EQ(&item.gradient(), gradient.ptr());
break;
}
case ItemType::SetInlineFillColor: {
EXPECT_FALSE(handle.isDrawingItem());
EXPECT_TRUE(handle.is<SetInlineFillColor>());
auto& item = handle.get<SetInlineFillColor>();
EXPECT_EQ(item.color(), Color::red);
break;
}
#if ENABLE(INLINE_PATH_DATA)
case ItemType::StrokeLine: {
EXPECT_TRUE(handle.isDrawingItem());
EXPECT_TRUE(handle.is<StrokeLine>());
auto& item = handle.get<StrokeLine>();
EXPECT_EQ(item.start(), FloatPoint(0, 0));
EXPECT_EQ(item.end(), FloatPoint(10., 15.));
break;
}
#endif
case ItemType::MetaCommandChangeItemBuffer:
break;
default: {
observedUnexpectedItem = true;
break;
}
}
}
EXPECT_FALSE(observedUnexpectedItem);
EXPECT_GT(list.sizeInBytes(), 0U);
list.clear();
observedUnexpectedItem = false;
for (auto itemAndExtent : list) {
UNUSED_PARAM(itemAndExtent);
observedUnexpectedItem = true;
}
EXPECT_TRUE(list.isEmpty());
EXPECT_EQ(list.sizeInBytes(), 0U);
EXPECT_FALSE(observedUnexpectedItem);
list.append<FillRectWithColor>(FloatRect { 0, 0, 100, 100 }, Color::black);
for (auto displayListItem : list) {
auto handle = displayListItem->item;
EXPECT_EQ(handle.type(), ItemType::FillRectWithColor);
EXPECT_TRUE(handle.is<FillRectWithColor>());
auto& item = handle.get<FillRectWithColor>();
EXPECT_EQ(*item.color().tryGetAsSRGBABytes(), Color::black);
EXPECT_EQ(item.rect(), FloatRect(0, 0, 100, 100));
}
EXPECT_FALSE(list.isEmpty());
EXPECT_GT(list.sizeInBytes(), 0U);
}
TEST(DisplayListTests, ItemBufferClient)
{
Vector<StrokePath> strokePathItems;
static ItemBufferIdentifier globalBufferIdentifier = ItemBufferIdentifier::generate();
class StrokePathReader : public ItemBufferReadingClient {
public:
StrokePathReader(const Vector<StrokePath>& items)
: m_items(items)
{
}
private:
std::optional<ItemHandle> WARN_UNUSED_RETURN decodeItem(const uint8_t* data, size_t dataLength, ItemType type, uint8_t* handleLocation) final
{
EXPECT_EQ(type, ItemType::StrokePath);
EXPECT_EQ(dataLength, sizeof(size_t));
new (handleLocation + sizeof(uint64_t)) StrokePath(m_items[*reinterpret_cast<const size_t*>(data)]);
return {{ handleLocation }};
}
const Vector<StrokePath>& m_items;
};
class StrokePathWriter : public ItemBufferWritingClient {
public:
StrokePathWriter(Vector<StrokePath>& items)
: m_items(items)
{
}
private:
ItemBufferHandle createItemBuffer(size_t capacity) final
{
EXPECT_LT(capacity, globalItemBufferCapacity);
return { globalBufferIdentifier, globalItemBuffer, globalItemBufferCapacity };
}
RefPtr<SharedBuffer> encodeItemOutOfLine(const DisplayListItem& displayListItem) const final
{
auto index = m_items.size();
m_items.append(std::get<StrokePath>(displayListItem));
return SharedBuffer::create(reinterpret_cast<uint8_t*>(&index), sizeof(size_t));
}
Vector<StrokePath>& m_items;
};
DisplayList list;
StrokePathWriter writer { strokePathItems };
list.setItemBufferWritingClient(&writer);
auto path = createComplexPath();
list.append<SetInlineStrokeColor>(Color::blue);
list.append<StrokePath>(path);
list.append<SetInlineStrokeColor>(Color::red);
list.append<StrokePath>(path);
DisplayList shallowCopy {{ ItemBufferHandle { globalBufferIdentifier, globalItemBuffer, list.sizeInBytes() } }};
StrokePathReader reader { strokePathItems };
shallowCopy.setItemBufferReadingClient(&reader);
Vector<ItemType> itemTypes;
for (auto displayListItem : shallowCopy)
itemTypes.append(displayListItem->item.type());
EXPECT_FALSE(shallowCopy.isEmpty());
EXPECT_EQ(itemTypes.size(), 4U);
EXPECT_EQ(itemTypes[0], ItemType::SetInlineStrokeColor);
EXPECT_EQ(itemTypes[1], ItemType::StrokePath);
EXPECT_EQ(itemTypes[2], ItemType::SetInlineStrokeColor);
EXPECT_EQ(itemTypes[3], ItemType::StrokePath);
}
} // namespace TestWebKitAPI