skia2/tests/SkGlyphBufferTest.cpp
Herb Derby 38f104e85d combine direct position calculation
Make the glyph buffers use the same Direct position calculations
for Bitmap and GPU processing. This will create fewer changes when
switching to SkGlyphDigest based API.

Bug: skia:13192

Change-Id: I7e3e44dcfc1a4bad014d0ebe2bef3c1b28c712f4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/529277
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Herb Derby <herb@google.com>
2022-04-11 20:37:07 +00:00

200 lines
8.2 KiB
C++

/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/core/SkEnumerate.h"
#include "src/core/SkGlyphBuffer.h"
#include "src/core/SkGlyphRunPainter.h"
#include "src/core/SkScalerContext.h"
#include "tests/Test.h"
DEF_TEST(SkPackedGlyphIDCtor, reporter) {
using PG = SkPackedGlyphID;
// x and y are in one quarter the sub-pixel sampling frequency.
// Number of steps on the interval [0, 1)
const int perUnit = 1u << (PG::kSubPixelPosLen + 2);
const float step = 1.f / perUnit;
const int testLimit = 2 * perUnit;
auto freqRound = [](uint32_t x) -> uint32_t {
return ((x + 2) >> 2) & PG::kSubPixelPosMask;
};
{
// Normal sub-pixel with y-axis snapping.
auto roundingSpec = SkGlyphPositionRoundingSpec(true, SkAxisAlignment::kX);
SkIPoint mask = roundingSpec.ignorePositionFieldMask;
for (int x = -testLimit; x < testLimit; x++) {
float fx = x * step;
SkPoint roundedPos = SkPoint{fx, 0} + roundingSpec.halfAxisSampleFreq;
SkPackedGlyphID packedID{3, roundedPos, mask};
uint32_t subX = freqRound(x);
uint32_t subY = 0;
SkPackedGlyphID correctID(3, subX, subY);
SkASSERT(packedID == correctID);
REPORTER_ASSERT(reporter, packedID == correctID);
}
}
{
// No subpixel positioning.
auto roundingSpec = SkGlyphPositionRoundingSpec(false, SkAxisAlignment::kNone);
SkIPoint mask = roundingSpec.ignorePositionFieldMask;
for (int y = -testLimit; y < testLimit; y++) {
for (int x = -testLimit; x < testLimit; x++) {
float fx = x * step, fy = y * step;
SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
SkPackedGlyphID packedID{3, roundedPos, mask};
uint32_t subX = 0;
uint32_t subY = 0;
SkPackedGlyphID correctID(3, subX, subY);
REPORTER_ASSERT(reporter, packedID == correctID);
}
}
}
{
// Subpixel with no axis snapping.
auto roundingSpec = SkGlyphPositionRoundingSpec(true, SkAxisAlignment::kNone);
SkIPoint mask = roundingSpec.ignorePositionFieldMask;
for (int y = -testLimit; y < testLimit; y++) {
for (int x = -testLimit; x < testLimit; x++) {
float fx = x * step, fy = y * step;
SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
SkPackedGlyphID packedID{3, roundedPos, mask};
uint32_t subX = freqRound(x);
uint32_t subY = freqRound(y);
SkPackedGlyphID correctID(3, subX, subY);
REPORTER_ASSERT(reporter, packedID == correctID);
}
}
}
{
// Test dynamic range by transposing a large distance.
// Floating point numbers have 24 bits of precision. The largest distance is 24 - 2 (for
// sub-pixel) - 1 (for truncation to floor trick in the code). This leaves 21 bits. Large
// Distance is 2^21 - 2 (because the test is on the interval [-2, 2).
const uint32_t kLogLargeDistance = 24 - PG::kSubPixelPosLen - 1;
const int64_t kLargeDistance = (1ull << kLogLargeDistance) - 2;
auto roundingSpec = SkGlyphPositionRoundingSpec(true, SkAxisAlignment::kNone);
SkIPoint mask = roundingSpec.ignorePositionFieldMask;
for (int y = -32; y < 33; y++) {
for (int x = -32; x < 33; x++) {
float fx = x * step + kLargeDistance, fy = y * step + kLargeDistance;
SkPoint roundedPos = SkPoint{fx, fy} + roundingSpec.halfAxisSampleFreq;
SkPackedGlyphID packedID{3, roundedPos, mask};
uint32_t subX = freqRound(x);
uint32_t subY = freqRound(y);
SkPackedGlyphID correctID(3, subX, subY);
REPORTER_ASSERT(reporter, packedID == correctID);
}
}
}
}
DEF_TEST(SkSourceGlyphBufferBasic, reporter) {
SkSourceGlyphBuffer rejects;
// Positions are picked to avoid precision problems.
const SkPoint positions[] = {{10.25,10.25}, {20.5,10.25}, {30.75,10.25}, {40,10.25}};
const SkGlyphID glyphIDs[] = {1, 2, 3, 4};
auto source = SkMakeZip(glyphIDs, positions);
rejects.setSource(source);
for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
}
// Reject a couple of glyphs.
rejects.reject(1);
rejects.reject(2, 100);
rejects.flipRejectsToSource();
REPORTER_ASSERT(reporter, std::get<1>(rejects.maxDimensionHint()) == 100);
for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
// This will index 1 and 2 from the original source.
size_t j = i + 1;
REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
}
// Reject an additional glyph
rejects.reject(0, 10);
rejects.flipRejectsToSource();
REPORTER_ASSERT(reporter, std::get<1>(rejects.maxDimensionHint()) == 10);
for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
// This will index 1 from the original source.
size_t j = i + 1;
REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
}
// Start all over
rejects.setSource(source);
for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[i]));
REPORTER_ASSERT(reporter, pos == std::get<1>(source[i]));
}
// Check that everything is working after calling setSource.
rejects.reject(1);
rejects.reject(2, 100);
rejects.flipRejectsToSource();
REPORTER_ASSERT(reporter, std::get<1>(rejects.maxDimensionHint()) == 100);
for (auto [i, glyphID, pos] : SkMakeEnumerate(rejects.source())) {
// This will index 1 and 2 from the original source.
size_t j = i + 1;
REPORTER_ASSERT(reporter, glyphID == std::get<0>(source[j]));
REPORTER_ASSERT(reporter, pos == std::get<1>(source[j]));
}
}
DEF_TEST(SkDrawableGlyphBufferBasic, reporter) {
// Positions are picked to avoid precision problems.
const SkPoint positions[] = {{10.25,10.25}, {20.5,10.25}, {30.75,10.25}, {40,10.25}};
const SkGlyphID glyphIDs[] = {1, 2, 3, 4};
SkGlyph glyphs[100];
auto source = SkMakeZip(glyphIDs, positions);
{
SkDrawableGlyphBuffer accepted;
accepted.ensureSize(100);
accepted.startSource(source);
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted.input())) {
REPORTER_ASSERT(reporter, packedID.packedID().glyphID() == glyphIDs[i]);
REPORTER_ASSERT(reporter, pos == positions[i]);
}
}
{
SkDrawableGlyphBuffer accepted;
accepted.ensureSize(100);
SkMatrix matrix = SkMatrix::Scale(0.5, 0.5);
SkGlyphPositionRoundingSpec rounding{true, SkAxisAlignment::kX};
SkMatrix positionMatrix{matrix};
positionMatrix.preTranslate(100, 100);
accepted.startDevicePositioning(source, positionMatrix, rounding);
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted.input())) {
REPORTER_ASSERT(reporter, glyphIDs[i] == packedID.packedID().glyphID());
REPORTER_ASSERT(reporter,
pos.x() == SkScalarFloorToInt(positions[i].x() * 0.5 + 50 +
SkPackedGlyphID::kSubpixelRound));
REPORTER_ASSERT(reporter,
pos.y() == SkScalarFloorToInt(positions[i].y() * 0.5 + 50 + 0.5));
}
}
{
SkDrawableGlyphBuffer accepted;
accepted.ensureSize(100);
accepted.startSource(source);
for (auto [i, packedID, pos] : SkMakeEnumerate(accepted.input())) {
accepted.accept(&glyphs[i], i);
}
for (auto [i, glyph, pos] : SkMakeEnumerate(accepted.accepted())) {
REPORTER_ASSERT(reporter, glyph.glyph() == &glyphs[i]);
}
}
}