blob: 96ddbcb78179bc98ecaf263138e329279d8ef185 [file] [log] [blame]
/*
* Copyright 2020 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkDrawable.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/core/SkPicture.h"
#include "include/core/SkPictureRecorder.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkString.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkRuntimeEffect.h"
#include "src/base/SkArenaAlloc.h"
#include "src/core/SkCanvasPriv.h"
#include "src/core/SkGlyph.h"
#include "src/core/SkMask.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
#include "tests/Test.h"
#include <cstddef>
#include <cstdint>
#include <initializer_list>
#include <iterator>
#include <optional>
DEF_TEST(SkGlyphRectBasic, reporter) {
using namespace skglyph;
SkGlyphRect r{1, 1, 10, 10};
REPORTER_ASSERT(reporter, !r.empty());
SkGlyphRect a = rect_union(r, empty_rect());
REPORTER_ASSERT(reporter, a.rect() == SkRect::MakeLTRB(1, 1, 10, 10));
auto widthHeight = a.widthHeight();
REPORTER_ASSERT(reporter, widthHeight.x() == 9 && widthHeight.y() == 9);
a = rect_intersection(r, full_rect());
REPORTER_ASSERT(reporter, a.rect() == SkRect::MakeLTRB(1, 1, 10, 10));
SkGlyphRect acc = full_rect();
for (int x = -10; x < 10; x++) {
for(int y = -10; y < 10; y++) {
acc = rect_intersection(acc, SkGlyphRect(x, y, x + 20, y + 20));
}
}
REPORTER_ASSERT(reporter, acc.rect() == SkRect::MakeLTRB(9, 9, 10, 10));
acc = empty_rect();
for (int x = -10; x < 10; x++) {
for(int y = -10; y < 10; y++) {
acc = rect_union(acc, SkGlyphRect(x, y, x + 20, y + 20));
}
}
REPORTER_ASSERT(reporter, acc.rect() == SkRect::MakeLTRB(-10, -10, 29, 29));
}
class SkGlyphTestPeer {
public:
static void SetGlyph1(SkGlyph* glyph) {
glyph->fAdvanceX = 10;
glyph->fAdvanceY = 11;
glyph->fLeft = -1;
glyph->fTop = -2;
glyph->fWidth = 8;
glyph->fHeight = 9;
glyph->fMaskFormat = SkMask::Format::kA8_Format;
}
static void SetGlyph2(SkGlyph* glyph) {
glyph->fAdvanceX = 10;
glyph->fAdvanceY = 11;
glyph->fLeft = 0;
glyph->fTop = -1;
glyph->fWidth = 8;
glyph->fHeight = 9;
glyph->fMaskFormat = SkMask::Format::kA8_Format;
}
};
DEF_TEST(SkGlyph_SendMetrics, reporter) {
{
SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
SkGlyphTestPeer::SetGlyph1(&srcGlyph);
SkBinaryWriteBuffer writeBuffer;
srcGlyph.flattenMetrics(writeBuffer);
sk_sp<SkData> data = writeBuffer.snapshotAsData();
SkReadBuffer readBuffer{data->data(), data->size()};
std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
}
{
SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
SkGlyphTestPeer::SetGlyph2(&srcGlyph);
SkBinaryWriteBuffer writeBuffer;
srcGlyph.flattenMetrics(writeBuffer);
sk_sp<SkData> data = writeBuffer.snapshotAsData();
SkReadBuffer readBuffer{data->data(), data->size()};
std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
}
uint8_t badData[] = {1, 2, 3, 4, 5, 6, 7, 8};
SkReadBuffer badBuffer{badData, std::size(badData)};
std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(badBuffer);
REPORTER_ASSERT(reporter, !badBuffer.isValid());
REPORTER_ASSERT(reporter, !dstGlyph.has_value());
}
DEF_TEST(SkGlyph_SendWithImage, reporter) {
SkArenaAlloc alloc{256};
SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
SkGlyphTestPeer::SetGlyph1(&srcGlyph);
static constexpr uint8_t X = 0xff;
static constexpr uint8_t O = 0x00;
uint8_t imageData[][8] = {
{X,X,X,X,X,X,X,X},
{X,O,O,O,O,O,O,X},
{X,O,O,O,O,O,O,X},
{X,O,O,O,O,O,O,X},
{X,O,O,X,X,O,O,X},
{X,O,O,O,O,O,O,X},
{X,O,O,O,O,O,O,X},
{X,O,O,O,O,O,O,X},
{X,X,X,X,X,X,X,X},
};
srcGlyph.setImage(&alloc, imageData);
SkBinaryWriteBuffer writeBuffer;
srcGlyph.flattenMetrics(writeBuffer);
srcGlyph.flattenImage(writeBuffer);
sk_sp<SkData> data = writeBuffer.snapshotAsData();
SkReadBuffer readBuffer{data->data(), data->size()};
std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
dstGlyph->addImageFromBuffer(readBuffer, &alloc);
REPORTER_ASSERT(reporter, readBuffer.isValid());
uint8_t* dstImage = (uint8_t*)dstGlyph->image();
for (int y = 0; y < dstGlyph->height(); ++y) {
for (int x = 0; x < dstGlyph->width(); ++x) {
REPORTER_ASSERT(reporter, imageData[y][x] == dstImage[y * dstGlyph->rowBytes() + x]);
}
}
// Add good metrics, but mess up image data
SkBinaryWriteBuffer badWriteBuffer;
srcGlyph.flattenMetrics(badWriteBuffer);
badWriteBuffer.writeInt(7);
badWriteBuffer.writeInt(8);
data = badWriteBuffer.snapshotAsData();
SkReadBuffer badReadBuffer{data->data(), data->size()};
dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
REPORTER_ASSERT(reporter, badReadBuffer.isValid()); // Reading glyph metrics is okay.
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
dstGlyph->addImageFromBuffer(badReadBuffer, &alloc);
REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
REPORTER_ASSERT(reporter, !dstGlyph->setImageHasBeenCalled());
}
DEF_TEST(SkGlyph_SendWithPath, reporter) {
SkArenaAlloc alloc{256};
SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
SkGlyphTestPeer::SetGlyph1(&srcGlyph);
SkPath srcPath;
srcPath.addRect(srcGlyph.rect());
srcGlyph.setPath(&alloc, &srcPath, false);
SkBinaryWriteBuffer writeBuffer;
srcGlyph.flattenMetrics(writeBuffer);
srcGlyph.flattenPath(writeBuffer);
sk_sp<SkData> data = writeBuffer.snapshotAsData();
SkReadBuffer readBuffer{data->data(), data->size()};
std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
dstGlyph->addPathFromBuffer(readBuffer, &alloc);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstGlyph->setPathHasBeenCalled());
const SkPath* dstPath = dstGlyph->path();
REPORTER_ASSERT(reporter, *dstPath == srcPath);
{
// Add good metrics, but mess up path data
SkBinaryWriteBuffer badWriteBuffer;
srcGlyph.flattenMetrics(badWriteBuffer);
// Force a false value to be read in addPathFromBuffer for hasPath.
badWriteBuffer.writeInt(8);
badWriteBuffer.writeInt(9);
data = badWriteBuffer.snapshotAsData();
SkReadBuffer badReadBuffer{data->data(), data->size()};
dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
dstGlyph->addPathFromBuffer(badReadBuffer, &alloc);
REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
REPORTER_ASSERT(reporter, !dstGlyph->setPathHasBeenCalled());
}
{
// Add good metrics, but no path data.
SkBinaryWriteBuffer badWriteBuffer;
srcGlyph.flattenMetrics(badWriteBuffer);
data = badWriteBuffer.snapshotAsData();
SkReadBuffer badReadBuffer{data->data(), data->size()};
dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
REPORTER_ASSERT(reporter, badReadBuffer.isValid()); // Reading glyph metrics is okay.
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
dstGlyph->addPathFromBuffer(badReadBuffer, &alloc);
REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
REPORTER_ASSERT(reporter, !dstGlyph->setPathHasBeenCalled());
}
}
DEF_TEST(SkGlyph_SendWithDrawable, reporter) {
SkArenaAlloc alloc{256};
SkGlyph srcGlyph{SkPackedGlyphID{(SkGlyphID)12}};
SkGlyphTestPeer::SetGlyph1(&srcGlyph);
class TestDrawable final : public SkDrawable {
public:
TestDrawable(SkRect rect) : fRect(rect) {}
private:
const SkRect fRect;
SkRect onGetBounds() override { return fRect; }
size_t onApproximateBytesUsed() override {
return 0;
}
void onDraw(SkCanvas* canvas) override {
SkPaint paint;
canvas->drawRect(fRect, paint);
}
};
sk_sp<SkDrawable> srcDrawable = sk_make_sp<TestDrawable>(srcGlyph.rect());
srcGlyph.setDrawable(&alloc, srcDrawable);
REPORTER_ASSERT(reporter, srcGlyph.setDrawableHasBeenCalled());
SkBinaryWriteBuffer writeBuffer;
srcGlyph.flattenMetrics(writeBuffer);
srcGlyph.flattenDrawable(writeBuffer);
sk_sp<SkData> data = writeBuffer.snapshotAsData();
SkReadBuffer readBuffer{data->data(), data->size()};
std::optional<SkGlyph> dstGlyph = SkGlyph::MakeFromBuffer(readBuffer);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
dstGlyph->addDrawableFromBuffer(readBuffer, &alloc);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstGlyph->setDrawableHasBeenCalled());
SkDrawable* dstDrawable = dstGlyph->drawable();
REPORTER_ASSERT(reporter, dstDrawable->getBounds() == srcDrawable->getBounds());
// Add good metrics, but mess up drawable data
SkBinaryWriteBuffer badWriteBuffer;
srcGlyph.flattenMetrics(badWriteBuffer);
badWriteBuffer.writeInt(7);
badWriteBuffer.writeInt(8);
data = badWriteBuffer.snapshotAsData();
SkReadBuffer badReadBuffer{data->data(), data->size()};
dstGlyph = SkGlyph::MakeFromBuffer(badReadBuffer);
REPORTER_ASSERT(reporter, badReadBuffer.isValid()); // Reading glyph metrics is okay.
REPORTER_ASSERT(reporter, dstGlyph.has_value());
REPORTER_ASSERT(reporter, srcGlyph.advanceVector() == dstGlyph->advanceVector());
REPORTER_ASSERT(reporter, srcGlyph.rect() == dstGlyph->rect());
REPORTER_ASSERT(reporter, srcGlyph.maskFormat() == dstGlyph->maskFormat());
dstGlyph->addDrawableFromBuffer(badReadBuffer, &alloc);
REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
REPORTER_ASSERT(reporter, !dstGlyph->setDrawableHasBeenCalled());
}
DEF_TEST(SkPictureBackedGlyphDrawable_Basic, reporter) {
class TestDrawable final : public SkDrawable {
public:
TestDrawable(SkRect rect) : fRect(rect) {}
private:
const SkRect fRect;
SkRect onGetBounds() override { return fRect; }
size_t onApproximateBytesUsed() override {
return 0;
}
void onDraw(SkCanvas* canvas) override {
SkPaint paint;
canvas->drawRect(fRect, paint);
}
};
sk_sp<SkDrawable> srcDrawable = sk_make_sp<TestDrawable>(SkRect::MakeWH(10, 20));
SkBinaryWriteBuffer writeBuffer;
SkPictureBackedGlyphDrawable::FlattenDrawable(writeBuffer, srcDrawable.get());
sk_sp<SkData> data = writeBuffer.snapshotAsData();
SkReadBuffer readBuffer{data->data(), data->size()};
sk_sp<SkPictureBackedGlyphDrawable> dstDrawable =
SkPictureBackedGlyphDrawable::MakeFromBuffer(readBuffer);
REPORTER_ASSERT(reporter, readBuffer.isValid());
REPORTER_ASSERT(reporter, dstDrawable != nullptr);
REPORTER_ASSERT(reporter, srcDrawable->getBounds() == dstDrawable->getBounds());
SkBinaryWriteBuffer badWriteBuffer;
badWriteBuffer.writeInt(7);
badWriteBuffer.writeInt(8);
data = badWriteBuffer.snapshotAsData();
SkReadBuffer badReadBuffer{data->data(), data->size()};
sk_sp<SkPictureBackedGlyphDrawable> badDrawable =
SkPictureBackedGlyphDrawable::MakeFromBuffer(badReadBuffer);
REPORTER_ASSERT(reporter, badDrawable == nullptr);
REPORTER_ASSERT(reporter, !badReadBuffer.isValid());
}
static sk_sp<SkDrawable> make_sksl_drawable() {
SkRect rect = SkRect::MakeWH(50, 50);
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(rect);
const sk_sp<SkRuntimeEffect> effect =
SkRuntimeEffect::MakeForShader(
SkString("half4 main(float2 xy) { return half4(0, 1, 0, 1); }"))
.effect;
SkASSERT(effect);
SkPaint paint;
paint.setShader(effect->makeShader(/*uniforms=*/nullptr, /*children=*/{}));
// See note in make_nested_sksl_drawable: We include enough ops that this drawable will be
// preserved as a sub-picture when we wrap it in a second layer.
for (int i = 0; i < kMaxPictureOpsToUnrollInsteadOfRef + 1; ++i) {
canvas->drawRect(rect, paint);
}
return recorder.finishRecordingAsDrawable();
}
static sk_sp<SkDrawable> make_nested_sksl_drawable() {
SkRect rect = SkRect::MakeWH(50, 50);
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(rect);
auto sksl_drawable = make_sksl_drawable();
sk_sp<SkPicture> sksl_picture = sksl_drawable->makePictureSnapshot();
// We need to ensure that the op count of our picture is larger than this threshold, so we
// actually get a nested (embedded) picture, rather than just playing the ops back.
SkASSERT(sksl_picture->approximateOpCount() > kMaxPictureOpsToUnrollInsteadOfRef);
canvas->drawPicture(sksl_picture);
return recorder.finishRecordingAsDrawable();
}
DEF_TEST(SkPictureBackedGlyphDrawable_SkSL, reporter) {
for (const sk_sp<SkDrawable>& drawable : {make_sksl_drawable(), make_nested_sksl_drawable()}) {
for (bool allowSkSL : {true, false}) {
REPORTER_ASSERT(reporter, drawable);
SkBinaryWriteBuffer writeBuffer;
SkPictureBackedGlyphDrawable::FlattenDrawable(writeBuffer, drawable.get());
sk_sp<SkData> data = writeBuffer.snapshotAsData();
SkReadBuffer readBuffer{data->data(), data->size()};
readBuffer.setAllowSkSL(allowSkSL);
sk_sp<SkPictureBackedGlyphDrawable> dstDrawable =
SkPictureBackedGlyphDrawable::MakeFromBuffer(readBuffer);
REPORTER_ASSERT(reporter, readBuffer.isValid() == allowSkSL);
REPORTER_ASSERT(reporter, !!dstDrawable == allowSkSL);
}
}
}