|  | /* | 
|  | * 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, rejects.rejectedMaxDimension() == 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, rejects.rejectedMaxDimension() == 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, rejects.rejectedMaxDimension() == 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]); | 
|  | } | 
|  | } | 
|  | } |