diff options
-rw-r--r-- | gyp/tests.gyp | 1 | ||||
-rw-r--r-- | src/core/SkAdvancedTypefaceMetrics.cpp | 175 | ||||
-rw-r--r-- | tests/WArrayTest.cpp | 219 |
3 files changed, 361 insertions, 34 deletions
diff --git a/gyp/tests.gyp b/gyp/tests.gyp index 872f035f4f..4d35312fa2 100644 --- a/gyp/tests.gyp +++ b/gyp/tests.gyp @@ -61,6 +61,7 @@ '../tests/TestSize.cpp', '../tests/ToUnicode.cpp', '../tests/UtilsTest.cpp', + '../tests/WArrayTest.cpp', '../tests/WritePixelsTest.cpp', '../tests/Writer32Test.cpp', '../tests/XfermodeTest.cpp', diff --git a/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp index cff0989e31..e004edc667 100644 --- a/src/core/SkAdvancedTypefaceMetrics.cpp +++ b/src/core/SkAdvancedTypefaceMetrics.cpp @@ -27,6 +27,9 @@ namespace skia_advanced_typeface_metrics_utils { +const int16_t kInvalidAdvance = SK_MinS16; +const int16_t kDontCareAdvance = SK_MinS16 + 1; + template <typename Data> void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range, int startId) { @@ -51,14 +54,69 @@ void finishRange( type) { range->fEndId = endId; range->fType = type; + stripUninterestingTrailingAdvancesFromRange(range); int newLength; if (type == SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange) { - newLength = endId - range->fStartId + 1; + newLength = range->fEndId - range->fStartId + 1; } else { + if (range->fEndId == range->fStartId) { + range->fType = + SkAdvancedTypefaceMetrics::AdvanceMetric<Data>::kRange; + } newLength = 1; } SkASSERT(range->fAdvance.count() >= newLength); range->fAdvance.setCount(newLength); + zeroWildcardsInRange(range); +} + +template <typename Data> +void stripUninterestingTrailingAdvancesFromRange( + SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range) { + SkASSERT(false); +} + +template <> +void stripUninterestingTrailingAdvancesFromRange<int16_t>( + SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* range) { + SkASSERT(range); + + int expectedAdvanceCount = range->fEndId - range->fStartId + 1; + if (range->fAdvance.count() < expectedAdvanceCount) { + return; + } + + for (int i = expectedAdvanceCount - 1; i >= 0; --i) { + if (range->fAdvance[i] != kDontCareAdvance && + range->fAdvance[i] != kInvalidAdvance && + range->fAdvance[i] != 0) { + range->fEndId = range->fStartId + i; + break; + } + } +} + +template <typename Data> +void zeroWildcardsInRange( + SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range) { + SkASSERT(false); +} + +template <> +void zeroWildcardsInRange<int16_t>( + SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* range) { + SkASSERT(range); + if (range->fType != SkAdvancedTypefaceMetrics::WidthRange::kRange) { + return; + } + SkASSERT(range->fAdvance.count() == range->fEndId - range->fStartId + 1); + + // Zero out wildcards. + for (int i = 0; i < range->fAdvance.count(); ++i) { + if (range->fAdvance[i] == kDontCareAdvance) { + range->fAdvance[i] = 0; + } + } } template <typename Data, typename FontHandle> @@ -68,75 +126,124 @@ SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData( const uint32_t* subsetGlyphIDs, uint32_t subsetGlyphIDsLength, bool (*getAdvance)(FontHandle fontHandle, int gId, Data* data)) { - // Assuming that an ASCII representation of a width or a glyph id is, - // on average, 3 characters long gives the following cut offs for - // using different range types: - // When currently in a range - // - Removing 4 0's is a win - // - Removing 5 repeats is a win - // When not currently in a range - // - Removing 1 0 is a win - // - Removing 3 repeats is a win + // Assuming that on average, the ASCII representation of an advance plus + // a space is 8 characters and the ASCII representation of a glyph id is 3 + // characters, then the following cut offs for using different range types + // apply: + // The cost of stopping and starting the range is 7 characers + // a. Removing 4 0's or don't care's is a win + // The cost of stopping and starting the range plus a run is 22 + // characters + // b. Removing 3 repeating advances is a win + // c. Removing 2 repeating advances and 3 don't cares is a win + // When not currently in a range the cost of a run over a range is 16 + // characaters, so: + // d. Removing a leading 0/don't cares is a win because it is omitted + // e. Removing 2 repeating advances is a win SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<Data> > result; SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* curRange; SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* prevRange = NULL; - curRange = appendRange(&result, 0); - Data lastAdvance = SK_MinS16; - int repeats = 0; + Data lastAdvance = kInvalidAdvance; + int repeatedAdvances = 0; + int wildCardsInRun = 0; + int leadingWildCards = 0; + int trailingWildCards = 0; uint32_t subsetIndex = 0; - for (int gId = 0; gId <= num_glyphs; gId++) { - Data advance = 0; - if (gId < num_glyphs) { + + // Limit the loop count to glyph id ranges provided. + int firstIndex = 0; + int lastIndex = num_glyphs; + if (subsetGlyphIDs) { + firstIndex = static_cast<int>(subsetGlyphIDs[0]); + lastIndex = + static_cast<int>(subsetGlyphIDs[subsetGlyphIDsLength - 1]) + 1; + } + curRange = appendRange(&result, firstIndex); + + for (int gId = firstIndex; gId <= lastIndex; gId++) { + Data advance = kInvalidAdvance; + if (gId < lastIndex) { // Get glyph id only when subset is NULL, or the id is in subset. if (!subsetGlyphIDs || (subsetIndex < subsetGlyphIDsLength && - static_cast<uint32_t>(gId) == subsetGlyphIDs[subsetIndex])) { + static_cast<uint32_t>(gId) == subsetGlyphIDs[subsetIndex])) { SkAssertResult(getAdvance(fontHandle, gId, &advance)); ++subsetIndex; + } else { + advance = kDontCareAdvance; } - } else { - advance = SK_MinS16; } if (advance == lastAdvance) { - repeats++; - } else if (curRange->fAdvance.count() == repeats + 1) { - if (lastAdvance == 0 && repeats >= 0) { + repeatedAdvances++; + trailingWildCards = 0; + } else if (advance == kDontCareAdvance) { + wildCardsInRun++; + trailingWildCards++; + } else if (curRange->fAdvance.count() == + repeatedAdvances + 1 + wildCardsInRun) { // All in run. + if (lastAdvance == 0) { resetRange(curRange, gId); - } else if (repeats >= 2) { + trailingWildCards = 0; + } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) { finishRange(curRange, gId - 1, SkAdvancedTypefaceMetrics::WidthRange::kRun); prevRange = curRange; curRange = appendRange(&curRange->fNext, gId); + trailingWildCards = 0; } - repeats = 0; + repeatedAdvances = 0; + wildCardsInRun = trailingWildCards; + leadingWildCards = trailingWildCards; + trailingWildCards = 0; } else { - if (lastAdvance == 0 && repeats >= 3) { - finishRange(curRange, gId - repeats - 2, + if (lastAdvance == 0 && + repeatedAdvances + 1 + wildCardsInRun >= 4) { + finishRange(curRange, + gId - repeatedAdvances - wildCardsInRun - 2, SkAdvancedTypefaceMetrics::WidthRange::kRange); prevRange = curRange; curRange = appendRange(&curRange->fNext, gId); - } else if (repeats >= 4) { - finishRange(curRange, gId - repeats - 2, + trailingWildCards = 0; + } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) { + finishRange(curRange, + gId - trailingWildCards - 1, SkAdvancedTypefaceMetrics::WidthRange::kRange); - curRange = appendRange(&curRange->fNext, gId - repeats - 1); + prevRange = curRange; + curRange = appendRange(&curRange->fNext, gId); + trailingWildCards = 0; + } else if (lastAdvance != 0 && + (repeatedAdvances + 1 >= 3 || + (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) { + finishRange(curRange, + gId - repeatedAdvances - wildCardsInRun - 2, + SkAdvancedTypefaceMetrics::WidthRange::kRange); + curRange = + appendRange(&curRange->fNext, + gId - repeatedAdvances - wildCardsInRun - 1); curRange->fAdvance.append(1, &lastAdvance); finishRange(curRange, gId - 1, SkAdvancedTypefaceMetrics::WidthRange::kRun); prevRange = curRange; curRange = appendRange(&curRange->fNext, gId); + trailingWildCards = 0; } - repeats = 0; + repeatedAdvances = 0; + wildCardsInRun = trailingWildCards; + leadingWildCards = trailingWildCards; + trailingWildCards = 0; } curRange->fAdvance.append(1, &advance); - lastAdvance = advance; + if (advance != kDontCareAdvance) { + lastAdvance = advance; + } } - if (curRange->fStartId == num_glyphs) { + if (curRange->fStartId == lastIndex) { SkASSERT(prevRange); - SkASSERT(prevRange->fNext->fStartId == num_glyphs); + SkASSERT(prevRange->fNext->fStartId == lastIndex); prevRange->fNext.reset(); } else { - finishRange(curRange, num_glyphs - 1, + finishRange(curRange, lastIndex - 1, SkAdvancedTypefaceMetrics::WidthRange::kRange); } return result.release(); diff --git a/tests/WArrayTest.cpp b/tests/WArrayTest.cpp new file mode 100644 index 0000000000..428ca5f31a --- /dev/null +++ b/tests/WArrayTest.cpp @@ -0,0 +1,219 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "Test.h" + +// Include the implementation so we can make an appropriate template instance. +#include "SkAdvancedTypefaceMetrics.cpp" + +using namespace skia_advanced_typeface_metrics_utils; + +namespace { + +// Negative values and zeros in a range plus trailing zeros. +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 +const int16_t data1[] = {-1, 0, -3, 4, 5, 6, 7, 0, 0, 0, 8, 0, 0, 0, 0}; +const char* expected1 = "0[-1 0 -3 4 5 6 7 0 0 0 8]"; + +// Run with leading and trailing zeros. +// Test rules: d 0 1 2 3 4 5 6 7 8 9 10 11 +const int16_t data2[] = {0, 0, 0, 100, 100, 100, 100, 100, 100, 100, 0, 0}; +const char* expected2 = "3 9 100"; + +// Removing 0's from a range. +// Test rules: a 0 1 2 3 4 5 6 7 8 9 10 11 +const int16_t data3[] = {1, 2, 0, 0, 0, 3, 4, 0, 0, 0, 0, 5}; +const char* expected3 = "0[1 2 0 0 0 3 4] 11[5]"; + +// Removing 0's from a run/range and between runs. +// Test rules: a, b 0 1 2 3 4 5 6 7 8 9 10 11 12 14 15 +const int16_t data4[] = {1, 0, 0, 0, 1, 2, 2, 2, 3, 0, 0, 0, 0, 3, 4}; +const char* expected4 = "0[1 0 0 0 1] 5 7 2 8[3] 13[3 4]"; + +// Runs that starts outside a range. +// Test rules: a, e 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 +const int16_t data5[] = {1, 1, 2, 3, 0, 0, 0, 0, 5, 5, 6, 7, 0, 0, 0, 0, 8, 0}; +const char* expected5 = "0 1 1 2[2 3] 8 9 5 10[6 7] 16[8]"; + +// Zeros and runs that should be broken out. +// Test rules: a, b, e 0 1 2 3 4 5 6 7 8 9 10 11 12 13 +const int16_t data6[] = {1, 0, 0, 0, 0, 1, 2, 3, 3, 4, 5, 5, 5, 6}; +const char* expected6 = "0[1] 5[1 2 3 3 4] 10 12 5 13[6]"; + +// Don't cares that aren't enough to break out a run. +// Test rules: c 0 1 2 3 4 5 +const int16_t data7[] = {1, 2, 10, 11, 2, 3}; +const char* expected7 = "0[1 2 10 11 2 3]"; +const uint32_t subset7[] = {0, 1, 4, 5}; +const char* expectedSubset7 = "0[1 2 0 0 2 3]"; + +// Don't cares that are enough to break out a run. +// Test rules: c 0 1 2 3 4 5 6 +const int16_t data8[] = {1, 2, 10, 11, 12, 2, 3}; +const char* expected8 = "0[1 2 10 11 12 2 3]"; +const uint32_t subset8[] = {0, 1, 5, 6}; +const char* expectedSubset8 = "0[1] 1 5 2 6[3]"; + +// Leading don't cares. +// Test rules: d 0 1 2 3 4 +const int16_t data9[] = {1, 1, 10, 2, 3}; +const char* expected9 = "0 1 1 2[10 2 3]"; +const uint32_t subset9[] = {0, 1, 3, 4}; +const char* expectedSubset9 = "0 1 1 3[2 3]"; + +// Almost run of don't cares inside a range. +// Test rules: c 0 1 2 3 4 5 +const int16_t data10[] = {1, 2, 10, 11, 12, 3}; +const char* expected10 = "0[1 2 10 11 12 3]"; +const uint32_t subset10[] = {0, 1, 5}; +const char* expectedSubset10 = "0[1 2 0 0 0 3]"; + +// Run of don't cares inside a range. +// Test rules: c 0 1 2 3 4 5 6 +const int16_t data11[] = {1, 2, 10, 11, 12, 13, 3}; +const char* expected11 = "0[1 2 10 11 12 13 3]"; +const uint32_t subset11[] = {0, 1, 6}; +const char* expectedSubset11 = "0[1 2] 6[3]"; + +// Almost run within a range with leading don't cares. +// Test rules: c 0 1 2 3 4 5 6 +const int16_t data12[] = {1, 10, 11, 2, 12, 13, 3}; +const char* expected12 = "0[1 10 11 2 12 13 3]"; +const uint32_t subset12[] = {0, 3, 6}; +const char* expectedSubset12 = "0[1 0 0 2 0 0 3]"; + +// Run within a range with leading don't cares. +// Test rules: c 0 1 2 3 4 5 6 7 +const int16_t data13[] = {1, 10, 11, 2, 2, 12, 13, 3}; +const char* expected13 = "0[1 10 11 2 2 12 13 3]"; +const uint32_t subset13[] = {0, 3, 4, 7}; +const char* expectedSubset13 = "0[1] 1 6 2 7[3]"; + +// Enough don't cares to breakup something. +// Test rules: a 0 1 2 3 4 5 +const int16_t data14[] = {1, 0, 0, 0, 0, 2}; +const char* expected14 = "0[1] 5[2]"; +const uint32_t subset14[] = {0, 5}; +const char* expectedSubset14 = "0[1] 5[2]"; + +} + +static SkString stringify_advance_data( + SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* data) { + SkString result; + bool leadingSpace = false; + while (data != NULL) { + if (leadingSpace) { + result.appendf(" "); + } else { + leadingSpace = true; + } + switch(data->fType) { + case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRun: + result.appendf("%d %d %d", data->fStartId, data->fEndId, + data->fAdvance[0]); + break; + case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRange: + result.appendf("%d[", data->fStartId); + for (int i = 0; i < data->fAdvance.count(); ++i) { + if (i > 0) { + result.appendf(" "); + } + result.appendf("%d", data->fAdvance[i]); + } + result.appendf("]"); + break; + case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kDefault: + result.appendf("<Default=%d>", data->fAdvance[0]); + break; + } + data = data->fNext.get(); + } + return result; +} + +class TestWData { + public: + TestWData(skiatest::Reporter* reporter, + const int16_t advances[], + int advanceLen, + const uint32_t subset[], + int subsetLen, + const char* expected) + : fAdvances(advances) + , fAdvancesLen(advanceLen) + , fSubset(subset) + , fSubsetLen(subsetLen) + , fExpected(expected) { + REPORTER_ASSERT(reporter, RunTest()); + } + + private: + const int16_t* fAdvances; + const int fAdvancesLen; + const uint32_t* fSubset; + const int fSubsetLen; + const char* fExpected; + + static bool getAdvance(TestWData* testCase, int gId, int16_t* advance) { + if (gId >= 0 && gId < testCase->fAdvancesLen) { + *advance = testCase->fAdvances[gId]; + return true; + } + return false; + } + + bool RunTest() { + SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t> > result; + result.reset(getAdvanceData(this, fAdvancesLen, fSubset, fSubsetLen, + getAdvance)); + + SkString stringResult = stringify_advance_data(result.get()); + if (!stringResult.equals(fExpected)) { + printf("Expected: %s\n Result: %s\n", fExpected, + stringResult.c_str()); + return false; + } + return true; + } +}; + +static void TestWArray(skiatest::Reporter* reporter) { + TestWData(reporter, data1, SK_ARRAY_COUNT(data1), NULL, 0, expected1); + TestWData(reporter, data2, SK_ARRAY_COUNT(data2), NULL, 0, expected2); + TestWData(reporter, data3, SK_ARRAY_COUNT(data3), NULL, 0, expected3); + TestWData(reporter, data4, SK_ARRAY_COUNT(data4), NULL, 0, expected4); + TestWData(reporter, data5, SK_ARRAY_COUNT(data5), NULL, 0, expected5); + TestWData(reporter, data6, SK_ARRAY_COUNT(data6), NULL, 0, expected6); + TestWData(reporter, data7, SK_ARRAY_COUNT(data7), NULL, 0, expected7); + TestWData(reporter, data7, SK_ARRAY_COUNT(data7), subset7, + SK_ARRAY_COUNT(subset7), expectedSubset7); + TestWData(reporter, data8, SK_ARRAY_COUNT(data8), NULL, 0, expected8); + TestWData(reporter, data8, SK_ARRAY_COUNT(data8), subset8, + SK_ARRAY_COUNT(subset8), expectedSubset8); + TestWData(reporter, data9, SK_ARRAY_COUNT(data9), NULL, 0, expected9); + TestWData(reporter, data9, SK_ARRAY_COUNT(data9), subset9, + SK_ARRAY_COUNT(subset9), expectedSubset9); + TestWData(reporter, data10, SK_ARRAY_COUNT(data10), NULL, 0, expected10); + TestWData(reporter, data10, SK_ARRAY_COUNT(data10), subset10, + SK_ARRAY_COUNT(subset10), expectedSubset10); + TestWData(reporter, data11, SK_ARRAY_COUNT(data11), NULL, 0, expected11); + TestWData(reporter, data11, SK_ARRAY_COUNT(data11), subset11, + SK_ARRAY_COUNT(subset11), expectedSubset11); + TestWData(reporter, data12, SK_ARRAY_COUNT(data12), NULL, 0, expected12); + TestWData(reporter, data12, SK_ARRAY_COUNT(data12), subset12, + SK_ARRAY_COUNT(subset12), expectedSubset12); + TestWData(reporter, data13, SK_ARRAY_COUNT(data13), NULL, 0, expected13); + TestWData(reporter, data13, SK_ARRAY_COUNT(data13), subset13, + SK_ARRAY_COUNT(subset13), expectedSubset13); + TestWData(reporter, data14, SK_ARRAY_COUNT(data14), NULL, 0, expected14); + TestWData(reporter, data14, SK_ARRAY_COUNT(data14), subset14, + SK_ARRAY_COUNT(subset14), expectedSubset14); +} + +#include "TestClassDef.h" +DEFINE_TESTCLASS("WArray", WArrayTest, TestWArray) |