Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(295)

Side by Side Diff: src/effects/SkBlurMask.cpp

Issue 21835004: Blur refactoring (Closed) Base URL: http://skia.googlecode.com/svn/trunk/
Patch Set: Removed unneeded #includes Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/effects/SkBlurMask.h ('k') | src/effects/SkBlurMaskFilter.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 "SkBlurMask.h" 10 #include "SkBlurMask.h"
11 #include "SkMath.h" 11 #include "SkMath.h"
12 #include "SkTemplates.h" 12 #include "SkTemplates.h"
13 #include "SkEndian.h" 13 #include "SkEndian.h"
14 14
15 const SkScalar SkBlurMask::kBlurRadiusFudgeFactor = SkFloatToScalar(.57735f); 15
16 SkScalar SkBlurMask::ConvertRadiusToSigma(SkScalar radius) {
17 // This constant approximates the scaling done in the software path's
18 // "high quality" mode, in SkBlurMask::Blur() (1 / sqrt(3)).
19 // IMHO, it actually should be 1: we blur "less" than we should do
20 // according to the CSS and canvas specs, simply because Safari does the sam e.
21 // Firefox used to do the same too, until 4.0 where they fixed it. So at so me
22 // point we should probably get rid of these scaling constants and rebaselin e
23 // all the blur tests.
24 static const SkScalar kBLUR_SIGMA_SCALE = SkFloatToScalar(0.57735f);
25
26 return radius ? kBLUR_SIGMA_SCALE * radius + 0.5f : 0.0f;
27 }
16 28
17 #define UNROLL_SEPARABLE_LOOPS 29 #define UNROLL_SEPARABLE_LOOPS
18 30
19 /** 31 /**
20 * This function performs a box blur in X, of the given radius. If the 32 * This function performs a box blur in X, of the given radius. If the
21 * "transpose" parameter is true, it will transpose the pixels on write, 33 * "transpose" parameter is true, it will transpose the pixels on write,
22 * such that X and Y are swapped. Reads are always performed from contiguous 34 * such that X and Y are swapped. Reads are always performed from contiguous
23 * memory in X, for speed. The destination buffer (dst) must be at least 35 * memory in X, for speed. The destination buffer (dst) must be at least
24 * (width + leftRadius + rightRadius) * height bytes in size. 36 * (width + leftRadius + rightRadius) * height bytes in size.
25 * 37 *
(...skipping 440 matching lines...) Expand 10 before | Expand all | Expand 10 after
466 478
467 // we use a local function to wrap the class static method to work around 479 // we use a local function to wrap the class static method to work around
468 // a bug in gcc98 480 // a bug in gcc98
469 void SkMask_FreeImage(uint8_t* image); 481 void SkMask_FreeImage(uint8_t* image);
470 void SkMask_FreeImage(uint8_t* image) { 482 void SkMask_FreeImage(uint8_t* image) {
471 SkMask::FreeImage(image); 483 SkMask::FreeImage(image);
472 } 484 }
473 485
474 bool SkBlurMask::Blur(SkMask* dst, const SkMask& src, 486 bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
475 SkScalar radius, Style style, Quality quality, 487 SkScalar radius, Style style, Quality quality,
476 SkIPoint* margin) 488 SkIPoint* margin) {
477 { 489 return SkBlurMask::BoxBlur(dst, src,
490 SkBlurMask::ConvertRadiusToSigma(radius),
491 style, quality, margin);
492 }
493
494 bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src,
495 SkScalar sigma, Style style, Quality quality,
496 SkIPoint* margin) {
478 497
479 if (src.fFormat != SkMask::kA8_Format) { 498 if (src.fFormat != SkMask::kA8_Format) {
480 return false; 499 return false;
481 } 500 }
482 501
483 // Force high quality off for small radii (performance) 502 // Force high quality off for small radii (performance)
484 if (radius < SkIntToScalar(3)) { 503 if (sigma <= SkIntToScalar(2)) {
485 quality = kLow_Quality; 504 quality = kLow_Quality;
486 } 505 }
487 506
507 SkScalar passRadius;
508 if (kHigh_Quality == quality) {
509 // For the high quality path the 3 pass box blur kernel width is
510 // 6*rad+1 while the full Gaussian width is 6*sigma.
511 passRadius = sigma - (1/6.0f);
512 } else {
513 // For the low quality path we only attempt to cover 3*sigma of the
514 // Gaussian blur area (1.5*sigma on each side). The single pass box
515 // blur's kernel size is 2*rad+1.
516 passRadius = 1.5f*sigma - 0.5f;
517 }
518
488 // highQuality: use three box blur passes as a cheap way 519 // highQuality: use three box blur passes as a cheap way
489 // to approximate a Gaussian blur 520 // to approximate a Gaussian blur
490 int passCount = (kHigh_Quality == quality) ? 3 : 1; 521 int passCount = (kHigh_Quality == quality) ? 3 : 1;
491 SkScalar passRadius = (kHigh_Quality == quality) ?
492 SkScalarMul( radius, kBlurRadiusFudgeFactor):
493 radius;
494 522
495 int rx = SkScalarCeil(passRadius); 523 int rx = SkScalarCeil(passRadius);
496 int outerWeight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255 ); 524 int outerWeight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255 );
497 525
498 SkASSERT(rx >= 0); 526 SkASSERT(rx >= 0);
499 SkASSERT((unsigned)outerWeight <= 255); 527 SkASSERT((unsigned)outerWeight <= 255);
500 if (rx <= 0) { 528 if (rx <= 0) {
501 return false; 529 return false;
502 } 530 }
503 531
504 int ry = rx; // only do square blur for now 532 int ry = rx; // only do square blur for now
505 533
506 int padx = passCount * rx; 534 int padx = passCount * rx;
507 int pady = passCount * ry; 535 int pady = passCount * ry;
508 536
509 if (margin) { 537 if (margin) {
510 margin->set(padx, pady); 538 margin->set(padx, pady);
511 } 539 }
512 dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady, 540 dst->fBounds.set(src.fBounds.fLeft - padx, src.fBounds.fTop - pady,
513 src.fBounds.fRight + padx, src.fBounds.fBottom + pady); 541 src.fBounds.fRight + padx, src.fBounds.fBottom + pady);
514 542
515 dst->fRowBytes = dst->fBounds.width(); 543 dst->fRowBytes = dst->fBounds.width();
516 dst->fFormat = SkMask::kA8_Format; 544 dst->fFormat = SkMask::kA8_Format;
517 dst->fImage = NULL; 545 dst->fImage = NULL;
518 546
519 if (src.fImage) { 547 if (src.fImage) {
520 size_t dstSize = dst->computeImageSize(); 548 size_t dstSize = dst->computeImageSize();
521 if (0 == dstSize) { 549 if (0 == dstSize) {
522 return false; // too big to allocate, abort 550 return false; // too big to allocate, abort
523 } 551 }
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
644 672
645 if ( x > 0.5f ) { 673 if ( x > 0.5f ) {
646 return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x); 674 return 0.5625f - (x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
647 } 675 }
648 if ( x > -0.5f ) { 676 if ( x > -0.5f ) {
649 return 0.5f - (0.75f * x - x3 / 3.0f); 677 return 0.5f - (0.75f * x - x3 / 3.0f);
650 } 678 }
651 return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x); 679 return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
652 } 680 }
653 681
654 // Compute the size of the array allocated for the profile.
655
656 static int compute_profile_size(SkScalar radius) {
657 return SkScalarRoundToInt(radius * 3);
658
659 }
660
661 /* compute_profile allocates and fills in an array of floating 682 /* compute_profile allocates and fills in an array of floating
662 point values between 0 and 255 for the profile signature of 683 point values between 0 and 255 for the profile signature of
663 a blurred half-plane with the given blur radius. Since we're 684 a blurred half-plane with the given blur radius. Since we're
664 going to be doing screened multiplications (i.e., 1 - (1-x)(1-y)) 685 going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
665 all the time, we actually fill in the profile pre-inverted 686 all the time, we actually fill in the profile pre-inverted
666 (already done 255-x). 687 (already done 255-x).
667 688
668 It's the responsibility of the caller to delete the 689 It's the responsibility of the caller to delete the
669 memory returned in profile_out. 690 memory returned in profile_out.
670 */ 691 */
671 692
672 static void compute_profile(SkScalar radius, unsigned int **profile_out) { 693 static void compute_profile(SkScalar sigma, unsigned int **profile_out) {
673 int size = compute_profile_size(radius); 694 int size = SkScalarCeilToInt(6*sigma);
674 695
675 int center = size >> 1; 696 int center = size >> 1;
676 unsigned int *profile = SkNEW_ARRAY(unsigned int, size); 697 unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
677 698
678 float invr = 1.f/radius; 699 float invr = 1.f/(2*sigma);
679 700
680 profile[0] = 255; 701 profile[0] = 255;
681 for (int x = 1 ; x < size ; ++x) { 702 for (int x = 1 ; x < size ; ++x) {
682 float scaled_x = (center - x - .5f) * invr; 703 float scaled_x = (center - x - .5f) * invr;
683 float gi = gaussianIntegral(scaled_x); 704 float gi = gaussianIntegral(scaled_x);
684 profile[x] = 255 - (uint8_t) (255.f * gi); 705 profile[x] = 255 - (uint8_t) (255.f * gi);
685 } 706 }
686 707
687 *profile_out = profile; 708 *profile_out = profile;
688 } 709 }
689 710
690 // TODO MAYBE: Maintain a profile cache to avoid recomputing this for 711 // TODO MAYBE: Maintain a profile cache to avoid recomputing this for
691 // commonly used radii. Consider baking some of the most common blur radii 712 // commonly used radii. Consider baking some of the most common blur radii
692 // directly in as static data? 713 // directly in as static data?
693 714
694 // Implementation adapted from Michael Herf's approach: 715 // Implementation adapted from Michael Herf's approach:
695 // http://stereopsis.com/shadowrect/ 716 // http://stereopsis.com/shadowrect/
696 717
697 static inline unsigned int profile_lookup( unsigned int *profile, int loc, int b lurred_width, int sharp_width ) { 718 static inline unsigned int profile_lookup( unsigned int *profile, int loc, int b lurred_width, int sharp_width ) {
698 int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge? 719 int dx = SkAbs32(((loc << 1) + 1) - blurred_width) - sharp_width; // how far are we from the original edge?
699 int ox = dx >> 1; 720 int ox = dx >> 1;
700 if (ox < 0) { 721 if (ox < 0) {
701 ox = 0; 722 ox = 0;
702 } 723 }
703 724
704 return profile[ox]; 725 return profile[ox];
705 } 726 }
706 727
707 bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src, 728 bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
708 SkScalar provided_radius, Style style, 729 SkScalar radius, Style style,
709 SkIPoint *margin, SkMask::CreateMode createMode) { 730 SkIPoint *margin, SkMask::CreateMode createMode) {
710 int profile_size; 731 return SkBlurMask::BlurRect(SkBlurMask::ConvertRadiusToSigma(radius),
732 dst, src,
733 style, margin, createMode);
734 }
711 735
712 float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudge Factor)); 736 bool SkBlurMask::BlurRect(SkScalar sigma, SkMask *dst,
713 737 const SkRect &src, Style style,
714 // adjust blur radius to match interpretation from boxfilter code 738 SkIPoint *margin, SkMask::CreateMode createMode) {
715 radius = (radius + .5f) * 2.f; 739 int profile_size = SkScalarCeilToInt(6*sigma);
716
717 profile_size = compute_profile_size(radius);
718 740
719 int pad = profile_size/2; 741 int pad = profile_size/2;
720 if (margin) { 742 if (margin) {
721 margin->set( pad, pad ); 743 margin->set( pad, pad );
722 } 744 }
723 745
724 dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad), 746 dst->fBounds.set(SkScalarRoundToInt(src.fLeft - pad),
725 SkScalarRoundToInt(src.fTop - pad), 747 SkScalarRoundToInt(src.fTop - pad),
726 SkScalarRoundToInt(src.fRight + pad), 748 SkScalarRoundToInt(src.fRight + pad),
727 SkScalarRoundToInt(src.fBottom + pad)); 749 SkScalarRoundToInt(src.fBottom + pad));
(...skipping 10 matching lines...) Expand all
738 dst->fBounds.set(SkScalarRoundToInt(src.fLeft), 760 dst->fBounds.set(SkScalarRoundToInt(src.fLeft),
739 SkScalarRoundToInt(src.fTop), 761 SkScalarRoundToInt(src.fTop),
740 SkScalarRoundToInt(src.fRight), 762 SkScalarRoundToInt(src.fRight),
741 SkScalarRoundToInt(src.fBottom)); // restore trimme d bounds 763 SkScalarRoundToInt(src.fBottom)); // restore trimme d bounds
742 dst->fRowBytes = sw; 764 dst->fRowBytes = sw;
743 } 765 }
744 return true; 766 return true;
745 } 767 }
746 unsigned int *profile = NULL; 768 unsigned int *profile = NULL;
747 769
748 compute_profile(radius, &profile); 770 compute_profile(sigma, &profile);
749 SkAutoTDeleteArray<unsigned int> ada(profile); 771 SkAutoTDeleteArray<unsigned int> ada(profile);
750 772
751 size_t dstSize = dst->computeImageSize(); 773 size_t dstSize = dst->computeImageSize();
752 if (0 == dstSize) { 774 if (0 == dstSize) {
753 return false; // too big to allocate, abort 775 return false; // too big to allocate, abort
754 } 776 }
755 777
756 uint8_t* dp = SkMask::AllocImage(dstSize); 778 uint8_t* dp = SkMask::AllocImage(dstSize);
757 779
758 dst->fImage = dp; 780 dst->fImage = dp;
759 781
760 int dstHeight = dst->fBounds.height(); 782 int dstHeight = dst->fBounds.height();
761 int dstWidth = dst->fBounds.width(); 783 int dstWidth = dst->fBounds.width();
762 784
763 // nearest odd number less than the profile size represents the center 785 // nearest odd number less than the profile size represents the center
764 // of the (2x scaled) profile 786 // of the (2x scaled) profile
765 int center = ( profile_size & ~1 ) - 1; 787 int center = ( profile_size & ~1 ) - 1;
766 788
767 int w = sw - center; 789 int w = sw - center;
768 int h = sh - center; 790 int h = sh - center;
769 791
770 uint8_t *outptr = dp; 792 uint8_t *outptr = dp;
771 793
772 SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth); 794 SkAutoTMalloc<uint8_t> horizontalScanline(dstWidth);
773 795
774 for (int x = 0 ; x < dstWidth ; ++x) { 796 for (int x = 0 ; x < dstWidth ; ++x) {
775 if (profile_size <= sw) { 797 if (profile_size <= sw) {
776 horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w); 798 horizontalScanline[x] = profile_lookup(profile, x, dstWidth, w);
777 } else { 799 } else {
778 float span = float(sw)/radius; 800 float span = float(sw)/(2*sigma);
779 float giX = 1.5f - (x+.5f)/radius; 801 float giX = 1.5f - (x+.5f)/(2*sigma);
780 horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - ga ussianIntegral(giX + span))); 802 horizontalScanline[x] = (uint8_t) (255 * (gaussianIntegral(giX) - ga ussianIntegral(giX + span)));
781 } 803 }
782 } 804 }
783 805
784 for (int y = 0 ; y < dstHeight ; ++y) { 806 for (int y = 0 ; y < dstHeight ; ++y) {
785 unsigned int profile_y; 807 unsigned int profile_y;
786 if (profile_size <= sh) { 808 if (profile_size <= sh) {
787 profile_y = profile_lookup(profile, y, dstHeight, h); 809 profile_y = profile_lookup(profile, y, dstHeight, h);
788 } else { 810 } else {
789 float span = float(sh)/radius; 811 float span = float(sh)/(2*sigma);
790 float giY = 1.5f - (y+.5f)/radius; 812 float giY = 1.5f - (y+.5f)/(2*sigma);
791 profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegr al(giY + span))); 813 profile_y = (uint8_t) (255 * (gaussianIntegral(giY) - gaussianIntegr al(giY + span)));
792 } 814 }
793 815
794 for (int x = 0 ; x < dstWidth ; x++) { 816 for (int x = 0 ; x < dstWidth ; x++) {
795 unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profi le_y); 817 unsigned int maskval = SkMulDiv255Round(horizontalScanline[x], profi le_y);
796 *(outptr++) = maskval; 818 *(outptr++) = maskval;
797 } 819 }
798 } 820 }
799 821
800 if (style == kInner_Style) { 822 if (style == kInner_Style) {
(...skipping 26 matching lines...) Expand all
827 uint8_t *dst_scanline = dp + y*dstWidth + pad; 849 uint8_t *dst_scanline = dp + y*dstWidth + pad;
828 memset(dst_scanline, 0xff, sw); 850 memset(dst_scanline, 0xff, sw);
829 } 851 }
830 } 852 }
831 // normal and solid styles are the same for analytic rect blurs, so don't 853 // normal and solid styles are the same for analytic rect blurs, so don't
832 // need to handle solid specially. 854 // need to handle solid specially.
833 855
834 return true; 856 return true;
835 } 857 }
836 858
859 bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar radius ,
860 Style style, SkIPoint* margin) {
861 return BlurGroundTruth(ConvertRadiusToSigma(radius), dst, src, style, margin );
862 }
837 // The "simple" blur is a direct implementation of separable convolution with a discrete 863 // The "simple" blur is a direct implementation of separable convolution with a discrete
838 // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but ve ry 864 // gaussian kernel. It's "ground truth" in a sense; too slow to be used, but ve ry
839 // useful for correctness comparisons. 865 // useful for correctness comparisons.
840 866
841 bool SkBlurMask::BlurGroundTruth(SkMask* dst, const SkMask& src, SkScalar provid ed_radius, 867 bool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src,
842 Style style, SkIPoint* margin) { 868 Style style, SkIPoint* margin) {
843 869
844 if (src.fFormat != SkMask::kA8_Format) { 870 if (src.fFormat != SkMask::kA8_Format) {
845 return false; 871 return false;
846 } 872 }
847 873
848 float radius = SkScalarToFloat(SkScalarMul(provided_radius, kBlurRadiusFudge Factor)); 874 float variance = sigma * sigma;
849 float stddev = SkScalarToFloat(radius) /2.0f;
850 float variance = stddev * stddev;
851 875
852 int windowSize = SkScalarCeil(stddev*4); 876 int windowSize = SkScalarCeil(sigma*4);
853 // round window size up to nearest odd number 877 // round window size up to nearest odd number
854 windowSize |= 1; 878 windowSize |= 1;
855 879
856 SkAutoTMalloc<float> gaussWindow(windowSize); 880 SkAutoTMalloc<float> gaussWindow(windowSize);
857 881
858 int halfWindow = windowSize >> 1; 882 int halfWindow = windowSize >> 1;
859 883
860 gaussWindow[halfWindow] = 1; 884 gaussWindow[halfWindow] = 1;
861 885
862 float windowSum = 1; 886 float windowSum = 1;
(...skipping 113 matching lines...) Expand 10 before | Expand all | Expand 10 after
976 (void)autoCall.detach(); 1000 (void)autoCall.detach();
977 } 1001 }
978 1002
979 if (style == kInner_Style) { 1003 if (style == kInner_Style) {
980 dst->fBounds = src.fBounds; // restore trimmed bounds 1004 dst->fBounds = src.fBounds; // restore trimmed bounds
981 dst->fRowBytes = src.fRowBytes; 1005 dst->fRowBytes = src.fRowBytes;
982 } 1006 }
983 1007
984 return true; 1008 return true;
985 } 1009 }
OLDNEW
« no previous file with comments | « src/effects/SkBlurMask.h ('k') | src/effects/SkBlurMaskFilter.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698