diff options
author | halcanary <halcanary@google.com> | 2016-08-17 07:57:27 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-08-17 07:57:27 -0700 |
commit | 86b6eabeae80c3db69226cd1693d7f1c4591d88e (patch) | |
tree | f1e01ca62f38780e7462ae6c801ea3df17898ec4 /src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp | |
parent | 93f366d675aa39197d6754c47979879baf379597 (diff) |
SkPDF: pull out SkPDFMakeCIDGlyphWidthsArray.cpp
Extract from inside SkPDFFont.cpp
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2251803002
Review-Url: https://codereview.chromium.org/2251803002
Diffstat (limited to 'src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp')
-rw-r--r-- | src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp new file mode 100644 index 0000000000..e1bb0d2785 --- /dev/null +++ b/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp @@ -0,0 +1,262 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkBitSet.h" +#include "SkPDFMakeCIDGlyphWidthsArray.h" +#include "SkPaint.h" +#include "SkGlyphCache.h" + +// TODO(halcanary): Write unit tests for SkPDFMakeCIDGlyphWidthsArray(). + +// TODO(halcanary): The logic in this file originated in several +// disparate places. I feel sure that someone could simplify this +// down to a single easy-to-read function. + +namespace { + +struct AdvanceMetric { + enum MetricType { + kDefault, // Default advance: fAdvance.count = 1 + kRange, // Advances for a range: fAdvance.count = fEndID-fStartID + kRun // fStartID-fEndID have same advance: fAdvance.count = 1 + }; + MetricType fType; + uint16_t fStartId; + uint16_t fEndId; + SkTDArray<int16_t> fAdvance; + AdvanceMetric(uint16_t startId) : fStartId(startId) {} + AdvanceMetric(AdvanceMetric&&) = default; + AdvanceMetric& operator=(AdvanceMetric&& other) = default; + AdvanceMetric(const AdvanceMetric&) = delete; + AdvanceMetric& operator=(const AdvanceMetric&) = delete; +}; +const int16_t kInvalidAdvance = SK_MinS16; +const int16_t kDontCareAdvance = SK_MinS16 + 1; +} // namespace + +// scale from em-units to base-1000, returning as a SkScalar +static SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { + if (emSize == 1000) { + return scaled; + } else { + return SkScalarMulDiv(scaled, 1000, emSize); + } +} + +static SkScalar scale_from_font_units(int16_t val, uint16_t emSize) { + return from_font_units(SkIntToScalar(val), emSize); +} + +static void strip_uninteresting_trailing_advances_from_range( + AdvanceMetric* 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; + } + } +} + +static void zero_wildcards_in_range(AdvanceMetric* range) { + SkASSERT(range); + if (range->fType != AdvanceMetric::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; + } + } +} + +static void finish_range( + AdvanceMetric* range, + int endId, + AdvanceMetric::MetricType type) { + range->fEndId = endId; + range->fType = type; + strip_uninteresting_trailing_advances_from_range(range); + int newLength; + if (type == AdvanceMetric::kRange) { + newLength = range->fEndId - range->fStartId + 1; + } else { + if (range->fEndId == range->fStartId) { + range->fType = AdvanceMetric::kRange; + } + newLength = 1; + } + SkASSERT(range->fAdvance.count() >= newLength); + range->fAdvance.setCount(newLength); + zero_wildcards_in_range(range); +} + +static void compose_advance_data(const AdvanceMetric& range, + uint16_t emSize, + int16_t* defaultAdvance, + SkPDFArray* result) { + switch (range.fType) { + case AdvanceMetric::kDefault: { + SkASSERT(range.fAdvance.count() == 1); + *defaultAdvance = range.fAdvance[0]; + break; + } + case AdvanceMetric::kRange: { + auto advanceArray = sk_make_sp<SkPDFArray>(); + for (int j = 0; j < range.fAdvance.count(); j++) + advanceArray->appendScalar( + scale_from_font_units(range.fAdvance[j], emSize)); + result->appendInt(range.fStartId); + result->appendObject(std::move(advanceArray)); + break; + } + case AdvanceMetric::kRun: { + SkASSERT(range.fAdvance.count() == 1); + result->appendInt(range.fStartId); + result->appendInt(range.fEndId); + result->appendScalar( + scale_from_font_units(range.fAdvance[0], emSize)); + break; + } + } +} + +/** Retrieve advance data for glyphs. Used by the PDF backend. */ +// TODO(halcanary): this function is complex enough to need its logic +// tested with unit tests. +sk_sp<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(SkGlyphCache* cache, + const SkBitSet* subset, + uint16_t emSize, + int16_t* defaultAdvance) { + // 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 + + auto result = sk_make_sp<SkPDFArray>(); + int num_glyphs = SkToInt(cache->getGlyphCount()); + + bool prevRange = false; + + int16_t lastAdvance = kInvalidAdvance; + int repeatedAdvances = 0; + int wildCardsInRun = 0; + int trailingWildCards = 0; + + // Limit the loop count to glyph id ranges provided. + int lastIndex = num_glyphs; + if (subset) { + while (!subset->isBitSet(lastIndex - 1) && lastIndex > 0) { + --lastIndex; + } + } + AdvanceMetric curRange(0); + + for (int gId = 0; gId <= lastIndex; gId++) { + int16_t advance = kInvalidAdvance; + if (gId < lastIndex) { + if (!subset || 0 == gId || subset->isBitSet(gId)) { + advance = (int16_t)cache->getGlyphIDAdvance(gId).fAdvanceX; + } else { + advance = kDontCareAdvance; + } + } + if (advance == lastAdvance) { + repeatedAdvances++; + trailingWildCards = 0; + } else if (advance == kDontCareAdvance) { + wildCardsInRun++; + trailingWildCards++; + } else if (curRange.fAdvance.count() == + repeatedAdvances + 1 + wildCardsInRun) { // All in run. + if (lastAdvance == 0) { + curRange.fStartId = gId; // reset + curRange.fAdvance.setCount(0); + trailingWildCards = 0; + } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) { + finish_range(&curRange, gId - 1, AdvanceMetric::kRun); + compose_advance_data(curRange, emSize, defaultAdvance, result.get()); + prevRange = true; + curRange = AdvanceMetric(gId); + trailingWildCards = 0; + } + repeatedAdvances = 0; + wildCardsInRun = trailingWildCards; + trailingWildCards = 0; + } else { + if (lastAdvance == 0 && + repeatedAdvances + 1 + wildCardsInRun >= 4) { + finish_range(&curRange, + gId - repeatedAdvances - wildCardsInRun - 2, + AdvanceMetric::kRange); + compose_advance_data(curRange, emSize, defaultAdvance, result.get()); + prevRange = true; + curRange = AdvanceMetric(gId); + trailingWildCards = 0; + } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) { + finish_range(&curRange, gId - trailingWildCards - 1, + AdvanceMetric::kRange); + compose_advance_data(curRange, emSize, defaultAdvance, result.get()); + prevRange = true; + curRange = AdvanceMetric(gId); + trailingWildCards = 0; + } else if (lastAdvance != 0 && + (repeatedAdvances + 1 >= 3 || + (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) { + finish_range(&curRange, + gId - repeatedAdvances - wildCardsInRun - 2, + AdvanceMetric::kRange); + compose_advance_data(curRange, emSize, defaultAdvance, result.get()); + curRange = + AdvanceMetric(gId - repeatedAdvances - wildCardsInRun - 1); + curRange.fAdvance.append(1, &lastAdvance); + finish_range(&curRange, gId - 1, AdvanceMetric::kRun); + compose_advance_data(curRange, emSize, defaultAdvance, result.get()); + prevRange = true; + curRange = AdvanceMetric(gId); + trailingWildCards = 0; + } + repeatedAdvances = 0; + wildCardsInRun = trailingWildCards; + trailingWildCards = 0; + } + curRange.fAdvance.append(1, &advance); + if (advance != kDontCareAdvance) { + lastAdvance = advance; + } + } + if (curRange.fStartId == lastIndex) { + if (!prevRange) { + return nullptr; // https://crbug.com/567031 + } + } else { + finish_range(&curRange, lastIndex - 1, AdvanceMetric::kRange); + compose_advance_data(curRange, emSize, defaultAdvance, result.get()); + } + return result; +} |