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:
parent
caf025e8f5
commit
3838fe3c82
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>();
|
||||
|
@ -133,7 +133,6 @@ private:
|
||||
#ifdef SK_DEBUG
|
||||
enum {
|
||||
kReset,
|
||||
kPackedID,
|
||||
kInput,
|
||||
kProcess,
|
||||
kDraw
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user