OLD | NEW |
1 | 1 |
2 /* | 2 /* |
3 * Copyright 2006 The Android Open Source Project | 3 * Copyright 2006 The Android Open Source Project |
4 * | 4 * |
5 * Use of this source code is governed by a BSD-style license that can be | 5 * Use of this source code is governed by a BSD-style license that can be |
6 * found in the LICENSE file. | 6 * found in the LICENSE file. |
7 */ | 7 */ |
8 | 8 |
9 | 9 |
10 #include "SkScalerContext.h" | 10 #include "SkScalerContext.h" |
(...skipping 322 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
333 SkIRect ir; | 333 SkIRect ir; |
334 devPath.getBounds().roundOut(&ir); | 334 devPath.getBounds().roundOut(&ir); |
335 | 335 |
336 if (ir.isEmpty() || !ir.is16Bit()) { | 336 if (ir.isEmpty() || !ir.is16Bit()) { |
337 goto SK_ERROR; | 337 goto SK_ERROR; |
338 } | 338 } |
339 glyph->fLeft = ir.fLeft; | 339 glyph->fLeft = ir.fLeft; |
340 glyph->fTop = ir.fTop; | 340 glyph->fTop = ir.fTop; |
341 glyph->fWidth = SkToU16(ir.width()); | 341 glyph->fWidth = SkToU16(ir.width()); |
342 glyph->fHeight = SkToU16(ir.height()); | 342 glyph->fHeight = SkToU16(ir.height()); |
| 343 |
| 344 if (glyph->fWidth > 0) { |
| 345 switch (fRec.fMaskFormat) { |
| 346 case SkMask::kLCD16_Format: |
| 347 case SkMask::kLCD32_Format: |
| 348 glyph->fWidth += 2; |
| 349 glyph->fLeft -= 1; |
| 350 break; |
| 351 default: |
| 352 break; |
| 353 } |
| 354 } |
343 } | 355 } |
344 } | 356 } |
345 | 357 |
346 if (SkMask::kARGB32_Format != glyph->fMaskFormat) { | 358 if (SkMask::kARGB32_Format != glyph->fMaskFormat) { |
347 glyph->fMaskFormat = fRec.fMaskFormat; | 359 glyph->fMaskFormat = fRec.fMaskFormat; |
348 } | 360 } |
349 | 361 |
350 // If we are going to create the mask, then we cannot keep the color | 362 // If we are going to create the mask, then we cannot keep the color |
351 if ((fGenerateImageFromPath || fMaskFilter) && | 363 if ((fGenerateImageFromPath || fMaskFilter) && |
352 SkMask::kARGB32_Format == glyph->fMaskFormat) { | 364 SkMask::kARGB32_Format == glyph->fMaskFormat) { |
(...skipping 26 matching lines...) Expand all Loading... |
379 // draw nothing 'cause we failed | 391 // draw nothing 'cause we failed |
380 glyph->fLeft = 0; | 392 glyph->fLeft = 0; |
381 glyph->fTop = 0; | 393 glyph->fTop = 0; |
382 glyph->fWidth = 0; | 394 glyph->fWidth = 0; |
383 glyph->fHeight = 0; | 395 glyph->fHeight = 0; |
384 // put a valid value here, in case it was earlier set to | 396 // put a valid value here, in case it was earlier set to |
385 // MASK_FORMAT_JUST_ADVANCE | 397 // MASK_FORMAT_JUST_ADVANCE |
386 glyph->fMaskFormat = fRec.fMaskFormat; | 398 glyph->fMaskFormat = fRec.fMaskFormat; |
387 } | 399 } |
388 | 400 |
| 401 #define SK_SHOW_TEXT_BLIT_COVERAGE 0 |
389 | 402 |
390 static void applyLUTToA8Mask(const SkMask& mask, const uint8_t* lut) { | 403 static void applyLUTToA8Mask(const SkMask& mask, const uint8_t* lut) { |
391 uint8_t* SK_RESTRICT dst = (uint8_t*)mask.fImage; | 404 uint8_t* SK_RESTRICT dst = (uint8_t*)mask.fImage; |
392 unsigned rowBytes = mask.fRowBytes; | 405 unsigned rowBytes = mask.fRowBytes; |
393 | 406 |
394 for (int y = mask.fBounds.height() - 1; y >= 0; --y) { | 407 for (int y = mask.fBounds.height() - 1; y >= 0; --y) { |
395 for (int x = mask.fBounds.width() - 1; x >= 0; --x) { | 408 for (int x = mask.fBounds.width() - 1; x >= 0; --x) { |
396 dst[x] = lut[dst[x]]; | 409 dst[x] = lut[dst[x]]; |
397 } | 410 } |
398 dst += rowBytes; | 411 dst += rowBytes; |
399 } | 412 } |
400 } | 413 } |
401 | 414 |
402 template<bool APPLY_PREBLEND> | 415 template<bool APPLY_PREBLEND> |
403 static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst, | 416 static void pack4xHToLCD16(const SkBitmap& src, const SkMask& dst, |
404 const SkMaskGamma::PreBlend& maskPreBlend) { | 417 const SkMaskGamma::PreBlend& maskPreBlend) { |
| 418 #define SAMPLES_PER_PIXEL 4 |
| 419 #define LCD_PER_PIXEL 3 |
405 SkASSERT(SkBitmap::kA8_Config == src.config()); | 420 SkASSERT(SkBitmap::kA8_Config == src.config()); |
406 SkASSERT(SkMask::kLCD16_Format == dst.fFormat); | 421 SkASSERT(SkMask::kLCD16_Format == dst.fFormat); |
407 | 422 |
408 const int width = dst.fBounds.width(); | 423 const int sample_width = src.width(); |
409 const int height = dst.fBounds.height(); | 424 const int height = src.height(); |
| 425 |
410 uint16_t* dstP = (uint16_t*)dst.fImage; | 426 uint16_t* dstP = (uint16_t*)dst.fImage; |
411 size_t dstRB = dst.fRowBytes; | 427 size_t dstRB = dst.fRowBytes; |
| 428 // An N tap FIR is defined by |
| 429 // out[n] = coeff[0]*x[n] + coeff[1]*x[n-1] + ... + coeff[N]*x[n-N] |
| 430 // or |
| 431 // out[n] = sum(i, 0, N, coeff[i]*x[n-i]) |
| 432 |
| 433 // The strategy is to use one FIR (different coefficients) for each of r, g,
and b. |
| 434 // This means using every 4th FIR output value of each FIR and discarding th
e rest. |
| 435 // The FIRs are aligned, and the coefficients reach 5 samples to each side o
f their 'center'. |
| 436 // (For r and b this is technically incorrect, but the coeffs outside round
to zero anyway.) |
| 437 |
| 438 // These are in some fixed point repesentation. |
| 439 // Adding up to more than one simulates ink spread. |
| 440 // For implementation reasons, these should never add up to more than two. |
| 441 |
| 442 // Coefficients determined by a gausian where 5 samples = 3 std deviations (
0x110 'contrast'). |
| 443 // Calculated using tools/generate_fir_coeff.py |
| 444 // With this one almost no fringing is ever seen, but it is imperceptibly bl
urry. |
| 445 // The lcd smoothed text is almost imperceptibly different from gray, |
| 446 // but is still sharper on small stems and small rounded corners than gray. |
| 447 // This also seems to be about as wide as one can get and only have a three
pixel kernel. |
| 448 // TODO: caculate these at runtime so parameters can be adjusted (esp contra
st). |
| 449 static const unsigned int coefficients[LCD_PER_PIXEL][SAMPLES_PER_PIXEL*3] =
{ |
| 450 //The red subpixel is centered inside the first sample (at 1/6 pixel), a
nd is shifted. |
| 451 { 0x03, 0x0b, 0x1c, 0x33, 0x40, 0x39, 0x24, 0x10, 0x05, 0x01, 0x00, 0x
00, }, |
| 452 //The green subpixel is centered between two samples (at 1/2 pixel), so
is symetric |
| 453 { 0x00, 0x02, 0x08, 0x16, 0x2b, 0x3d, 0x3d, 0x2b, 0x16, 0x08, 0x02, 0x
00, }, |
| 454 //The blue subpixel is centered inside the last sample (at 5/6 pixel), a
nd is shifted. |
| 455 { 0x00, 0x00, 0x01, 0x05, 0x10, 0x24, 0x39, 0x40, 0x33, 0x1c, 0x0b, 0x
03, }, |
| 456 }; |
412 | 457 |
413 for (int y = 0; y < height; ++y) { | 458 for (int y = 0; y < height; ++y) { |
414 const uint8_t* srcP = src.getAddr8(0, y); | 459 const uint8_t* srcP = src.getAddr8(0, y); |
415 for (int x = 0; x < width; ++x) { | 460 |
416 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fR); | 461 // TODO: this fir filter implementation is straight forward, but slow. |
417 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fG); | 462 // It should be possible to make it much faster. |
418 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fB); | 463 for (int sample_x = -4, pixel_x = 0; sample_x < sample_width + 4; sample
_x += 4, ++pixel_x) { |
419 dstP[x] = SkPack888ToRGB16(r, g, b); | 464 int fir[LCD_PER_PIXEL] = { 0 }; |
| 465 for (int sample_index = SkMax32(0, sample_x - 4), coeff_index = samp
le_index - (sample_x - 4) |
| 466 ; sample_index < SkMin32(sample_x + 8, sample_width) |
| 467 ; ++sample_index, ++coeff_index) |
| 468 { |
| 469 int sample_value = srcP[sample_index]; |
| 470 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpx
l_index) { |
| 471 fir[subpxl_index] += coefficients[subpxl_index][coeff_index]
* sample_value; |
| 472 } |
| 473 } |
| 474 for (int subpxl_index = 0; subpxl_index < LCD_PER_PIXEL; ++subpxl_in
dex) { |
| 475 fir[subpxl_index] /= 0x100; |
| 476 fir[subpxl_index] = SkMin32(fir[subpxl_index], 255); |
| 477 } |
| 478 |
| 479 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(fir[0], maskPreBlend.fR); |
| 480 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(fir[1], maskPreBlend.fG); |
| 481 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(fir[2], maskPreBlend.fB); |
| 482 #if SK_SHOW_TEXT_BLIT_COVERAGE |
| 483 r = SkMax32(r, 10); g = SkMax32(g, 10); b = SkMax32(b, 10); |
| 484 #endif |
| 485 dstP[pixel_x] = SkPack888ToRGB16(r, g, b); |
420 } | 486 } |
421 dstP = (uint16_t*)((char*)dstP + dstRB); | 487 dstP = (uint16_t*)((char*)dstP + dstRB); |
422 } | 488 } |
423 } | 489 } |
424 | 490 |
425 template<bool APPLY_PREBLEND> | 491 template<bool APPLY_PREBLEND> |
426 static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst, | 492 static void pack4xHToLCD32(const SkBitmap& src, const SkMask& dst, |
427 const SkMaskGamma::PreBlend& maskPreBlend) { | 493 const SkMaskGamma::PreBlend& maskPreBlend) { |
428 SkASSERT(SkBitmap::kA8_Config == src.config()); | 494 SkASSERT(SkBitmap::kA8_Config == src.config()); |
429 SkASSERT(SkMask::kLCD32_Format == dst.fFormat); | 495 SkASSERT(SkMask::kLCD32_Format == dst.fFormat); |
430 | 496 |
431 const int width = dst.fBounds.width(); | 497 const int width = dst.fBounds.width(); |
432 const int height = dst.fBounds.height(); | 498 const int height = dst.fBounds.height(); |
433 SkPMColor* dstP = (SkPMColor*)dst.fImage; | 499 SkPMColor* dstP = (SkPMColor*)dst.fImage; |
434 size_t dstRB = dst.fRowBytes; | 500 size_t dstRB = dst.fRowBytes; |
435 | 501 |
436 for (int y = 0; y < height; ++y) { | 502 for (int y = 0; y < height; ++y) { |
437 const uint8_t* srcP = src.getAddr8(0, y); | 503 const uint8_t* srcP = src.getAddr8(0, y); |
| 504 |
| 505 // TODO: need to use fir filter here as well. |
438 for (int x = 0; x < width; ++x) { | 506 for (int x = 0; x < width; ++x) { |
439 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fR); | 507 U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fR); |
440 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fG); | 508 U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fG); |
441 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fB); | 509 U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fB); |
442 dstP[x] = SkPackARGB32(0xFF, r, g, b); | 510 dstP[x] = SkPackARGB32(0xFF, r, g, b); |
443 } | 511 } |
444 dstP = (SkPMColor*)((char*)dstP + dstRB); | 512 dstP = (SkPMColor*)((char*)dstP + dstRB); |
445 } | 513 } |
446 } | 514 } |
447 | 515 |
(...skipping 17 matching lines...) Expand all Loading... |
465 paint.setAntiAlias(false); | 533 paint.setAntiAlias(false); |
466 } else { | 534 } else { |
467 config = SkBitmap::kA8_Config; | 535 config = SkBitmap::kA8_Config; |
468 paint.setAntiAlias(true); | 536 paint.setAntiAlias(true); |
469 switch (mask.fFormat) { | 537 switch (mask.fFormat) { |
470 case SkMask::kA8_Format: | 538 case SkMask::kA8_Format: |
471 break; | 539 break; |
472 case SkMask::kLCD16_Format: | 540 case SkMask::kLCD16_Format: |
473 case SkMask::kLCD32_Format: | 541 case SkMask::kLCD32_Format: |
474 // TODO: trigger off LCD orientation | 542 // TODO: trigger off LCD orientation |
475 dstW *= 3; | 543 dstW = 4*dstW - 8; |
476 matrix.postScale(SkIntToScalar(3), SK_Scalar1); | 544 matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft + 1), |
| 545 -SkIntToScalar(mask.fBounds.fTop)); |
| 546 matrix.postScale(SkIntToScalar(4), SK_Scalar1); |
477 dstRB = 0; // signals we need a copy | 547 dstRB = 0; // signals we need a copy |
478 break; | 548 break; |
479 default: | 549 default: |
480 SkDEBUGFAIL("unexpected mask format"); | 550 SkDEBUGFAIL("unexpected mask format"); |
481 } | 551 } |
482 } | 552 } |
483 | 553 |
484 SkRasterClip clip; | 554 SkRasterClip clip; |
485 clip.setRect(SkIRect::MakeWH(dstW, dstH)); | 555 clip.setRect(SkIRect::MakeWH(dstW, dstH)); |
486 | 556 |
(...skipping 20 matching lines...) Expand all Loading... |
507 draw.drawPath(path, paint); | 577 draw.drawPath(path, paint); |
508 | 578 |
509 switch (mask.fFormat) { | 579 switch (mask.fFormat) { |
510 case SkMask::kA8_Format: | 580 case SkMask::kA8_Format: |
511 if (maskPreBlend.isApplicable()) { | 581 if (maskPreBlend.isApplicable()) { |
512 applyLUTToA8Mask(mask, maskPreBlend.fG); | 582 applyLUTToA8Mask(mask, maskPreBlend.fG); |
513 } | 583 } |
514 break; | 584 break; |
515 case SkMask::kLCD16_Format: | 585 case SkMask::kLCD16_Format: |
516 if (maskPreBlend.isApplicable()) { | 586 if (maskPreBlend.isApplicable()) { |
517 pack3xHToLCD16<true>(bm, mask, maskPreBlend); | 587 pack4xHToLCD16<true>(bm, mask, maskPreBlend); |
518 } else { | 588 } else { |
519 pack3xHToLCD16<false>(bm, mask, maskPreBlend); | 589 pack4xHToLCD16<false>(bm, mask, maskPreBlend); |
520 } | 590 } |
521 break; | 591 break; |
522 case SkMask::kLCD32_Format: | 592 case SkMask::kLCD32_Format: |
523 if (maskPreBlend.isApplicable()) { | 593 if (maskPreBlend.isApplicable()) { |
524 pack3xHToLCD32<true>(bm, mask, maskPreBlend); | 594 pack4xHToLCD32<true>(bm, mask, maskPreBlend); |
525 } else { | 595 } else { |
526 pack3xHToLCD32<false>(bm, mask, maskPreBlend); | 596 pack4xHToLCD32<false>(bm, mask, maskPreBlend); |
527 } | 597 } |
528 break; | 598 break; |
529 default: | 599 default: |
530 break; | 600 break; |
531 } | 601 } |
532 } | 602 } |
533 | 603 |
534 static void extract_alpha(const SkMask& dst, | 604 static void extract_alpha(const SkMask& dst, |
535 const SkPMColor* srcRow, size_t srcRB) { | 605 const SkPMColor* srcRow, size_t srcRB) { |
536 int width = dst.fBounds.width(); | 606 int width = dst.fBounds.width(); |
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
855 SkScalerContext* SkTypeface::createScalerContext(const SkDescriptor* desc, | 925 SkScalerContext* SkTypeface::createScalerContext(const SkDescriptor* desc, |
856 bool allowFailure) const { | 926 bool allowFailure) const { |
857 SkScalerContext* c = this->onCreateScalerContext(desc); | 927 SkScalerContext* c = this->onCreateScalerContext(desc); |
858 | 928 |
859 if (!c && !allowFailure) { | 929 if (!c && !allowFailure) { |
860 c = SkNEW_ARGS(SkScalerContext_Empty, | 930 c = SkNEW_ARGS(SkScalerContext_Empty, |
861 (const_cast<SkTypeface*>(this), desc)); | 931 (const_cast<SkTypeface*>(this), desc)); |
862 } | 932 } |
863 return c; | 933 return c; |
864 } | 934 } |
OLD | NEW |