Use SkDrawableGlyphBuffer for bitmap rendering

Change-Id: I0e7c995123c55b63b2d4c9ddee033d7098cdc358
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/247416
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Herb Derby <herb@google.com>
This commit is contained in:
Herbert Derby 2019-10-09 15:09:15 -04:00 committed by Skia Commit-Bot
parent caf025e8f5
commit 3838fe3c82
9 changed files with 125 additions and 124 deletions

View File

@ -84,11 +84,11 @@ public:
this->drawPath(src, paint, nullptr, false, !isHairline, customBlitter);
}
void paintPaths(SkSpan<const SkPathPos> pathsAndPositions,
void paintPaths(SkDrawableGlyphBuffer* drawables,
SkScalar scale,
const SkPaint& paint) const override;
void paintMasks(SkSpan<const SkMask> masks, const SkPaint& paint) const override;
void paintMasks(SkDrawableGlyphBuffer* drawables, const SkPaint& paint) const override;
static bool ComputeMaskBounds(const SkRect& devPathBounds, const SkIRect* clipBounds,
const SkMaskFilter* filter, const SkMatrix* filterMatrix,

View File

@ -12,6 +12,7 @@
#include "src/core/SkScalerContext.h"
#include "src/core/SkStrike.h"
#include "src/core/SkUtils.h"
#include <climits>
// disable warning : local variable used without having been initialized
#if defined _WIN32
@ -21,7 +22,18 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
void SkDraw::paintMasks(SkSpan<const SkMask> masks, const SkPaint& paint) const {
static bool check_glyph_position(SkPoint position) {
// Prevent glyphs from being drawn outside of or straddling the edge of device space.
// Comparisons written a little weirdly so that NaN coordinates are treated safely.
auto gt = [](float a, int b) { return !(a <= (float)b); };
auto lt = [](float a, int b) { return !(a >= (float)b); };
return !(gt(position.fX, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
gt(position.fY, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)));
}
void SkDraw::paintMasks(SkDrawableGlyphBuffer* drawables, const SkPaint& paint) const {
// The size used for a typical blitter.
SkSTArenaAlloc<3308> alloc;
@ -38,10 +50,51 @@ void SkDraw::paintMasks(SkSpan<const SkMask> masks, const SkPaint& paint) const
bool useRegion = fRC->isBW() && !fRC->isRect();
if (useRegion) {
for (const SkMask& mask : masks) {
SkRegion::Cliperator clipper(fRC->bwRgn(), mask.fBounds);
for (auto t : drawables->drawable()) {
SkGlyphVariant glyph; SkPoint pos;
std::tie(glyph, pos) = t;
if (check_glyph_position(pos)) {
SkMask mask = glyph.glyph()->mask(pos);
SkRegion::Cliperator clipper(fRC->bwRgn(), mask.fBounds);
if (!clipper.done()) {
if (SkMask::kARGB32_Format == mask.fFormat) {
SkBitmap bm;
bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.size()),
mask.fImage,
mask.fRowBytes);
this->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), paint);
} else {
const SkIRect& cr = clipper.rect();
do {
blitter->blitMask(mask, cr);
clipper.next();
} while (!clipper.done());
}
}
}
}
} else {
SkIRect clipBounds = fRC->isBW() ? fRC->bwRgn().getBounds()
: fRC->aaRgn().getBounds();
for (auto t : drawables->drawable()) {
SkGlyphVariant glyph; SkPoint pos;
std::tie(glyph, pos) = t;
if (check_glyph_position(pos)) {
SkMask mask = glyph.glyph()->mask(pos);
SkIRect storage;
const SkIRect* bounds = &mask.fBounds;
// this extra test is worth it, assuming that most of the time it succeeds
// since we can avoid writing to storage
if (!clipBounds.containsNoEmptyCheck(mask.fBounds)) {
if (!storage.intersect(mask.fBounds, clipBounds)) {
continue;
}
bounds = &storage;
}
if (!clipper.done()) {
if (SkMask::kARGB32_Format == mask.fFormat) {
SkBitmap bm;
bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.size()),
@ -49,51 +102,22 @@ void SkDraw::paintMasks(SkSpan<const SkMask> masks, const SkPaint& paint) const
mask.fRowBytes);
this->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), paint);
} else {
const SkIRect& cr = clipper.rect();
do {
blitter->blitMask(mask, cr);
clipper.next();
} while (!clipper.done());
blitter->blitMask(mask, *bounds);
}
}
}
} else {
SkIRect clipBounds = fRC->isBW() ? fRC->bwRgn().getBounds()
: fRC->aaRgn().getBounds();
for (const SkMask& mask : masks) {
SkIRect storage;
const SkIRect* bounds = &mask.fBounds;
// this extra test is worth it, assuming that most of the time it succeeds
// since we can avoid writing to storage
if (!clipBounds.containsNoEmptyCheck(mask.fBounds)) {
if (!storage.intersect(mask.fBounds, clipBounds)) {
continue;
}
bounds = &storage;
}
if (SkMask::kARGB32_Format == mask.fFormat) {
SkBitmap bm;
bm.installPixels(SkImageInfo::MakeN32Premul(mask.fBounds.size()),
mask.fImage,
mask.fRowBytes);
this->drawSprite(bm, mask.fBounds.x(), mask.fBounds.y(), paint);
} else {
blitter->blitMask(mask, *bounds);
}
}
}
}
void SkDraw::paintPaths(SkSpan<const SkPathPos> pathsAndPositions,
void SkDraw::paintPaths(SkDrawableGlyphBuffer* drawables,
SkScalar scale,
const SkPaint& paint) const {
for (const auto& pathAndPos : pathsAndPositions) {
for (auto t : drawables->drawable()) {
SkGlyphVariant path; SkPoint pos;
std::tie(path, pos) = t;
SkMatrix m;
SkPoint position = pathAndPos.position;
m.setScaleTranslate(scale, scale, position.x(), position.y());
this->drawPath(*pathAndPos.path, paint, &m, false);
m.setScaleTranslate(scale, scale, pos.x(), pos.y());
this->drawPath(*path.path(), paint, &m, false);
}
}

View File

@ -10,7 +10,6 @@
#include "src/core/SkStrikeForGPU.h"
void SkDrawableGlyphBuffer::ensureSize(size_t size) {
SkASSERT(fPhase == kReset);
if (size > fMaxSize) {
fMultiBuffer.reset(size);
fPositions.reset(size);
@ -19,13 +18,12 @@ void SkDrawableGlyphBuffer::ensureSize(size_t size) {
fInputSize = 0;
fDrawableSize = 0;
SkDEBUGCODE(fPhase = kPackedID);
}
void SkDrawableGlyphBuffer::startSource(
const SkZip<const SkGlyphID, const SkPoint>& source, SkPoint origin) {
SkASSERT(fPhase == kPackedID);
fInputSize = source.size();
fDrawableSize = 0;
// Map all the positions.
auto positions = source.get<1>();
@ -44,8 +42,8 @@ void SkDrawableGlyphBuffer::startDevice(
const SkZip<const SkGlyphID, const SkPoint>& source,
SkPoint origin, const SkMatrix& viewMatrix,
const SkGlyphPositionRoundingSpec& roundingSpec) {
SkASSERT(fPhase == kPackedID);
fInputSize = source.size();
fDrawableSize = 0;
// Map the positions including subpixel position.
auto positions = source.get<1>();

View File

@ -133,7 +133,6 @@ private:
#ifdef SK_DEBUG
enum {
kReset,
kPackedID,
kInput,
kProcess,
kDraw

View File

@ -76,17 +76,6 @@ SkGlyphRunListPainter::SkGlyphRunListPainter(const GrRenderTargetContext& rtc)
#endif
static bool check_glyph_position(SkPoint position) {
// Prevent glyphs from being drawn outside of or straddling the edge of device space.
// Comparisons written a little weirdly so that NaN coordinates are treated safely.
auto gt = [](float a, int b) { return !(a <= (float)b); };
auto lt = [](float a, int b) { return !(a >= (float)b); };
return !(gt(position.fX, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
lt(position.fX, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) ||
gt(position.fY, INT_MAX - (INT16_MAX + SkTo<int>(UINT16_MAX))) ||
lt(position.fY, INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)));
}
SkSpan<const SkPackedGlyphID> SkGlyphRunListPainter::DeviceSpacePackedGlyphIDs(
const SkGlyphPositionRoundingSpec& roundingSpec,
const SkMatrix& viewMatrix,
@ -148,7 +137,6 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
SkPoint origin = glyphRunList.origin();
for (auto& glyphRun : glyphRunList) {
const SkFont& runFont = glyphRun.font();
auto runSize = glyphRun.runSize();
if (SkStrikeSpec::ShouldDrawAsPath(runPaint, runFont, deviceMatrix)) {
@ -157,71 +145,24 @@ void SkGlyphRunListPainter::drawForBitmapDevice(
auto strike = strikeSpec.findOrCreateExclusiveStrike();
// Used for the side effect for creating the right positions.
SourceSpacePackedGlyphIDs(
origin,
runSize,
glyphRun.glyphsIDs().data(),
glyphRun.positions().data(),
fPositions,
fPackedGlyphIDs);
SkBulkGlyphMetricsAndPaths glyphPaths{strikeSpec};
auto glyphs = glyphPaths.glyphs(glyphRun.glyphsIDs());
SkTDArray<SkPathPos> pathsAndPositions;
pathsAndPositions.setReserve(runSize);
for (size_t i = 0; i < runSize; i++) {
const SkGlyph& glyph = *glyphs[i];
SkPoint position = fPositions[i];
if (check_glyph_position(position) && !glyph.isEmpty() && glyph.path() != nullptr) {
pathsAndPositions.push_back(SkPathPos{glyph.path(), position});
}
}
fDrawable.startSource(glyphRun.source(), origin);
strike->prepareForDrawingPathsCPU(&fDrawable);
// The paint we draw paths with must have the same anti-aliasing state as the runFont
// allowing the paths to have the same edging as the glyph masks.
SkPaint pathPaint = runPaint;
pathPaint.setAntiAlias(runFont.hasSomeAntiAliasing());
bitmapDevice->paintPaths(
SkSpan<const SkPathPos>{pathsAndPositions.begin(), pathsAndPositions.size()},
strikeSpec.strikeToSourceRatio(), pathPaint);
bitmapDevice->paintPaths(&fDrawable, strikeSpec.strikeToSourceRatio(), pathPaint);
} else {
SkStrikeSpec strikeSpec = SkStrikeSpec::MakeMask(
runFont, runPaint, props, fScalerContextFlags, deviceMatrix);
auto strike = strikeSpec.findOrCreateExclusiveStrike();
auto packedGlyphIDs = DeviceSpacePackedGlyphIDs(
strike->roundingSpec(),
deviceMatrix,
origin,
runSize,
glyphRun.glyphsIDs().data(),
glyphRun.positions().data(),
fPositions,
fPackedGlyphIDs);
SkBulkGlyphMetricsAndImages glyphImages{strikeSpec};
SkSpan<const SkGlyph*> glyphs = glyphImages.glyphs(packedGlyphIDs);
SkTDArray<SkMask> masks;
masks.setReserve(runSize);
SkPoint* posCursor = fPositions.get();
for (const SkGlyph* glyph : glyphs) {
SkPoint position = *posCursor++;
// The glyph could have dimensions (!isEmpty()), but still may have no bits if
// the width is too wide. So check that there really is an image.
if (check_glyph_position(position)
&& !glyph->isEmpty()
&& glyph->image() != nullptr) {
masks.push_back(glyph->mask(position));
}
}
bitmapDevice->paintMasks(SkSpan<const SkMask>{masks.begin(), masks.size()}, runPaint);
fDrawable.startDevice(glyphRun.source(), origin, deviceMatrix, strike->roundingSpec());
strike->prepareForDrawingMasksCPU(&fDrawable);
bitmapDevice->paintMasks(&fDrawable, runPaint);
}
}
}
@ -930,9 +871,9 @@ std::unique_ptr<GrDrawOp> GrTextContext::createOp_TestingOnly(GrRecordingContext
#endif // GR_TEST_UTILS
#endif // SK_SUPPORT_GPU
SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, int size)
SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* painter, size_t size)
: fPainter{painter} {
SkASSERT(size >= 0);
fPainter->fDrawable.ensureSize(size);
if (fPainter->fMaxRunSize < size) {
fPainter->fMaxRunSize = size;
@ -943,6 +884,7 @@ SkGlyphRunListPainter::ScopedBuffers::ScopedBuffers(SkGlyphRunListPainter* paint
}
SkGlyphRunListPainter::ScopedBuffers::~ScopedBuffers() {
fPainter->fDrawable.reset();
fPainter->fPaths.clear();
fPainter->fARGBGlyphsIDs.clear();
fPainter->fARGBPositions.clear();

View File

@ -10,6 +10,7 @@
#include "include/core/SkSurfaceProps.h"
#include "src/core/SkDistanceFieldGen.h"
#include "src/core/SkGlyphBuffer.h"
#include "src/core/SkGlyphRun.h"
#include "src/core/SkScalerContext.h"
#include "src/core/SkTextBlobPriv.h"
@ -66,11 +67,10 @@ public:
public:
virtual ~BitmapDevicePainter() = default;
virtual void paintPaths(SkSpan<const SkPathPos> pathsAndPositions,
SkScalar scale,
const SkPaint& paint) const = 0;
virtual void paintPaths(
SkDrawableGlyphBuffer* drawables, SkScalar scale, const SkPaint& paint) const = 0;
virtual void paintMasks(SkSpan<const SkMask> masks, const SkPaint& paint) const = 0;
virtual void paintMasks(SkDrawableGlyphBuffer* drawables, const SkPaint& paint) const = 0;
};
void drawForBitmapDevice(
@ -93,7 +93,7 @@ private:
SkScalerContextFlags flags, SkStrikeForGPUCacheInterface* strikeCache);
struct ScopedBuffers {
ScopedBuffers(SkGlyphRunListPainter* painter, int size);
ScopedBuffers(SkGlyphRunListPainter* painter, size_t size);
~ScopedBuffers();
SkGlyphRunListPainter* fPainter;
};
@ -143,7 +143,9 @@ private:
SkStrikeForGPUCacheInterface* const fStrikeCache;
int fMaxRunSize{0};
SkDrawableGlyphBuffer fDrawable;
size_t fMaxRunSize{0};
SkAutoTMalloc<SkPoint> fPositions;
SkAutoTMalloc<SkPackedGlyphID> fPackedGlyphIDs;
SkAutoTMalloc<SkGlyphPos> fGlyphPos;

View File

@ -50,11 +50,13 @@ public:
fOverdrawCanvas{overdrawCanvas},
fPainter{props, kN32_SkColorType, nullptr, SkStrikeCache::GlobalStrikeCache()} {}
void paintPaths(SkSpan<const SkPathPos> pathsAndPositions, SkScalar scale,
const SkPaint& paint) const override {}
void paintPaths(SkDrawableGlyphBuffer*, SkScalar scale, const SkPaint& paint) const override {}
void paintMasks(SkSpan<const SkMask> masks, const SkPaint& paint) const override {
for (auto& mask : masks) {
void paintMasks(SkDrawableGlyphBuffer* drawables, const SkPaint& paint) const override {
for (auto t : drawables->drawable()) {
SkGlyphVariant glyph; SkPoint pos;
std::tie(glyph, pos) = t;
SkMask mask = glyph.glyph()->mask(pos);
fOverdrawCanvas->drawRect(SkRect::Make(mask.fBounds), SkPaint());
}
}

View File

@ -13,6 +13,7 @@
#include "include/private/SkMutex.h"
#include "include/private/SkOnce.h"
#include "include/private/SkTemplates.h"
#include "src/core/SkEnumerate.h"
#include "src/core/SkMakeUnique.h"
#include <cctype>
@ -180,6 +181,36 @@ SkStrike::prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs, const SkGlyph* r
return {results, glyphIDs.size()};
}
void SkStrike::prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables) {
for (auto t : SkMakeEnumerate(drawables->input())) {
size_t i; SkGlyphVariant packedID;
std::forward_as_tuple(i, std::tie(packedID, std::ignore)) = t;
SkGlyph* glyph = this->glyph(packedID);
if (!glyph->isEmpty()) {
const void* image = this->prepareImage(glyph);
// If the glyph is too large, then no image is created.
if (image != nullptr) {
drawables->push_back(glyph, i);
}
}
}
}
void SkStrike::prepareForDrawingPathsCPU(SkDrawableGlyphBuffer* drawables) {
for (auto t : SkMakeEnumerate(drawables->input())) {
size_t i; SkGlyphVariant packedID;
std::forward_as_tuple(i, std::tie(packedID, std::ignore)) = t;
SkGlyph* glyph = this->glyph(packedID);
if (!glyph->isEmpty()) {
const SkPath* path = this->preparePath(glyph);
// The glyph my not have a path.
if (path != nullptr) {
drawables->push_back(path, i);
}
}
}
}
// N.B. This glyphMetrics call culls all the glyphs which will not display based on a non-finite
// position or that there are no mask pixels.
SkSpan<const SkGlyphPos>

View File

@ -112,6 +112,9 @@ public:
SkSpan<const SkGlyph*> prepareImages(SkSpan<const SkPackedGlyphID> glyphIDs,
const SkGlyph* results[]);
void prepareForDrawingMasksCPU(SkDrawableGlyphBuffer* drawables);
void prepareForDrawingPathsCPU(SkDrawableGlyphBuffer* drawables);
SkSpan<const SkGlyphPos> prepareForDrawingRemoveEmpty(const SkPackedGlyphID packedGlyphIDs[],
const SkPoint positions[],
size_t n,