| /* | |
| * 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 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, kX_SkAxisAlignment}; | |
| accepted.startBitmapDevice(source, {100, 100}, matrix, rounding); | |
| for (auto [i, packedID, pos] : SkMakeEnumerate(accepted.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 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]); | |
| } | |
| } | |
| } |