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:
parent
2128b4ac44
commit
e3f1b59d9f
202
gm/typeface.cpp
202
gm/typeface.cpp
@ -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")) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user