skia2/tests/SkGlyphBufferTest.cpp
Herb Derby 30fdea3d8f track glyph scaling hints in min/max pairs
Track the scaling hints as the minimum of the maximum dimension and
the maximum of the maximum dimension. Having the range will allow
for better scaling choices in the drawing of last resort.

The next CL will start using this range to calculate scaling factors.

Bug: chromium:1280180

Change-Id: I2c07303d3303c71ede2a0b1133ee2e218f7bd84e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/493456
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Herb Derby <herb@google.com>
2022-01-11 17:45:21 +00:00

196 lines
8.0 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, kX_SkAxisAlignment);
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, kNone_SkAxisAlignment);
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, kNone_SkAxisAlignment);
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, kNone_SkAxisAlignment);
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 drawable;
drawable.ensureSize(100);
drawable.startSource(source);
for (auto [i, packedID, pos] : SkMakeEnumerate(drawable.input())) {
REPORTER_ASSERT(reporter, packedID.packedID().glyphID() == glyphIDs[i]);
REPORTER_ASSERT(reporter, pos == positions[i]);
}
}
{
SkDrawableGlyphBuffer drawable;
drawable.ensureSize(100);
SkMatrix matrix = SkMatrix::Scale(0.5, 0.5);
SkGlyphPositionRoundingSpec rounding{true, kX_SkAxisAlignment};
drawable.startBitmapDevice(source, {100, 100}, matrix, rounding);
for (auto [i, packedID, pos] : SkMakeEnumerate(drawable.input())) {
REPORTER_ASSERT(reporter, glyphIDs[i] == packedID.packedID().glyphID());
REPORTER_ASSERT(reporter,
pos.x() == positions[i].x() * 0.5 + 50 + SkPackedGlyphID::kSubpixelRound);
REPORTER_ASSERT(reporter, pos.y() == positions[i].y() * 0.5 + 50 + 0.5);
}
}
{
SkDrawableGlyphBuffer drawable;
drawable.ensureSize(100);
drawable.startSource(source);
for (auto [i, packedID, pos] : SkMakeEnumerate(drawable.input())) {
drawable.push_back(&glyphs[i], i);
}
for (auto [i, glyph, pos] : SkMakeEnumerate(drawable.drawable())) {
REPORTER_ASSERT(reporter, glyph.glyph() == &glyphs[i]);
}
}
}