OLD | NEW |
| (Empty) |
1 /* libs/graphics/sgl/SkScalerContext.cpp | |
2 ** | |
3 ** Copyright 2006, The Android Open Source Project | |
4 ** | |
5 ** Licensed under the Apache License, Version 2.0 (the "License"); | |
6 ** you may not use this file except in compliance with the License. | |
7 ** You may obtain a copy of the License at | |
8 ** | |
9 ** http://www.apache.org/licenses/LICENSE-2.0 | |
10 ** | |
11 ** Unless required by applicable law or agreed to in writing, software | |
12 ** distributed under the License is distributed on an "AS IS" BASIS, | |
13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 ** See the License for the specific language governing permissions and | |
15 ** limitations under the License. | |
16 */ | |
17 | |
18 #include "SkScalerContext.h" | |
19 #include "SkDescriptor.h" | |
20 #include "SkDraw.h" | |
21 #include "SkFontHost.h" | |
22 #include "SkMaskFilter.h" | |
23 #include "SkPathEffect.h" | |
24 #include "SkRasterizer.h" | |
25 #include "SkRegion.h" | |
26 #include "SkStroke.h" | |
27 #include "SkThread.h" | |
28 | |
29 #ifdef SK_DEBUG | |
30 // #define TRACK_MISSING_CHARS | |
31 #endif | |
32 | |
33 #define ComputeBWRowBytes(width) (((unsigned)(width) + 7) >> 3) | |
34 | |
35 static const uint8_t* gBlackGammaTable; | |
36 static const uint8_t* gWhiteGammaTable; | |
37 | |
38 void SkGlyph::toMask(SkMask* mask) const { | |
39 SkASSERT(mask); | |
40 | |
41 mask->fImage = (uint8_t*)fImage; | |
42 mask->fBounds.set(fLeft, fTop, fLeft + fWidth, fTop + fHeight); | |
43 mask->fRowBytes = this->rowBytes(); | |
44 mask->fFormat = fMaskFormat; | |
45 } | |
46 | |
47 size_t SkGlyph::computeImageSize() const { | |
48 size_t size = this->rowBytes() * fHeight; | |
49 if (fMaskFormat == SkMask::k3D_Format) { | |
50 size *= 3; | |
51 } | |
52 return size; | |
53 } | |
54 | |
55 void SkGlyph::zeroMetrics() { | |
56 fAdvanceX = 0; | |
57 fAdvanceY = 0; | |
58 fWidth = 0; | |
59 fHeight = 0; | |
60 fTop = 0; | |
61 fLeft = 0; | |
62 fRsbDelta = 0; | |
63 fLsbDelta = 0; | |
64 } | |
65 | |
66 #ifdef SK_DEBUG | |
67 #define DUMP_RECx | |
68 #endif | |
69 | |
70 static SkFlattenable* load_flattenable(const SkDescriptor* desc, uint32_t tag) { | |
71 SkFlattenable* obj = NULL; | |
72 uint32_t len; | |
73 const void* data = desc->findEntry(tag, &len); | |
74 | |
75 if (data) { | |
76 SkFlattenableReadBuffer buffer(data, len); | |
77 obj = buffer.readFlattenable(); | |
78 SkASSERT(buffer.offset() == buffer.size()); | |
79 } | |
80 return obj; | |
81 } | |
82 | |
83 SkScalerContext::SkScalerContext(const SkDescriptor* desc) | |
84 : fPathEffect(NULL), fMaskFilter(NULL) | |
85 { | |
86 static bool gHaveGammaTables; | |
87 if (!gHaveGammaTables) { | |
88 const uint8_t* tables[2]; | |
89 SkFontHost::GetGammaTables(tables); | |
90 gBlackGammaTable = tables[0]; | |
91 gWhiteGammaTable = tables[1]; | |
92 gHaveGammaTables = true; | |
93 } | |
94 | |
95 fBaseGlyphCount = 0; | |
96 fAuxScalerContext = NULL; | |
97 | |
98 const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL); | |
99 SkASSERT(rec); | |
100 | |
101 fRec = *rec; | |
102 | |
103 #ifdef DUMP_REC | |
104 desc->assertChecksum(); | |
105 SkDebugf("SkScalarContext checksum %x count %d length %d\n", desc->getChecks
um(), desc->getCount(), desc->getLength()); | |
106 SkDebugf(" textsize %g prescale %g preskew %g post [%g %g %g %g]\n", | |
107 rec->fTextSize, rec->fPreScaleX, rec->fPreSkewX, rec->fPost2x2[0][0], | |
108 rec->fPost2x2[0][1], rec->fPost2x2[1][0], rec->fPost2x2[1][1]); | |
109 SkDebugf(" frame %g miter %g hints %d framefill %d format %d join %d\n", | |
110 rec->fFrameWidth, rec->fMiterLimit, rec->fHints, rec->fFrameAndFill, | |
111 rec->fMaskFormat, rec->fStrokeJoin); | |
112 SkDebugf(" pathEffect %x maskFilter %x\n", desc->findEntry(kPathEffect_SkDe
scriptorTag, NULL), | |
113 desc->findEntry(kMaskFilter_SkDescriptorTag, NULL)); | |
114 #endif | |
115 | |
116 fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptor
Tag); | |
117 fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptor
Tag); | |
118 fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptor
Tag); | |
119 } | |
120 | |
121 SkScalerContext::~SkScalerContext() { | |
122 fPathEffect->safeUnref(); | |
123 fMaskFilter->safeUnref(); | |
124 fRasterizer->safeUnref(); | |
125 | |
126 SkDELETE(fAuxScalerContext); | |
127 } | |
128 | |
129 SkScalerContext* SkScalerContext::loadAuxContext() const { | |
130 if (NULL == fAuxScalerContext) { | |
131 fAuxScalerContext = SkFontHost::CreateFallbackScalerContext(fRec); | |
132 if (NULL != fAuxScalerContext) { | |
133 fAuxScalerContext->setBaseGlyphCount(this->getGlyphCount()); | |
134 } | |
135 } | |
136 return fAuxScalerContext; | |
137 } | |
138 | |
139 #ifdef TRACK_MISSING_CHARS | |
140 static uint8_t gMissingChars[1 << 13]; | |
141 #endif | |
142 | |
143 uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) { | |
144 unsigned glyphID = this->generateCharToGlyph(uni); | |
145 | |
146 if (0 == glyphID) { // try auxcontext | |
147 SkScalerContext* ctx = this->loadAuxContext(); | |
148 if (NULL != ctx) { | |
149 glyphID = ctx->generateCharToGlyph(uni); | |
150 if (0 != glyphID) { // only fiddle with it if its not missing | |
151 glyphID += this->getGlyphCount(); | |
152 if (glyphID > 0xFFFF) { | |
153 glyphID = 0; | |
154 } | |
155 } | |
156 } | |
157 } | |
158 #ifdef TRACK_MISSING_CHARS | |
159 if (0 == glyphID) { | |
160 bool announce = false; | |
161 if (uni > 0xFFFF) { // we don't record these | |
162 announce = true; | |
163 } else { | |
164 unsigned index = uni >> 3; | |
165 unsigned mask = 1 << (uni & 7); | |
166 SkASSERT(index < SK_ARRAY_COUNT(gMissingChars)); | |
167 if ((gMissingChars[index] & mask) == 0) { | |
168 gMissingChars[index] |= mask; | |
169 announce = true; | |
170 } | |
171 } | |
172 if (announce) { | |
173 printf(">>> MISSING CHAR <<< 0x%04X\n", uni); | |
174 } | |
175 } | |
176 #endif | |
177 return SkToU16(glyphID); | |
178 } | |
179 | |
180 /* Internal routine to resolve auxContextID into a real context. | |
181 Only makes sense to call once the glyph has been given a | |
182 valid auxGlyphID. | |
183 */ | |
184 SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) const { | |
185 SkScalerContext* ctx = const_cast<SkScalerContext*>(this); | |
186 | |
187 if (glyph.getGlyphID() >= this->getGlyphCount()) { | |
188 ctx = this->loadAuxContext(); | |
189 if (NULL == ctx) { // if no aux, just return us | |
190 ctx = const_cast<SkScalerContext*>(this); | |
191 } | |
192 } | |
193 return ctx; | |
194 } | |
195 | |
196 static int plus_minus_pin(int value, int max) { | |
197 SkASSERT(max >= 0); | |
198 | |
199 if (value > max) { | |
200 value = max; | |
201 } else if (value < -max) { | |
202 value = -max; | |
203 } | |
204 return value; | |
205 } | |
206 | |
207 void SkScalerContext::getAdvance(SkGlyph* glyph) { | |
208 // mark us as just having a valid advance | |
209 glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE; | |
210 // we mark the format before making the call, in case the impl | |
211 // internally ends up calling its generateMetrics, which is OK | |
212 // albeit slower than strictly necessary | |
213 this->getGlyphContext(*glyph)->generateAdvance(glyph); | |
214 } | |
215 | |
216 void SkScalerContext::getMetrics(SkGlyph* glyph) { | |
217 this->getGlyphContext(*glyph)->generateMetrics(glyph); | |
218 | |
219 // for now we have separate cache entries for devkerning on and off | |
220 // in the future we might share caches, but make our measure/draw | |
221 // code make the distinction. Thus we zap the values if the caller | |
222 // has not asked for them. | |
223 if ((fRec.fFlags & SkScalerContext::kDevKernText_Flag) == 0) { | |
224 // no devkern, so zap the fields | |
225 glyph->fLsbDelta = glyph->fRsbDelta = 0; | |
226 } | |
227 | |
228 // if either dimension is empty, zap the image bounds of the glyph | |
229 if (0 == glyph->fWidth || 0 == glyph->fHeight) { | |
230 glyph->fWidth = 0; | |
231 glyph->fHeight = 0; | |
232 glyph->fTop = 0; | |
233 glyph->fLeft = 0; | |
234 glyph->fMaskFormat = 0; | |
235 return; | |
236 } | |
237 | |
238 if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { | |
239 SkPath devPath, fillPath; | |
240 SkMatrix fillToDevMatrix; | |
241 | |
242 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); | |
243 | |
244 if (fRasterizer) { | |
245 SkMask mask; | |
246 | |
247 if (fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, | |
248 fMaskFilter, &mask, | |
249 SkMask::kJustComputeBounds_CreateMode)) { | |
250 glyph->fLeft = mask.fBounds.fLeft; | |
251 glyph->fTop = mask.fBounds.fTop; | |
252 glyph->fWidth = SkToU16(mask.fBounds.width()); | |
253 glyph->fHeight = SkToU16(mask.fBounds.height()); | |
254 } else { | |
255 // draw nothing 'cause we failed | |
256 glyph->fLeft = 0; | |
257 glyph->fTop = 0; | |
258 glyph->fWidth = 0; | |
259 glyph->fHeight = 0; | |
260 return; | |
261 } | |
262 } else { | |
263 // just use devPath | |
264 SkRect r; | |
265 SkIRect ir; | |
266 | |
267 devPath.computeBounds(&r, SkPath::kExact_BoundsType); | |
268 r.roundOut(&ir); | |
269 | |
270 glyph->fLeft = ir.fLeft; | |
271 glyph->fTop = ir.fTop; | |
272 glyph->fWidth = SkToU16(ir.width()); | |
273 glyph->fHeight = SkToU16(ir.height()); | |
274 } | |
275 } | |
276 | |
277 glyph->fMaskFormat = fRec.fMaskFormat; | |
278 | |
279 if (fMaskFilter) { | |
280 SkMask src, dst; | |
281 SkMatrix matrix; | |
282 | |
283 glyph->toMask(&src); | |
284 fRec.getMatrixFrom2x2(&matrix); | |
285 | |
286 src.fImage = NULL; // only want the bounds from the filter | |
287 if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) { | |
288 SkASSERT(dst.fImage == NULL); | |
289 glyph->fLeft = dst.fBounds.fLeft; | |
290 glyph->fTop = dst.fBounds.fTop; | |
291 glyph->fWidth = SkToU16(dst.fBounds.width()); | |
292 glyph->fHeight = SkToU16(dst.fBounds.height()); | |
293 glyph->fMaskFormat = dst.fFormat; | |
294 } | |
295 } | |
296 } | |
297 | |
298 void SkScalerContext::getImage(const SkGlyph& origGlyph) { | |
299 const SkGlyph* glyph = &origGlyph; | |
300 SkGlyph tmpGlyph; | |
301 | |
302 if (fMaskFilter) { // restore the prefilter bounds | |
303 tmpGlyph.fID = origGlyph.fID; | |
304 | |
305 // need the original bounds, sans our maskfilter | |
306 SkMaskFilter* mf = fMaskFilter; | |
307 fMaskFilter = NULL; // temp disable | |
308 this->getMetrics(&tmpGlyph); | |
309 fMaskFilter = mf; // restore | |
310 | |
311 tmpGlyph.fImage = origGlyph.fImage; | |
312 | |
313 // we need the prefilter bounds to be <= filter bounds | |
314 SkASSERT(tmpGlyph.fWidth <= origGlyph.fWidth); | |
315 SkASSERT(tmpGlyph.fHeight <= origGlyph.fHeight); | |
316 glyph = &tmpGlyph; | |
317 } | |
318 | |
319 if (fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL) { | |
320 SkPath devPath, fillPath; | |
321 SkMatrix fillToDevMatrix; | |
322 | |
323 this->internalGetPath(*glyph, &fillPath, &devPath, &fillToDevMatrix); | |
324 | |
325 if (fRasterizer) { | |
326 SkMask mask; | |
327 | |
328 glyph->toMask(&mask); | |
329 mask.fFormat = SkMask::kA8_Format; | |
330 bzero(glyph->fImage, mask.computeImageSize()); | |
331 | |
332 if (!fRasterizer->rasterize(fillPath, fillToDevMatrix, NULL, | |
333 fMaskFilter, &mask, | |
334 SkMask::kJustRenderImage_CreateMode)) { | |
335 return; | |
336 } | |
337 } else { | |
338 SkBitmap bm; | |
339 SkBitmap::Config config; | |
340 SkMatrix matrix; | |
341 SkRegion clip; | |
342 SkPaint paint; | |
343 SkDraw draw; | |
344 | |
345 if (SkMask::kA8_Format == fRec.fMaskFormat) { | |
346 config = SkBitmap::kA8_Config; | |
347 paint.setAntiAlias(true); | |
348 } else { | |
349 SkASSERT(SkMask::kBW_Format == fRec.fMaskFormat); | |
350 config = SkBitmap::kA1_Config; | |
351 paint.setAntiAlias(false); | |
352 } | |
353 | |
354 clip.setRect(0, 0, glyph->fWidth, glyph->fHeight); | |
355 matrix.setTranslate(-SkIntToScalar(glyph->fLeft), | |
356 -SkIntToScalar(glyph->fTop)); | |
357 bm.setConfig(config, glyph->fWidth, glyph->fHeight, | |
358 glyph->rowBytes()); | |
359 bm.setPixels(glyph->fImage); | |
360 bzero(glyph->fImage, bm.height() * bm.rowBytes()); | |
361 | |
362 draw.fClip = &clip; | |
363 draw.fMatrix = &matrix; | |
364 draw.fBitmap = &bm; | |
365 draw.fBounder = NULL; | |
366 draw.drawPath(devPath, paint); | |
367 } | |
368 } else { | |
369 this->getGlyphContext(*glyph)->generateImage(*glyph); | |
370 } | |
371 | |
372 if (fMaskFilter) { | |
373 SkMask srcM, dstM; | |
374 SkMatrix matrix; | |
375 | |
376 // the src glyph image shouldn't be 3D | |
377 SkASSERT(SkMask::k3D_Format != glyph->fMaskFormat); | |
378 glyph->toMask(&srcM); | |
379 fRec.getMatrixFrom2x2(&matrix); | |
380 | |
381 if (fMaskFilter->filterMask(&dstM, srcM, matrix, NULL)) { | |
382 int width = SkFastMin32(origGlyph.fWidth, dstM.fBounds.width()); | |
383 int height = SkFastMin32(origGlyph.fHeight, dstM.fBounds.height()); | |
384 int dstRB = origGlyph.rowBytes(); | |
385 int srcRB = dstM.fRowBytes; | |
386 | |
387 const uint8_t* src = (const uint8_t*)dstM.fImage; | |
388 uint8_t* dst = (uint8_t*)origGlyph.fImage; | |
389 | |
390 if (SkMask::k3D_Format == dstM.fFormat) { | |
391 // we have to copy 3 times as much | |
392 height *= 3; | |
393 } | |
394 | |
395 // clean out our glyph, since it may be larger than dstM | |
396 //bzero(dst, height * dstRB); | |
397 | |
398 while (--height >= 0) { | |
399 memcpy(dst, src, width); | |
400 src += srcRB; | |
401 dst += dstRB; | |
402 } | |
403 SkMask::FreeImage(dstM.fImage); | |
404 } | |
405 } | |
406 | |
407 // check to see if we should filter the alpha channel | |
408 | |
409 if (NULL == fMaskFilter && | |
410 fRec.fMaskFormat != SkMask::kBW_Format && | |
411 (fRec.fFlags & (kGammaForBlack_Flag | kGammaForWhite_Flag)) != 0) | |
412 { | |
413 const uint8_t* table = (fRec.fFlags & kGammaForBlack_Flag) ? gBlackGamma
Table : gWhiteGammaTable; | |
414 if (NULL != table) | |
415 { | |
416 uint8_t* dst = (uint8_t*)origGlyph.fImage; | |
417 unsigned rowBytes = origGlyph.rowBytes(); | |
418 | |
419 for (int y = origGlyph.fHeight - 1; y >= 0; --y) | |
420 { | |
421 for (int x = origGlyph.fWidth - 1; x >= 0; --x) | |
422 dst[x] = table[dst[x]]; | |
423 dst += rowBytes; | |
424 } | |
425 } | |
426 } | |
427 } | |
428 | |
429 void SkScalerContext::getPath(const SkGlyph& glyph, SkPath* path) | |
430 { | |
431 this->internalGetPath(glyph, NULL, path, NULL); | |
432 } | |
433 | |
434 void SkScalerContext::getFontMetrics(SkPaint::FontMetrics* mx, SkPaint::FontMetr
ics* my) | |
435 { | |
436 this->generateFontMetrics(mx, my); | |
437 } | |
438 | |
439 /////////////////////////////////////////////////////////////////////// | |
440 | |
441 void SkScalerContext::internalGetPath(const SkGlyph& glyph, SkPath* fillPath, Sk
Path* devPath, SkMatrix* fillToDevMatrix) | |
442 { | |
443 SkPath path; | |
444 | |
445 this->getGlyphContext(glyph)->generatePath(glyph, &path); | |
446 | |
447 if (fRec.fFrameWidth > 0 || fPathEffect != NULL) | |
448 { | |
449 // need the path in user-space, with only the point-size applied | |
450 // so that our stroking and effects will operate the same way they | |
451 // would if the user had extracted the path themself, and then | |
452 // called drawPath | |
453 SkPath localPath; | |
454 SkMatrix matrix, inverse; | |
455 | |
456 fRec.getMatrixFrom2x2(&matrix); | |
457 matrix.invert(&inverse); | |
458 path.transform(inverse, &localPath); | |
459 // now localPath is only affected by the paint settings, and not the can
vas matrix | |
460 | |
461 SkScalar width = fRec.fFrameWidth; | |
462 | |
463 if (fPathEffect) | |
464 { | |
465 SkPath effectPath; | |
466 | |
467 if (fPathEffect->filterPath(&effectPath, localPath, &width)) | |
468 localPath.swap(effectPath); | |
469 } | |
470 | |
471 if (width > 0) | |
472 { | |
473 SkStroke stroker; | |
474 SkPath outline; | |
475 | |
476 stroker.setWidth(width); | |
477 stroker.setMiterLimit(fRec.fMiterLimit); | |
478 stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin); | |
479 stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag)); | |
480 stroker.strokePath(localPath, &outline); | |
481 localPath.swap(outline); | |
482 } | |
483 | |
484 // now return stuff to the caller | |
485 if (fillToDevMatrix) | |
486 *fillToDevMatrix = matrix; | |
487 | |
488 if (devPath) | |
489 localPath.transform(matrix, devPath); | |
490 | |
491 if (fillPath) | |
492 fillPath->swap(localPath); | |
493 } | |
494 else // nothing tricky to do | |
495 { | |
496 if (fillToDevMatrix) | |
497 fillToDevMatrix->reset(); | |
498 | |
499 if (devPath) | |
500 { | |
501 if (fillPath == NULL) | |
502 devPath->swap(path); | |
503 else | |
504 *devPath = path; | |
505 } | |
506 | |
507 if (fillPath) | |
508 fillPath->swap(path); | |
509 } | |
510 | |
511 if (devPath) | |
512 devPath->updateBoundsCache(); | |
513 if (fillPath) | |
514 fillPath->updateBoundsCache(); | |
515 } | |
516 | |
517 | |
518 void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const | |
519 { | |
520 dst->reset(); | |
521 dst->setScaleX(fPost2x2[0][0]); | |
522 dst->setSkewX( fPost2x2[0][1]); | |
523 dst->setSkewY( fPost2x2[1][0]); | |
524 dst->setScaleY(fPost2x2[1][1]); | |
525 } | |
526 | |
527 void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const | |
528 { | |
529 m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize); | |
530 if (fPreSkewX) | |
531 m->postSkew(fPreSkewX, 0); | |
532 } | |
533 | |
534 void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const | |
535 { | |
536 this->getLocalMatrix(m); | |
537 | |
538 // now concat the device matrix | |
539 { | |
540 SkMatrix deviceMatrix; | |
541 this->getMatrixFrom2x2(&deviceMatrix); | |
542 m->postConcat(deviceMatrix); | |
543 } | |
544 } | |
545 | |
546 #include "SkFontHost.h" | |
547 | |
548 SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) | |
549 { | |
550 return SkFontHost::CreateScalerContext(desc); | |
551 } | |
552 | |
OLD | NEW |