aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
diff options
context:
space:
mode:
authorGravatar halcanary <halcanary@google.com>2016-08-17 07:57:27 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-08-17 07:57:27 -0700
commit86b6eabeae80c3db69226cd1693d7f1c4591d88e (patch)
treef1e01ca62f38780e7462ae6c801ea3df17898ec4 /src/pdf/SkPDFMakeCIDGlyphWidthsArray.cpp
parent93f366d675aa39197d6754c47979879baf379597 (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.cpp262
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;
+}