OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2016 Google Inc. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 #include "SkBitSet.h" | |
9 #include "SkPDFMakeCIDGlyphWidthsArray.h" | |
10 #include "SkSinglyLinkedList.h" | |
11 #include "SkPaint.h" | |
12 #include "SkGlyphCache.h" | |
13 | |
14 // TODO(halcanary): Write unit tests for SkPDFMakeCIDGlyphWidthsArray(). | |
15 | |
16 // TODO(halcanary): The logic in this file originated in several | |
17 // disparate places. I feel sure that someone could simplify this | |
18 // down to a single easy-to-read function. | |
19 | |
20 namespace { | |
21 | |
22 // scale from em-units to base-1000, returning as a SkScalar | |
23 static SkScalar from_font_units(SkScalar scaled, uint16_t emSize) { | |
24 if (emSize == 1000) { | |
25 return scaled; | |
26 } else { | |
27 return SkScalarMulDiv(scaled, 1000, emSize); | |
28 } | |
29 } | |
30 | |
31 static SkScalar scale_from_font_units(int16_t val, uint16_t emSize) { | |
32 return from_font_units(SkIntToScalar(val), emSize); | |
33 } | |
34 | |
35 struct AdvanceMetric { | |
36 enum MetricType { | |
37 kDefault, // Default advance: fAdvance.count = 1 | |
38 kRange, // Advances for a range: fAdvance.count = fEndID-fStartID | |
39 kRun // fStartID-fEndID have same advance: fAdvance.count = 1 | |
40 }; | |
41 MetricType fType; | |
42 uint16_t fStartId; | |
43 uint16_t fEndId; | |
44 SkTDArray<int16_t> fAdvance; | |
45 AdvanceMetric(uint16_t startId) : fStartId(startId) {} | |
46 AdvanceMetric(AdvanceMetric&&) = default; | |
47 AdvanceMetric& operator=(AdvanceMetric&& other) = default; | |
48 AdvanceMetric(const AdvanceMetric&) = delete; | |
49 AdvanceMetric& operator=(const AdvanceMetric&) = delete; | |
50 }; | |
51 | |
52 const int16_t kInvalidAdvance = SK_MinS16; | |
53 const int16_t kDontCareAdvance = SK_MinS16 + 1; | |
54 | |
55 static void strip_uninteresting_trailing_advances_from_range( | |
56 AdvanceMetric* range) { | |
57 SkASSERT(range); | |
58 | |
59 int expectedAdvanceCount = range->fEndId - range->fStartId + 1; | |
60 if (range->fAdvance.count() < expectedAdvanceCount) { | |
61 return; | |
62 } | |
63 | |
64 for (int i = expectedAdvanceCount - 1; i >= 0; --i) { | |
65 if (range->fAdvance[i] != kDontCareAdvance && | |
66 range->fAdvance[i] != kInvalidAdvance && | |
67 range->fAdvance[i] != 0) { | |
68 range->fEndId = range->fStartId + i; | |
69 break; | |
70 } | |
71 } | |
72 } | |
73 | |
74 static void zero_wildcards_in_range(AdvanceMetric* range) { | |
75 SkASSERT(range); | |
76 if (range->fType != AdvanceMetric::kRange) { | |
77 return; | |
78 } | |
79 SkASSERT(range->fAdvance.count() == range->fEndId - range->fStartId + 1); | |
80 | |
81 // Zero out wildcards. | |
82 for (int i = 0; i < range->fAdvance.count(); ++i) { | |
83 if (range->fAdvance[i] == kDontCareAdvance) { | |
84 range->fAdvance[i] = 0; | |
85 } | |
86 } | |
87 } | |
88 | |
89 static void finish_range( | |
90 AdvanceMetric* range, | |
91 int endId, | |
92 AdvanceMetric::MetricType type) { | |
93 range->fEndId = endId; | |
94 range->fType = type; | |
95 strip_uninteresting_trailing_advances_from_range(range); | |
96 int newLength; | |
97 if (type == AdvanceMetric::kRange) { | |
98 newLength = range->fEndId - range->fStartId + 1; | |
99 } else { | |
100 if (range->fEndId == range->fStartId) { | |
101 range->fType = AdvanceMetric::kRange; | |
102 } | |
103 newLength = 1; | |
104 } | |
105 SkASSERT(range->fAdvance.count() >= newLength); | |
106 range->fAdvance.setCount(newLength); | |
107 zero_wildcards_in_range(range); | |
108 } | |
109 | |
110 /** Retrieve advance data for glyphs. Used by the PDF backend. */ | |
111 // TODO(halcanary): this function is complex enough to need its logic | |
112 // tested with unit tests. On the other hand, I want to do another | |
113 // round of re-factoring before figuring out how to mock this. | |
114 // TODO(halcanary): this function should be combined with | |
115 // compose_advance_data() so that we don't need to produce a linked list | |
116 // of intermediate values. Or we could make the intermediate value | |
117 // something other than a linked list. | |
118 template <typename T> | |
119 static void set_glyph_widths(T advancer, | |
120 const SkBitSet* subset, | |
121 SkSinglyLinkedList<AdvanceMetric>* glyphWidths) { | |
122 // Assuming that on average, the ASCII representation of an advance plus | |
123 // a space is 8 characters and the ASCII representation of a glyph id is 3 | |
124 // characters, then the following cut offs for using different range types | |
125 // apply: | |
126 // The cost of stopping and starting the range is 7 characers | |
127 // a. Removing 4 0's or don't care's is a win | |
128 // The cost of stopping and starting the range plus a run is 22 | |
129 // characters | |
130 // b. Removing 3 repeating advances is a win | |
131 // c. Removing 2 repeating advances and 3 don't cares is a win | |
132 // When not currently in a range the cost of a run over a range is 16 | |
133 // characaters, so: | |
134 // d. Removing a leading 0/don't cares is a win because it is omitted | |
135 // e. Removing 2 repeating advances is a win | |
136 | |
137 int num_glyphs = advancer.count(); | |
138 | |
139 AdvanceMetric* prevRange = nullptr; | |
140 int16_t lastAdvance = kInvalidAdvance; | |
141 int repeatedAdvances = 0; | |
142 int wildCardsInRun = 0; | |
143 int trailingWildCards = 0; | |
144 | |
145 // Limit the loop count to glyph id ranges provided. | |
146 int lastIndex = num_glyphs; | |
147 if (subset) { | |
148 while (!subset->isBitSet(lastIndex - 1) && lastIndex > 0) { | |
149 --lastIndex; | |
150 } | |
151 } | |
152 AdvanceMetric curRange(0); | |
153 | |
154 for (int gId = 0; gId <= lastIndex; gId++) { | |
155 int16_t advance = kInvalidAdvance; | |
156 if (gId < lastIndex) { | |
157 if (!subset || 0 == gId || subset->isBitSet(gId)) { | |
158 advance = (int16_t)advancer(gId); | |
159 } else { | |
160 advance = kDontCareAdvance; | |
161 } | |
162 } | |
163 if (advance == lastAdvance) { | |
164 repeatedAdvances++; | |
165 trailingWildCards = 0; | |
166 } else if (advance == kDontCareAdvance) { | |
167 wildCardsInRun++; | |
168 trailingWildCards++; | |
169 } else if (curRange.fAdvance.count() == | |
170 repeatedAdvances + 1 + wildCardsInRun) { // All in run. | |
171 if (lastAdvance == 0) { | |
172 curRange.fStartId = gId; // reset | |
173 curRange.fAdvance.setCount(0); | |
174 trailingWildCards = 0; | |
175 } else if (repeatedAdvances + 1 >= 2 || trailingWildCards >= 4) { | |
176 finish_range(&curRange, gId - 1, AdvanceMetric::kRun); | |
177 prevRange = glyphWidths->emplace_back(std::move(curRange)); | |
178 curRange = AdvanceMetric(gId); | |
179 trailingWildCards = 0; | |
180 } | |
181 repeatedAdvances = 0; | |
182 wildCardsInRun = trailingWildCards; | |
183 trailingWildCards = 0; | |
184 } else { | |
185 if (lastAdvance == 0 && | |
186 repeatedAdvances + 1 + wildCardsInRun >= 4) { | |
187 finish_range(&curRange, | |
188 gId - repeatedAdvances - wildCardsInRun - 2, | |
189 AdvanceMetric::kRange); | |
190 prevRange = glyphWidths->emplace_back(std::move(curRange)); | |
191 curRange = AdvanceMetric(gId); | |
192 trailingWildCards = 0; | |
193 } else if (trailingWildCards >= 4 && repeatedAdvances + 1 < 2) { | |
194 finish_range(&curRange, gId - trailingWildCards - 1, | |
195 AdvanceMetric::kRange); | |
196 prevRange = glyphWidths->emplace_back(std::move(curRange)); | |
197 curRange = AdvanceMetric(gId); | |
198 trailingWildCards = 0; | |
199 } else if (lastAdvance != 0 && | |
200 (repeatedAdvances + 1 >= 3 || | |
201 (repeatedAdvances + 1 >= 2 && wildCardsInRun >= 3))) { | |
202 finish_range(&curRange, | |
203 gId - repeatedAdvances - wildCardsInRun - 2, | |
204 AdvanceMetric::kRange); | |
205 (void)glyphWidths->emplace_back(std::move(curRange)); | |
206 curRange = | |
207 AdvanceMetric(gId - repeatedAdvances - wildCardsInRun - 1); | |
208 curRange.fAdvance.append(1, &lastAdvance); | |
209 finish_range(&curRange, gId - 1, AdvanceMetric::kRun); | |
210 prevRange = glyphWidths->emplace_back(std::move(curRange)); | |
211 curRange = AdvanceMetric(gId); | |
212 trailingWildCards = 0; | |
213 } | |
214 repeatedAdvances = 0; | |
215 wildCardsInRun = trailingWildCards; | |
216 trailingWildCards = 0; | |
217 } | |
218 curRange.fAdvance.append(1, &advance); | |
219 if (advance != kDontCareAdvance) { | |
220 lastAdvance = advance; | |
221 } | |
222 } | |
223 if (curRange.fStartId == lastIndex) { | |
224 if (!prevRange) { | |
225 glyphWidths->reset(); | |
226 return; // https://crbug.com/567031 | |
227 } | |
228 } else { | |
229 finish_range(&curRange, lastIndex - 1, AdvanceMetric::kRange); | |
230 glyphWidths->emplace_back(std::move(curRange)); | |
231 } | |
232 } | |
233 | |
234 sk_sp<SkPDFArray> compose_advance_data( | |
235 const SkSinglyLinkedList<AdvanceMetric>& advanceInfo, | |
236 uint16_t emSize, | |
237 int16_t* defaultAdvance) { | |
238 auto result = sk_make_sp<SkPDFArray>(); | |
239 for (const AdvanceMetric& range : advanceInfo) { | |
240 switch (range.fType) { | |
241 case AdvanceMetric::kDefault: { | |
242 SkASSERT(range.fAdvance.count() == 1); | |
243 *defaultAdvance = range.fAdvance[0]; | |
244 break; | |
245 } | |
246 case AdvanceMetric::kRange: { | |
247 auto advanceArray = sk_make_sp<SkPDFArray>(); | |
248 for (int j = 0; j < range.fAdvance.count(); j++) | |
249 advanceArray->appendScalar( | |
250 scale_from_font_units(range.fAdvance[j], emSize)); | |
251 result->appendInt(range.fStartId); | |
252 result->appendObject(std::move(advanceArray)); | |
253 break; | |
254 } | |
255 case AdvanceMetric::kRun: { | |
256 SkASSERT(range.fAdvance.count() == 1); | |
257 result->appendInt(range.fStartId); | |
258 result->appendInt(range.fEndId); | |
259 result->appendScalar( | |
260 scale_from_font_units(range.fAdvance[0], emSize)); | |
261 break; | |
262 } | |
263 } | |
264 } | |
265 return result; | |
266 } | |
267 | |
268 struct Advancer { | |
269 SkGlyphCache* fCache; | |
270 SkScalar operator()(SkGlyphID gId) const { | |
271 return fCache->getGlyphIDAdvance(gId).fAdvanceX; | |
272 } | |
273 int count() const { return SkToInt(fCache->getGlyphCount()); } | |
274 }; | |
275 | |
276 template <typename T> | |
277 sk_sp<SkPDFArray> make_cid_glyph_widths_array(T advancer, | |
278 const SkBitSet* subset, | |
279 uint16_t emSize, | |
280 int16_t* defaultWidth) { | |
281 SkSinglyLinkedList<AdvanceMetric> glyphWidths; | |
282 set_glyph_widths<T>(advancer, subset, &glyphWidths); | |
283 return compose_advance_data(glyphWidths, emSize, defaultWidth); | |
284 } | |
285 } // namespace | |
286 | |
287 sk_sp<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(SkGlyphCache* cache, | |
bungeman-skia
2016/08/16 19:16:25
It is nice to pull out this call and all it's supp
| |
288 const SkBitSet* subset, | |
289 uint16_t emSize, | |
290 int16_t* defaultWidth) { | |
291 return make_cid_glyph_widths_array<Advancer>( | |
292 Advancer{cache}, | |
293 subset, emSize, defaultWidth); | |
294 } | |
295 | |
296 sk_sp<SkPDFArray> SkPDFMakeCIDGlyphWidthsArray(const SkPDFAdvancerMock& mock, | |
297 const SkBitSet* subset, | |
298 uint16_t emSize, | |
299 int16_t* defaultWidth) { | |
300 return make_cid_glyph_widths_array<const SkPDFAdvancerMock&>( | |
301 mock, subset, emSize, defaultWidth); | |
302 } | |
OLD | NEW |