SkScalerContext to handle hairline paths.
Create an actual hairline path instead of a filled path. Allow metric and image generation for glyphs to handle hairlines in the event a color glyph has an outline and a hairline is needed. This also fixes glyph bounds issues with path based glyphs drawn with hairlines. Stroke+Fill is not handled very well, but is currently being deprecated and so given less weight. It works, but is somewhat arbitrary. Also add more paint overrides to Viewer. Note that this only adds hairline handling for backup purposes. The code in SkStrikeSpec::ShouldDrawAsPath which causes most harline glyphs to be drawn directly from paths is not changed. Change-Id: Icfadcd818d20b2557e4703c8e99d6d7dd0b4af70 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/368156 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Ben Wagner <bungeman@google.com>
This commit is contained in:
parent
863497b7fe
commit
f5cbbc6c15
@ -830,7 +830,7 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC
|
||||
|
||||
if (paint.getMaskFilter()) {
|
||||
SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle
|
||||
: SkStrokeRec::kHairline_InitStyle;
|
||||
: SkStrokeRec::kHairline_InitStyle;
|
||||
if (as_MFB(paint.getMaskFilter())
|
||||
->filterPath(devPath, fMatrixProvider->localToDevice(), *fRC, blitter, style)) {
|
||||
return; // filterPath() called the blitter, so we're done
|
||||
|
@ -79,7 +79,7 @@ SkScalerContext::SkScalerContext(sk_sp<SkTypeface> typeface, const SkScalerConte
|
||||
, fPathEffect(sk_ref_sp(effects.fPathEffect))
|
||||
, fMaskFilter(sk_ref_sp(effects.fMaskFilter))
|
||||
// Initialize based on our settings. Subclasses can also force this.
|
||||
, fGenerateImageFromPath(fRec.fFrameWidth > 0 || fPathEffect != nullptr)
|
||||
, fGenerateImageFromPath(fRec.fFrameWidth >= 0 || fPathEffect != nullptr)
|
||||
|
||||
, fPreBlend(fMaskFilter ? SkMaskGamma::PreBlend() : SkScalerContext::GetMaskPreBlend(fRec))
|
||||
{
|
||||
@ -214,14 +214,20 @@ SkGlyph SkScalerContext::internalMakeGlyph(SkPackedGlyphID packedID, SkMask::For
|
||||
const bool a8FromLCD = fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag;
|
||||
const bool fromLCD = (glyph.fMaskFormat == SkMask::kLCD16_Format) ||
|
||||
(glyph.fMaskFormat == SkMask::kA8_Format && a8FromLCD);
|
||||
if (0 < glyph.fWidth && fromLCD) {
|
||||
if (fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag) {
|
||||
glyph.fHeight += 2;
|
||||
glyph.fTop -= 1;
|
||||
} else {
|
||||
glyph.fWidth += 2;
|
||||
glyph.fLeft -= 1;
|
||||
}
|
||||
const bool notEmptyAndFromLCD = 0 < glyph.fWidth && fromLCD;
|
||||
const bool verticalLCD = fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag;
|
||||
|
||||
const bool hasHairline = fRec.fFrameWidth == 0;
|
||||
|
||||
const bool needExtraWidth = (notEmptyAndFromLCD && !verticalLCD) || hasHairline;
|
||||
const bool needExtraHeight = (notEmptyAndFromLCD && verticalLCD) || hasHairline;
|
||||
if (needExtraWidth) {
|
||||
glyph.fWidth += 2;
|
||||
glyph.fLeft -= 1;
|
||||
}
|
||||
if (needExtraHeight) {
|
||||
glyph.fHeight += 2;
|
||||
glyph.fTop -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -448,12 +454,15 @@ static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) {
|
||||
|
||||
static void generateMask(const SkMask& mask, const SkPath& path,
|
||||
const SkMaskGamma::PreBlend& maskPreBlend,
|
||||
const bool doBGR, const bool doVert, const bool a8FromLCD) {
|
||||
const bool doBGR, const bool doVert, const bool a8FromLCD,
|
||||
const SkPaint::Style paintStyle) {
|
||||
SkASSERT(mask.fFormat == SkMask::kBW_Format ||
|
||||
mask.fFormat == SkMask::kA8_Format ||
|
||||
mask.fFormat == SkMask::kLCD16_Format);
|
||||
|
||||
SkPaint paint;
|
||||
SkPath strokePath;
|
||||
const SkPath* pathToUse = &path;
|
||||
|
||||
int srcW = mask.fBounds.width();
|
||||
int srcH = mask.fBounds.height();
|
||||
@ -464,6 +473,7 @@ static void generateMask(const SkMask& mask, const SkPath& path,
|
||||
matrix.setTranslate(-SkIntToScalar(mask.fBounds.fLeft),
|
||||
-SkIntToScalar(mask.fBounds.fTop));
|
||||
|
||||
paint.setStyle(paintStyle);
|
||||
paint.setAntiAlias(SkMask::kBW_Format != mask.fFormat);
|
||||
|
||||
const bool fromLCD = (mask.fFormat == SkMask::kLCD16_Format) ||
|
||||
@ -482,6 +492,17 @@ static void generateMask(const SkMask& mask, const SkPath& path,
|
||||
0, 1, -SkIntToScalar(mask.fBounds.fTop),
|
||||
0, 0, 1);
|
||||
}
|
||||
|
||||
// LCD hairline doesn't line up with the pixels, so do it the expensive way.
|
||||
SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
|
||||
if (paintStyle != SkPaint::kFill_Style) {
|
||||
rec.setStrokeStyle(1.0f, paintStyle == SkPaint::kStrokeAndFill_Style);
|
||||
rec.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 0.0f);
|
||||
}
|
||||
if (rec.needToApply() && rec.applyToPath(&strokePath, path)) {
|
||||
pathToUse = &strokePath;
|
||||
paint.setStyle(SkPaint::kFill_Style);
|
||||
}
|
||||
}
|
||||
|
||||
SkRasterClip clip;
|
||||
@ -506,7 +527,7 @@ static void generateMask(const SkMask& mask, const SkPath& path,
|
||||
draw.fDst = dst;
|
||||
draw.fRC = &clip;
|
||||
draw.fMatrixProvider = &matrixProvider;
|
||||
draw.drawPath(path, paint);
|
||||
draw.drawPath(*pathToUse, paint);
|
||||
|
||||
switch (mask.fFormat) {
|
||||
case SkMask::kBW_Format:
|
||||
@ -567,7 +588,11 @@ void SkScalerContext::getImage(const SkGlyph& origGlyph) {
|
||||
const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
|
||||
const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
|
||||
const bool a8LCD = SkToBool(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag);
|
||||
generateMask(mask, devPath, fPreBlend, doBGR, doVert, a8LCD);
|
||||
const bool frameAndFill = SkToBool(fRec.fFlags & kFrameAndFill_Flag);
|
||||
const SkPaint::Style paintStyle = fRec.fFrameWidth != 0 ? SkPaint::kFill_Style
|
||||
: frameAndFill ? SkPaint::kStrokeAndFill_Style
|
||||
: SkPaint::kStroke_Style;
|
||||
generateMask(mask, devPath, fPreBlend, doBGR, doVert, a8LCD, paintStyle);
|
||||
}
|
||||
}
|
||||
|
||||
@ -633,7 +658,7 @@ bool SkScalerContext::internalGetPath(SkPackedGlyphID glyphID, SkPath* devPath)
|
||||
}
|
||||
}
|
||||
|
||||
if (fRec.fFrameWidth > 0 || fPathEffect != nullptr) {
|
||||
if (fRec.fFrameWidth >= 0 || fPathEffect != nullptr) {
|
||||
// need the path in user-space, with only the point-size applied
|
||||
// so that our stroking and effects will operate the same way they
|
||||
// would if the user had extracted the path themself, and then
|
||||
@ -651,7 +676,7 @@ bool SkScalerContext::internalGetPath(SkPackedGlyphID glyphID, SkPath* devPath)
|
||||
|
||||
SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
|
||||
|
||||
if (fRec.fFrameWidth > 0) {
|
||||
if (fRec.fFrameWidth >= 0) {
|
||||
rec.setStrokeStyle(fRec.fFrameWidth,
|
||||
SkToBool(fRec.fFlags & kFrameAndFill_Flag));
|
||||
// glyphs are always closed contours, so cap type is ignored,
|
||||
@ -961,7 +986,7 @@ void SkScalerContext::MakeRecAndEffects(const SkFont& font, const SkPaint& paint
|
||||
#endif
|
||||
}
|
||||
|
||||
if (style != SkPaint::kFill_Style && strokeWidth > 0) {
|
||||
if (style != SkPaint::kFill_Style && strokeWidth >= 0) {
|
||||
rec->fFrameWidth = strokeWidth;
|
||||
rec->fMiterLimit = paint.getStrokeMiter();
|
||||
rec->fStrokeJoin = SkToU8(paint.getStrokeJoin());
|
||||
@ -971,7 +996,7 @@ void SkScalerContext::MakeRecAndEffects(const SkFont& font, const SkPaint& paint
|
||||
flags |= SkScalerContext::kFrameAndFill_Flag;
|
||||
}
|
||||
} else {
|
||||
rec->fFrameWidth = 0;
|
||||
rec->fFrameWidth = -1;
|
||||
rec->fMiterLimit = 0;
|
||||
rec->fStrokeJoin = 0;
|
||||
rec->fStrokeCap = 0;
|
||||
|
@ -1362,6 +1362,21 @@ public:
|
||||
if (fPaintOverrides->fDither) {
|
||||
paint.setDither(fPaint->isDither());
|
||||
}
|
||||
if (fPaintOverrides->fStyle) {
|
||||
paint.setStyle(fPaint->getStyle());
|
||||
}
|
||||
if (fPaintOverrides->fWidth) {
|
||||
paint.setStrokeWidth(fPaint->getStrokeWidth());
|
||||
}
|
||||
if (fPaintOverrides->fMiterLimit) {
|
||||
paint.setStrokeMiter(fPaint->getStrokeMiter());
|
||||
}
|
||||
if (fPaintOverrides->fCapType) {
|
||||
paint.setStrokeCap(fPaint->getStrokeCap());
|
||||
}
|
||||
if (fPaintOverrides->fJoinType) {
|
||||
paint.setStrokeJoin(fPaint->getStrokeJoin());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
SkPaint* fPaint;
|
||||
@ -2015,6 +2030,75 @@ void Viewer::drawImGui() {
|
||||
"Default\0No Dither\0Dither\0\0",
|
||||
&SkPaintFields::fDither,
|
||||
&SkPaint::isDither, &SkPaint::setDither);
|
||||
|
||||
int styleIdx = 0;
|
||||
if (fPaintOverrides.fStyle) {
|
||||
styleIdx = SkTo<int>(fPaint.getStyle()) + 1;
|
||||
}
|
||||
if (ImGui::Combo("Style", &styleIdx,
|
||||
"Default\0Fill\0Stroke\0Stroke and Fill\0\0"))
|
||||
{
|
||||
if (styleIdx == 0) {
|
||||
fPaintOverrides.fStyle = false;
|
||||
fPaint.setStyle(SkPaint::kFill_Style);
|
||||
} else {
|
||||
fPaint.setStyle(SkTo<SkPaint::Style>(styleIdx - 1));
|
||||
fPaintOverrides.fStyle = true;
|
||||
}
|
||||
paramsChanged = true;
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Override Stroke Width", &fPaintOverrides.fWidth);
|
||||
if (fPaintOverrides.fWidth) {
|
||||
float width = fPaint.getStrokeWidth();
|
||||
if (ImGui::SliderFloat("Stroke Width", &width, 0, 20)) {
|
||||
fPaint.setStrokeWidth(width);
|
||||
paramsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Override Miter Limit", &fPaintOverrides.fMiterLimit);
|
||||
if (fPaintOverrides.fMiterLimit) {
|
||||
float miterLimit = fPaint.getStrokeMiter();
|
||||
if (ImGui::SliderFloat("Miter Limit", &miterLimit, 0, 20)) {
|
||||
fPaint.setStrokeMiter(miterLimit);
|
||||
paramsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
int capIdx = 0;
|
||||
if (fPaintOverrides.fCapType) {
|
||||
capIdx = SkTo<int>(fPaint.getStrokeCap()) + 1;
|
||||
}
|
||||
if (ImGui::Combo("Cap Type", &capIdx,
|
||||
"Default\0Butt\0Round\0Square\0\0"))
|
||||
{
|
||||
if (capIdx == 0) {
|
||||
fPaintOverrides.fCapType = false;
|
||||
fPaint.setStrokeCap(SkPaint::kDefault_Cap);
|
||||
} else {
|
||||
fPaint.setStrokeCap(SkTo<SkPaint::Cap>(capIdx - 1));
|
||||
fPaintOverrides.fCapType = true;
|
||||
}
|
||||
paramsChanged = true;
|
||||
}
|
||||
|
||||
int joinIdx = 0;
|
||||
if (fPaintOverrides.fJoinType) {
|
||||
joinIdx = SkTo<int>(fPaint.getStrokeJoin()) + 1;
|
||||
}
|
||||
if (ImGui::Combo("Join Type", &joinIdx,
|
||||
"Default\0Miter\0Round\0Bevel\0\0"))
|
||||
{
|
||||
if (joinIdx == 0) {
|
||||
fPaintOverrides.fJoinType = false;
|
||||
fPaint.setStrokeJoin(SkPaint::kDefault_Join);
|
||||
} else {
|
||||
fPaint.setStrokeJoin(SkTo<SkPaint::Join>(joinIdx - 1));
|
||||
fPaintOverrides.fJoinType = true;
|
||||
}
|
||||
paramsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Font")) {
|
||||
|
Loading…
Reference in New Issue
Block a user