Non-normal blur styles to work with non-a8.

BUG=skia:7941

Change-Id: I500561c2fec1bcab324fd9519f1d20d356afdfb5
Reviewed-on: https://skia-review.googlesource.com/127500
Commit-Queue: Ben Wagner <bungeman@google.com>
Reviewed-by: Herb Derby <herb@google.com>
This commit is contained in:
Ben Wagner 2018-05-10 15:24:20 -04:00 committed by Skia Commit-Bot
parent 2128b4ac44
commit e3f1b59d9f
4 changed files with 478 additions and 220 deletions

View File

@ -8,8 +8,10 @@
#include "gm.h"
#include "sk_tool_utils.h"
#include "Resources.h"
#include "SkBlurTypes.h"
#include "SkCanvas.h"
#include "SkFontStyle.h"
#include "SkMaskFilter.h"
#include "SkString.h"
#include "SkSurfaceProps.h"
#include "SkTypeface.h"
@ -150,58 +152,60 @@ DEF_GM( return new TypefaceStylesGM(true); )
static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
char character = 'A') {
struct AliasType {
bool antiAlias;
bool subpixelAntitalias;
bool inLayer;
} constexpr aliasTypes[] {
struct AliasType {
bool antiAlias;
bool subpixelAntitalias;
bool inLayer;
} constexpr aliasTypes[] {
#ifndef SK_BUILD_FOR_IOS
// This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
// The crash looks like
// libTrueTypeScaler.dylib`<redacted> + 80
// stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
// -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36]
// 0x330b19d4 <+84>: movs r3, #0x0
// 0x330b19d6 <+86>: add r2, sp, #0x28
// 0x330b19d8 <+88>: ldr r0, [r4, #0x4]
// Disable testing embedded bitmaps on iOS for now.
// See https://bug.skia.org/5530 .
{ false, false, false }, // aliased
// This gm crashes on iOS when drawing an embedded bitmap when requesting aliased rendering.
// The crash looks like
// libTrueTypeScaler.dylib`<redacted> + 80
// stop reason = EXC_BAD_ACCESS (code=EXC_ARM_DA_ALIGN, address=...)
// -> 0x330b19d0 <+80>: strd r2, r3, [r5, #36]
// 0x330b19d4 <+84>: movs r3, #0x0
// 0x330b19d6 <+86>: add r2, sp, #0x28
// 0x330b19d8 <+88>: ldr r0, [r4, #0x4]
// Disable testing embedded bitmaps on iOS for now.
// See https://bug.skia.org/5530 .
{ false, false, false }, // aliased
#endif
{ true, false, false }, // anti-aliased
{ true, true , false }, // subpixel anti-aliased
{ true, false, true }, // anti-aliased in layer (flat pixel geometry)
{ true, true , true }, // subpixel anti-aliased in layer (flat pixel geometry)
};
{ true, false, false }, // anti-aliased
{ true, true , false }, // subpixel anti-aliased
{ true, false, true }, // anti-aliased in layer (flat pixel geometry)
{ true, true , true }, // subpixel anti-aliased in layer (flat pixel geometry)
};
// The hintgasp.ttf is designed for the following sizes to be different.
// GASP_DOGRAY 0x0002 0<=ppem<=10
// GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10
// GASP_GRIDFIT 0x0001 11<=ppem<=12
// GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12
// GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14
// GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14
// (neither) 0x0000 15<=ppem
// Odd sizes have embedded bitmaps.
constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
// The hintgasp.ttf is designed for the following sizes to be different.
// GASP_DOGRAY 0x0002 0<=ppem<=10
// GASP_SYMMETRIC_SMOOTHING 0x0008 0<=ppem<=10
// GASP_GRIDFIT 0x0001 11<=ppem<=12
// GASP_SYMMETRIC_GRIDFIT 0x0004 11<=ppem<=12
// GASP_DOGRAY|GASP_GRIDFIT 0x0003 13<=ppem<=14
// GASP_SYMMETRIC_SMOOTHING|GASP_SYMMETRIC_GRIDFIT 0x000C 13<=ppem<=14
// (neither) 0x0000 15<=ppem
// Odd sizes have embedded bitmaps.
constexpr SkScalar textSizes[] = { 9, 10, 11, 12, 13, 14, 15, 16 };
constexpr SkPaint::Hinting hintingTypes[] = { SkPaint::kNo_Hinting,
SkPaint::kSlight_Hinting,
SkPaint::kNormal_Hinting,
SkPaint::kFull_Hinting };
constexpr SkPaint::Hinting hintingTypes[] = { SkPaint::kNo_Hinting,
SkPaint::kSlight_Hinting,
SkPaint::kNormal_Hinting,
SkPaint::kFull_Hinting };
struct SubpixelType {
bool requested;
SkVector offset;
} constexpr subpixelTypes[] = {
{ false, { 0.00, 0.00 } },
{ true , { 0.00, 0.00 } },
{ true , { 0.25, 0.00 } },
{ true , { 0.25, 0.25 } },
};
struct SubpixelType {
bool requested;
SkVector offset;
} constexpr subpixelTypes[] = {
{ false, { 0.00, 0.00 } },
{ true , { 0.00, 0.00 } },
{ true , { 0.25, 0.00 } },
{ true , { 0.25, 0.25 } },
};
constexpr bool rotateABitTypes[] = { false, true };
constexpr bool rotateABitTypes[] = { false, true };
SkScalar y = 0; // The baseline of the previous output
{
SkPaint paint;
paint.setTypeface(face);
paint.setEmbeddedBitmapText(true);
@ -209,7 +213,6 @@ static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
SkScalar x = 0;
SkScalar xMax = x;
SkScalar xBase = 0;
SkScalar y = 0; // The baseline of the previous output
for (const SubpixelType subpixel : subpixelTypes) {
y = 0;
paint.setSubpixelText(subpixel.requested);
@ -237,9 +240,8 @@ static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
canvas->rotate(2, x + subpixel.offset.x(),
y + subpixel.offset.y());
}
canvas->drawText(&character, 1,
x + subpixel.offset.x(),
y + subpixel.offset.y(), paint);
canvas->drawText(&character, 1, x + subpixel.offset.x(),
y + subpixel.offset.y(), paint);
SkScalar dx = SkScalarCeilToScalar(
paint.measureText(&character, 1)) + 5;
@ -252,9 +254,105 @@ static void draw_typeface_rendering_gm(SkCanvas* canvas, sk_sp<SkTypeface> face,
}
xBase = xMax;
}
}
constexpr struct StyleTests {
SkPaint::Style style;
SkScalar strokeWidth;
} styleTypes[] = {
{ SkPaint::kFill_Style, 0.0f},
{ SkPaint::kStroke_Style, 0.0f},
{ SkPaint::kStroke_Style, 0.5f},
{ SkPaint::kStrokeAndFill_Style, 1.0f},
};
constexpr bool fakeBoldTypes[] = { false, true };
{
SkPaint paint;
paint.setTypeface(face);
paint.setTextSize(16);
SkScalar x = 0;
for (const bool& fakeBold : fakeBoldTypes) {
SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr));
y += dy;
x = 5;
paint.setFakeBoldText(fakeBold);
for (const AliasType& alias : aliasTypes) {
paint.setAntiAlias(alias.antiAlias);
paint.setLCDRenderText(alias.subpixelAntitalias);
SkAutoCanvasRestore acr(canvas, false);
if (alias.inLayer) {
canvas->saveLayer(nullptr, &paint);
}
for (const StyleTests& style : styleTypes) {
paint.setStyle(style.style);
paint.setStrokeWidth(style.strokeWidth);
canvas->drawText(&character, 1, x, y, paint);
SkScalar dx = SkScalarCeilToScalar(paint.measureText(&character, 1)) + 5;
x += dx;
}
}
y += 10;
}
}
constexpr struct MaskTests {
SkBlurStyle style;
SkScalar sigma;
} maskTypes[] = {
{ SkBlurStyle::kNormal_SkBlurStyle, 0.0f},
{ SkBlurStyle::kSolid_SkBlurStyle, 0.0f},
{ SkBlurStyle::kOuter_SkBlurStyle, 0.0f},
{ SkBlurStyle::kInner_SkBlurStyle, 0.0f},
{ SkBlurStyle::kNormal_SkBlurStyle, 0.5f},
{ SkBlurStyle::kSolid_SkBlurStyle, 0.5f},
{ SkBlurStyle::kOuter_SkBlurStyle, 0.5f},
{ SkBlurStyle::kInner_SkBlurStyle, 0.5f},
{ SkBlurStyle::kNormal_SkBlurStyle, 2.0f},
{ SkBlurStyle::kSolid_SkBlurStyle, 2.0f},
{ SkBlurStyle::kOuter_SkBlurStyle, 2.0f},
{ SkBlurStyle::kInner_SkBlurStyle, 2.0f},
};
{
SkPaint paint;
paint.setTypeface(face);
paint.setTextSize(16);
SkScalar x = 0;
{
for (const AliasType& alias : aliasTypes) {
SkScalar dy = SkScalarCeilToScalar(paint.getFontMetrics(nullptr));
y += dy;
x = 5;
paint.setAntiAlias(alias.antiAlias);
paint.setLCDRenderText(alias.subpixelAntitalias);
SkAutoCanvasRestore acr(canvas, false);
if (alias.inLayer) {
canvas->saveLayer(nullptr, &paint);
}
for (const MaskTests& mask : maskTypes) {
paint.setMaskFilter(SkMaskFilter::MakeBlur(mask.style, mask.sigma));
canvas->drawText(&character, 1, x, y, paint);
SkScalar dx = SkScalarCeilToScalar(paint.measureText(&character, 1)) + 5;
x += dx;
}
paint.setMaskFilter(nullptr);
}
y += 10;
}
}
}
DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 680, SK_ColorWHITE,
DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 840, SK_ColorWHITE,
SkStringPrintf("typefacerendering%s",
sk_tool_utils::platform_font_manager())) {
if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/hintgasp.ttf")) {
@ -265,7 +363,7 @@ DEF_SIMPLE_GM_BG_NAME(typefacerendering, canvas, 640, 680, SK_ColorWHITE,
// Type1 fonts don't currently work in Skia on Windows.
#ifndef SK_BUILD_FOR_WIN
DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 680, SK_ColorWHITE,
DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 840, SK_ColorWHITE,
SkStringPrintf("typefacerendering_pfa%s",
sk_tool_utils::platform_font_manager())) {
if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfa")) {
@ -274,7 +372,7 @@ DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfa, canvas, 640, 680, SK_ColorWHITE,
}
}
DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfb, canvas, 640, 680, SK_ColorWHITE,
DEF_SIMPLE_GM_BG_NAME(typefacerendering_pfb, canvas, 640, 840, SK_ColorWHITE,
SkStringPrintf("typefacerendering_pfb%s",
sk_tool_utils::platform_font_manager())) {
if (sk_sp<SkTypeface> face = MakeResourceAsTypeface("fonts/Roboto2-Regular.pfb")) {

View File

@ -32,60 +32,65 @@ SkScalar SkBlurMask::ConvertSigmaToRadius(SkScalar sigma) {
}
template <typename AlphaIter>
static void merge_src_with_blur(uint8_t dst[], int dstRB,
const uint8_t src[], int srcRB,
AlphaIter src, int srcRB,
const uint8_t blur[], int blurRB,
int sw, int sh) {
dstRB -= sw;
srcRB -= sw;
blurRB -= sw;
while (--sh >= 0) {
AlphaIter rowSrc(src);
for (int x = sw - 1; x >= 0; --x) {
*dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*src)));
dst += 1;
src += 1;
blur += 1;
*dst = SkToU8(SkAlphaMul(*blur, SkAlpha255To256(*rowSrc)));
++dst;
++rowSrc;
++blur;
}
dst += dstRB;
src += srcRB;
src >>= srcRB;
blur += blurRB;
}
}
static void clamp_with_orig(uint8_t dst[], int dstRowBytes,
const uint8_t src[], int srcRowBytes,
int sw, int sh,
SkBlurStyle style) {
template <typename AlphaIter>
static void clamp_solid_with_orig(uint8_t dst[], int dstRowBytes,
AlphaIter src, int srcRowBytes,
int sw, int sh) {
int x;
while (--sh >= 0) {
switch (style) {
case kSolid_SkBlurStyle:
for (x = sw - 1; x >= 0; --x) {
int s = *src;
int d = *dst;
*dst = SkToU8(s + d - SkMulDiv255Round(s, d));
dst += 1;
src += 1;
}
break;
case kOuter_SkBlurStyle:
for (x = sw - 1; x >= 0; --x) {
if (*src) {
*dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - *src)));
}
dst += 1;
src += 1;
}
break;
default:
SkDEBUGFAIL("Unexpected blur style here");
break;
AlphaIter rowSrc(src);
for (x = sw - 1; x >= 0; --x) {
int s = *rowSrc;
int d = *dst;
*dst = SkToU8(s + d - SkMulDiv255Round(s, d));
++dst;
++rowSrc;
}
dst += dstRowBytes - sw;
src += srcRowBytes - sw;
src >>= srcRowBytes;
}
}
template <typename AlphaIter>
static void clamp_outer_with_orig(uint8_t dst[], int dstRowBytes,
AlphaIter src, int srcRowBytes,
int sw, int sh) {
int x;
while (--sh >= 0) {
AlphaIter rowSrc(src);
for (x = sw - 1; x >= 0; --x) {
int srcValue = *rowSrc;
if (srcValue) {
*dst = SkToU8(SkAlphaMul(*dst, SkAlpha255To256(255 - srcValue)));
}
++dst;
++rowSrc;
}
dst += dstRowBytes - sw;
src >>= srcRowBytes;
}
}
///////////////////////////////////////////////////////////////////////////////
// we use a local function to wrap the class static method to work around
@ -105,53 +110,147 @@ bool SkBlurMask::BoxBlur(SkMask* dst, const SkMask& src, SkScalar sigma, SkBlurS
return false;
}
SkIPoint border;
SkMaskBlurFilter blurFilter{sigma, sigma};
if (blurFilter.hasNoBlur()) {
if (blurFilter.hasNoBlur() && style != kOuter_SkBlurStyle) {
return false;
}
border = blurFilter.blur(src, dst);
const SkIPoint border = blurFilter.blur(src, dst);
// If src.fImage is null, then this call is only to calculate the border.
if (src.fImage != nullptr && dst->fImage == nullptr) {
return false;
}
if (src.fImage != nullptr) {
// if need be, alloc the "real" dst (same size as src) and copy/merge
// the blur into it (applying the src)
if (style == kInner_SkBlurStyle) {
// now we allocate the "real" dst, mirror the size of src
size_t srcSize = src.computeImageSize();
if (0 == srcSize) {
return false; // too big to allocate, abort
}
auto blur = dst->fImage;
dst->fImage = SkMask::AllocImage(srcSize);
auto blurStart = &blur[border.x() + border.y() * dst->fRowBytes];
merge_src_with_blur(dst->fImage, src.fRowBytes,
src.fImage, src.fRowBytes,
blurStart,
dst->fRowBytes,
src.fBounds.width(), src.fBounds.height());
SkMask::FreeImage(blur);
} else if (style != kNormal_SkBlurStyle) {
auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
clamp_with_orig(dstStart,
dst->fRowBytes, src.fImage, src.fRowBytes,
src.fBounds.width(), src.fBounds.height(), style);
}
}
if (style == kInner_SkBlurStyle) {
dst->fBounds = src.fBounds; // restore trimmed bounds
dst->fRowBytes = src.fRowBytes;
}
if (margin != nullptr) {
*margin = border;
}
if (src.fImage == nullptr) {
if (style == kInner_SkBlurStyle) {
dst->fBounds = src.fBounds; // restore trimmed bounds
dst->fRowBytes = dst->fBounds.width();
}
return true;
}
switch (style) {
case kNormal_SkBlurStyle:
break;
case kSolid_SkBlurStyle: {
auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
switch (src.fFormat) {
case SkMask::kBW_Format:
clamp_solid_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
break;
case SkMask::kA8_Format:
clamp_solid_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
break;
case SkMask::kARGB32_Format: {
uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
clamp_solid_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
} break;
case SkMask::kLCD16_Format: {
uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
clamp_solid_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
} break;
default:
SK_ABORT("Unhandled format.");
};
} break;
case kOuter_SkBlurStyle: {
auto dstStart = &dst->fImage[border.x() + border.y() * dst->fRowBytes];
switch (src.fFormat) {
case SkMask::kBW_Format:
clamp_outer_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
break;
case SkMask::kA8_Format:
clamp_outer_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
break;
case SkMask::kARGB32_Format: {
uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
clamp_outer_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
} break;
case SkMask::kLCD16_Format: {
uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
clamp_outer_with_orig(
dstStart, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
src.fBounds.width(), src.fBounds.height());
} break;
default:
SK_ABORT("Unhandled format.");
};
} break;
case kInner_SkBlurStyle: {
// now we allocate the "real" dst, mirror the size of src
SkMask blur = *dst;
dst->fBounds = src.fBounds;
dst->fRowBytes = dst->fBounds.width();
size_t dstSize = dst->computeImageSize();
if (0 == dstSize) {
return false; // too big to allocate, abort
}
dst->fImage = SkMask::AllocImage(dstSize);
auto blurStart = &blur.fImage[border.x() + border.y() * blur.fRowBytes];
switch (src.fFormat) {
case SkMask::kBW_Format:
merge_src_with_blur(
dst->fImage, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kBW_Format>(src.fImage, 0), src.fRowBytes,
blurStart, blur.fRowBytes,
src.fBounds.width(), src.fBounds.height());
break;
case SkMask::kA8_Format:
merge_src_with_blur(
dst->fImage, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kA8_Format>(src.fImage), src.fRowBytes,
blurStart, blur.fRowBytes,
src.fBounds.width(), src.fBounds.height());
break;
case SkMask::kARGB32_Format: {
uint32_t* srcARGB = reinterpret_cast<uint32_t*>(src.fImage);
merge_src_with_blur(
dst->fImage, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kARGB32_Format>(srcARGB), src.fRowBytes,
blurStart, blur.fRowBytes,
src.fBounds.width(), src.fBounds.height());
} break;
case SkMask::kLCD16_Format: {
uint16_t* srcLCD = reinterpret_cast<uint16_t*>(src.fImage);
merge_src_with_blur(
dst->fImage, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kLCD16_Format>(srcLCD), src.fRowBytes,
blurStart, blur.fRowBytes,
src.fBounds.width(), src.fBounds.height());
} break;
default:
SK_ABORT("Unhandled format.");
};
SkMask::FreeImage(blur.fImage);
} break;
}
return true;
}
@ -509,24 +608,35 @@ bool SkBlurMask::BlurGroundTruth(SkScalar sigma, SkMask* dst, const SkMask& src,
}
dst->fImage = dstPixels;
// if need be, alloc the "real" dst (same size as src) and copy/merge
// the blur into it (applying the src)
if (style == kInner_SkBlurStyle) {
// now we allocate the "real" dst, mirror the size of src
size_t srcSize = src.computeImageSize();
if (0 == srcSize) {
return false; // too big to allocate, abort
}
dst->fImage = SkMask::AllocImage(srcSize);
merge_src_with_blur(dst->fImage, src.fRowBytes,
srcPixels, src.fRowBytes,
dstPixels + pad*dst->fRowBytes + pad,
dst->fRowBytes, srcWidth, srcHeight);
SkMask::FreeImage(dstPixels);
} else if (style != kNormal_SkBlurStyle) {
clamp_with_orig(dstPixels + pad*dst->fRowBytes + pad,
dst->fRowBytes, srcPixels, src.fRowBytes, srcWidth, srcHeight, style);
}
switch (style) {
case kNormal_SkBlurStyle:
break;
case kSolid_SkBlurStyle: {
clamp_solid_with_orig(
dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
srcWidth, srcHeight);
} break;
case kOuter_SkBlurStyle: {
clamp_outer_with_orig(
dstPixels + pad*dst->fRowBytes + pad, dst->fRowBytes,
SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
srcWidth, srcHeight);
} break;
case kInner_SkBlurStyle: {
// now we allocate the "real" dst, mirror the size of src
size_t srcSize = src.computeImageSize();
if (0 == srcSize) {
return false; // too big to allocate, abort
}
dst->fImage = SkMask::AllocImage(srcSize);
merge_src_with_blur(dst->fImage, src.fRowBytes,
SkMask::AlphaIter<SkMask::kA8_Format>(srcPixels), src.fRowBytes,
dstPixels + pad*dst->fRowBytes + pad,
dst->fRowBytes, srcWidth, srcHeight);
SkMask::FreeImage(dstPixels);
} break;
};
(void)autoCall.release();
}

View File

@ -10,7 +10,9 @@
#ifndef SkMask_DEFINED
#define SkMask_DEFINED
#include "SkColorPriv.h"
#include "SkRect.h"
#include "SkTemplates.h"
/** \class SkMask
SkMask is used to describe alpha bitmaps, either 1bit, 8bit, or
@ -128,12 +130,102 @@ struct SkMask {
kComputeBoundsAndRenderImage_CreateMode //!< compute bounds, alloc image and render into it
};
/** Iterates over the coverage values along a scanline in a given SkMask::Format. Provides
* constructor, copy constructor for creating
* operator++, operator-- for iterating over the coverage values on a scanline
* operator>>= to add row bytes
* operator* to get the coverage value at the current location
* operator< to compare two iterators
*/
template <Format F> struct AlphaIter;
/**
* Returns initial destination mask data padded by radiusX and radiusY
*/
static SkMask PrepareDestination(int radiusX, int radiusY, const SkMask& src);
};
template <> struct SkMask::AlphaIter<SkMask::kBW_Format> {
AlphaIter(const uint8_t* ptr, int offset) : fPtr(ptr), fOffset(7 - offset) {}
AlphaIter(const AlphaIter& that) : fPtr(that.fPtr), fOffset(that.fOffset) {}
AlphaIter& operator++() {
if (0 < fOffset ) {
--fOffset;
} else {
++fPtr;
fOffset = 7;
}
return *this;
}
AlphaIter& operator--() {
if (fOffset < 7) {
++fOffset;
} else {
--fPtr;
fOffset = 0;
}
return *this;
}
AlphaIter& operator>>=(uint32_t rb) {
fPtr = SkTAddOffset<const uint8_t>(fPtr, rb);
return *this;
}
uint8_t operator*() const { return ((*fPtr) >> fOffset) & 1 ? 0xFF : 0; }
bool operator<(const AlphaIter& that) const {
return fPtr < that.fPtr || (fPtr == that.fPtr && fOffset > that.fOffset);
}
const uint8_t* fPtr;
int fOffset;
};
template <> struct SkMask::AlphaIter<SkMask::kA8_Format> {
AlphaIter(const uint8_t* ptr) : fPtr(ptr) {}
AlphaIter(const AlphaIter& that) : fPtr(that.fPtr) {}
AlphaIter& operator++() { ++fPtr; return *this; }
AlphaIter& operator--() { --fPtr; return *this; }
AlphaIter& operator>>=(uint32_t rb) {
fPtr = SkTAddOffset<const uint8_t>(fPtr, rb);
return *this;
}
uint8_t operator*() const { return *fPtr; }
bool operator<(const AlphaIter& that) const { return fPtr < that.fPtr; }
const uint8_t* fPtr;
};
template <> struct SkMask::AlphaIter<SkMask::kARGB32_Format> {
AlphaIter(const uint32_t* ptr) : fPtr(ptr) {}
AlphaIter(const AlphaIter& that) : fPtr(that.fPtr) {}
AlphaIter& operator++() { ++fPtr; return *this; }
AlphaIter& operator--() { --fPtr; return *this; }
AlphaIter& operator>>=(uint32_t rb) {
fPtr = SkTAddOffset<const uint32_t>(fPtr, rb);
return *this;
}
uint8_t operator*() const { return SkGetPackedA32(*fPtr); }
bool operator<(const AlphaIter& that) const { return fPtr < that.fPtr; }
const uint32_t* fPtr;
};
template <> struct SkMask::AlphaIter<SkMask::kLCD16_Format> {
AlphaIter(const uint16_t* ptr) : fPtr(ptr) {}
AlphaIter(const AlphaIter& that) : fPtr(that.fPtr) {}
AlphaIter& operator++() { ++fPtr; return *this; }
AlphaIter& operator--() { --fPtr; return *this; }
AlphaIter& operator>>=(uint32_t rb) {
fPtr = SkTAddOffset<const uint16_t>(fPtr, rb);
return *this;
}
uint8_t operator*() const {
unsigned packed = *fPtr;
unsigned r = SkPacked16ToR32(packed);
unsigned g = SkPacked16ToG32(packed);
unsigned b = SkPacked16ToB32(packed);
return (r + g + b) / 3;
}
bool operator<(const AlphaIter& that) const { return fPtr < that.fPtr; }
const uint16_t* fPtr;
};
///////////////////////////////////////////////////////////////////////////////
/**

View File

@ -11,6 +11,7 @@
#include "SkMalloc.h"
#include "SkMaskBlurFilter.h"
#include "SkNx.h"
#include "SkTemplates.h"
#include <cmath>
#include <climits>
@ -959,63 +960,6 @@ static SkIPoint small_blur(double sigmaX, double sigmaY, const SkMask& src, SkMa
return {radiusX, radiusY};
}
struct AlphaIterA1 {
AlphaIterA1(const uint8_t* ptr, int offset) : fPtr(ptr), fOffset(7 - offset) {}
AlphaIterA1(const AlphaIterA1& that) : fPtr(that.fPtr), fOffset(that.fOffset) {}
AlphaIterA1& operator++() {
if (0 < fOffset ) {
--fOffset;
} else {
++fPtr;
fOffset = 7;
}
return *this;
}
AlphaIterA1& operator--() {
if (fOffset < 7) {
++fOffset;
} else {
--fPtr;
fOffset = 0;
}
return *this;
}
uint8_t operator*() { return ((*fPtr) >> fOffset) & 1 ? 0xFF : 0; }
bool operator<(const AlphaIterA1& that) {
return fPtr < that.fPtr || (fPtr == that.fPtr && fOffset > that.fOffset);
}
const uint8_t* fPtr;
int fOffset;
};
using AlphaIterA8 = uint8_t*;
struct AlphaIterARGB {
AlphaIterARGB(const uint32_t* ptr) : fPtr(ptr) {}
AlphaIterARGB(const AlphaIterARGB& that) : fPtr(that.fPtr) {}
AlphaIterARGB& operator++() { ++fPtr; return *this; }
AlphaIterARGB& operator--() { --fPtr; return *this; }
uint8_t operator*() { return SkGetPackedA32(*fPtr); }
bool operator<(const AlphaIterARGB& that) { return fPtr < that.fPtr; }
const uint32_t* fPtr;
};
struct AlphaIterLCD {
AlphaIterLCD(const uint16_t* ptr) : fPtr(ptr) {}
AlphaIterLCD(const AlphaIterLCD& that) : fPtr(that.fPtr) {}
AlphaIterLCD& operator++() { ++fPtr; return *this; }
AlphaIterLCD& operator--() { --fPtr; return *this; }
uint8_t operator*() {
unsigned packed = *fPtr;
unsigned r = SkPacked16ToR32(packed);
unsigned g = SkPacked16ToG32(packed);
unsigned b = SkPacked16ToB32(packed);
return (r + g + b) / 3;
}
bool operator<(const AlphaIterLCD& that) { return fPtr < that.fPtr; }
const uint16_t* fPtr;
};
} // namespace
// TODO: assuming sigmaW = sigmaH. Allow different sigmas. Right now the
@ -1062,31 +1006,45 @@ SkIPoint SkMaskBlurFilter::blur(const SkMask& src, SkMask* dst) const {
// Blur horizontally, and transpose.
const PlanGauss::Scan& scanW = planW.makeBlurScan(srcW, buffer);
for (int y = 0; y < srcH; y++) {
uint8_t* srcStart = &src.fImage[y * src.fRowBytes];
auto tmpStart = &tmp[y];
switch (src.fFormat) {
case SkMask::kBW_Format:
scanW.blur(AlphaIterA1(srcStart, 0), AlphaIterA1(srcStart + (srcW / 8), srcW % 8),
tmpStart, tmpW, tmpStart + tmpW * tmpH);
break;
case SkMask::kA8_Format:
scanW.blur(AlphaIterA8(srcStart), AlphaIterA8(srcStart + srcW),
tmpStart, tmpW, tmpStart + tmpW * tmpH);
break;
case SkMask::kARGB32_Format: {
const uint32_t* argbStart = reinterpret_cast<const uint32_t*>(srcStart);
scanW.blur(AlphaIterARGB(argbStart), AlphaIterARGB(argbStart + srcW),
tmpStart, tmpW, tmpStart + tmpW * tmpH);
} break;
case SkMask::kLCD16_Format: {
const uint16_t* lcdStart = reinterpret_cast<const uint16_t*>(srcStart);
scanW.blur(AlphaIterLCD(lcdStart), AlphaIterLCD(lcdStart + srcW),
tmpStart, tmpW, tmpStart + tmpW * tmpH);
} break;
default:
SK_ABORT("Unhandled format.");
}
switch (src.fFormat) {
case SkMask::kBW_Format: {
const uint8_t* bwStart = src.fImage;
auto start = SkMask::AlphaIter<SkMask::kBW_Format>(bwStart, 0);
auto end = SkMask::AlphaIter<SkMask::kBW_Format>(bwStart + (srcW / 8), srcW % 8);
for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
auto tmpStart = &tmp[y];
scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
}
} break;
case SkMask::kA8_Format: {
const uint8_t* a8Start = src.fImage;
auto start = SkMask::AlphaIter<SkMask::kA8_Format>(a8Start);
auto end = SkMask::AlphaIter<SkMask::kA8_Format>(a8Start + srcW);
for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
auto tmpStart = &tmp[y];
scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
}
} break;
case SkMask::kARGB32_Format: {
const uint32_t* argbStart = reinterpret_cast<const uint32_t*>(src.fImage);
auto start = SkMask::AlphaIter<SkMask::kARGB32_Format>(argbStart);
auto end = SkMask::AlphaIter<SkMask::kARGB32_Format>(argbStart + srcW);
for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
auto tmpStart = &tmp[y];
scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
}
} break;
case SkMask::kLCD16_Format: {
const uint16_t* lcdStart = reinterpret_cast<const uint16_t*>(src.fImage);
auto start = SkMask::AlphaIter<SkMask::kLCD16_Format>(lcdStart);
auto end = SkMask::AlphaIter<SkMask::kLCD16_Format>(lcdStart + srcW);
for (int y = 0; y < srcH; ++y, start >>= src.fRowBytes, end >>= src.fRowBytes) {
auto tmpStart = &tmp[y];
scanW.blur(start, end, tmpStart, tmpW, tmpStart + tmpW * tmpH);
}
} break;
default:
SK_ABORT("Unhandled format.");
}
// Blur vertically (scan in memory order because of the transposition),