668995122f
Always call generateMetrics before generatePath so that generateMetrics can determine which glyph representation to use and if that glyph representation can be modeled as a path. Pass an allocator into generateMetrics so that it can set the path to not existing. This allows generatePath to continue to work as it used to, creating a path if any path is available. However, generateMetrics may first set the path to not existing. Update getPath and internalGetPath to use the path on the glyph if it has already been set. Update makeGlyph and internalMakeGlyph to always call generateMetrics first (which is now more like initGlyph). Update the SkGlyph::PathData to indicate that it is a dev-path and not a user-path. A user-path will have effects applied to it. A dev-path is always a resolved path which is always filled -- unless it is hairline. Update everything else for the knock on effects and to take advantage of this information. Bug: chromium:1266022 Change-Id: Id3f3cf5a534ab99f3a5779c910c1d1e191e68b1e Reviewed-on: https://skia-review.googlesource.com/c/skia/+/478658 Reviewed-by: Herb Derby <herb@google.com> Commit-Queue: Ben Wagner <bungeman@google.com>
237 lines
8.6 KiB
C++
237 lines
8.6 KiB
C++
/*
|
|
* Copyright 2015 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "include/core/SkBitmap.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkPath.h"
|
|
#include "src/core/SkAdvancedTypefaceMetrics.h"
|
|
#include "src/core/SkGlyph.h"
|
|
#include "src/core/SkRectPriv.h"
|
|
#include "tools/fonts/RandomScalerContext.h"
|
|
|
|
class SkDescriptor;
|
|
|
|
class RandomScalerContext : public SkScalerContext {
|
|
public:
|
|
RandomScalerContext(sk_sp<SkRandomTypeface>,
|
|
const SkScalerContextEffects&,
|
|
const SkDescriptor*,
|
|
bool fFakeIt);
|
|
|
|
protected:
|
|
bool generateAdvance(SkGlyph*) override;
|
|
void generateMetrics(SkGlyph*, SkArenaAlloc*) override;
|
|
void generateImage(const SkGlyph&) override;
|
|
bool generatePath(const SkGlyph&, SkPath*) override;
|
|
void generateFontMetrics(SkFontMetrics*) override;
|
|
|
|
private:
|
|
SkRandomTypeface* getRandomTypeface() const {
|
|
return static_cast<SkRandomTypeface*>(this->getTypeface());
|
|
}
|
|
std::unique_ptr<SkScalerContext> fProxy;
|
|
// Many of the SkGlyphs returned are the same as those created by the fProxy.
|
|
// When they are not, the originals are kept here.
|
|
SkTHashMap<SkPackedGlyphID, SkGlyph> fProxyGlyphs;
|
|
bool fFakeIt;
|
|
};
|
|
|
|
RandomScalerContext::RandomScalerContext(sk_sp<SkRandomTypeface> face,
|
|
const SkScalerContextEffects& effects,
|
|
const SkDescriptor* desc,
|
|
bool fakeIt)
|
|
: SkScalerContext(std::move(face), effects, desc)
|
|
, fProxy(getRandomTypeface()->proxy()->createScalerContext(SkScalerContextEffects(), desc))
|
|
, fFakeIt(fakeIt) {
|
|
fProxy->forceGenerateImageFromPath();
|
|
}
|
|
|
|
bool RandomScalerContext::generateAdvance(SkGlyph* glyph) { return fProxy->generateAdvance(glyph); }
|
|
|
|
void RandomScalerContext::generateMetrics(SkGlyph* glyph, SkArenaAlloc* alloc) {
|
|
// Here we will change the mask format of the glyph
|
|
// NOTE: this may be overridden by the base class (e.g. if a mask filter is applied).
|
|
SkMask::Format format = SkMask::kA8_Format;
|
|
switch (glyph->getGlyphID() % 4) {
|
|
case 0: format = SkMask::kLCD16_Format; break;
|
|
case 1: format = SkMask::kA8_Format; break;
|
|
case 2: format = SkMask::kARGB32_Format; break;
|
|
case 3: format = SkMask::kBW_Format; break;
|
|
}
|
|
|
|
*glyph = fProxy->internalMakeGlyph(glyph->getPackedID(), format, alloc);
|
|
|
|
if (fFakeIt || (glyph->getGlyphID() % 4) != 2) {
|
|
return;
|
|
}
|
|
|
|
fProxy->getPath(*glyph, alloc);
|
|
if (!glyph->path()) {
|
|
return;
|
|
}
|
|
|
|
// The proxy glyph has a path, but this glyph does not.
|
|
// Stash the proxy glyph so it can be used later.
|
|
const SkGlyph* proxyGlyph = fProxyGlyphs.set(glyph->getPackedID(), std::move(*glyph));
|
|
const SkPath& proxyPath = *proxyGlyph->path();
|
|
|
|
*glyph = SkGlyph(glyph->getPackedID());
|
|
glyph->setPath(alloc, nullptr, false);
|
|
glyph->fMaskFormat = SkMask::kARGB32_Format;
|
|
glyph->fAdvanceX = proxyGlyph->fAdvanceX;
|
|
glyph->fAdvanceY = proxyGlyph->fAdvanceY;
|
|
|
|
SkRect storage;
|
|
const SkPaint& paint = this->getRandomTypeface()->paint();
|
|
const SkRect& newBounds =
|
|
paint.doComputeFastBounds(proxyPath.getBounds(), &storage, SkPaint::kFill_Style);
|
|
SkIRect ibounds;
|
|
newBounds.roundOut(&ibounds);
|
|
glyph->fLeft = ibounds.fLeft;
|
|
glyph->fTop = ibounds.fTop;
|
|
glyph->fWidth = ibounds.width();
|
|
glyph->fHeight = ibounds.height();
|
|
}
|
|
|
|
void RandomScalerContext::generateImage(const SkGlyph& glyph) {
|
|
if (fFakeIt) {
|
|
sk_bzero(glyph.fImage, glyph.imageSize());
|
|
return;
|
|
}
|
|
|
|
SkGlyph* proxyGlyph = fProxyGlyphs.find(glyph.getPackedID());
|
|
if (!proxyGlyph || !proxyGlyph->path()) {
|
|
fProxy->getImage(glyph);
|
|
return;
|
|
}
|
|
const SkPath& path = *proxyGlyph->path();
|
|
const bool hairline = proxyGlyph->pathIsHairline();
|
|
|
|
SkBitmap bm;
|
|
bm.installPixels(SkImageInfo::MakeN32Premul(glyph.fWidth, glyph.fHeight),
|
|
glyph.fImage,
|
|
glyph.rowBytes());
|
|
bm.eraseColor(0);
|
|
|
|
SkCanvas canvas(bm);
|
|
canvas.translate(-SkIntToScalar(glyph.fLeft), -SkIntToScalar(glyph.fTop));
|
|
SkPaint paint = this->getRandomTypeface()->paint();
|
|
if (hairline) {
|
|
// We have a device path with effects already applied which is normally a fill path.
|
|
// However here we do not have a fill path and there is no area to fill.
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
paint.setStroke(0);
|
|
}
|
|
canvas.drawPath(path, paint); //Need to modify the paint if the devPath is hairline
|
|
}
|
|
|
|
bool RandomScalerContext::generatePath(const SkGlyph& glyph, SkPath* path) {
|
|
SkGlyph* shadowProxyGlyph = fProxyGlyphs.find(glyph.getPackedID());
|
|
if (shadowProxyGlyph && shadowProxyGlyph->path()) {
|
|
path->reset();
|
|
return false;
|
|
}
|
|
return fProxy->generatePath(glyph, path);
|
|
}
|
|
|
|
void RandomScalerContext::generateFontMetrics(SkFontMetrics* metrics) {
|
|
fProxy->getFontMetrics(metrics);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkRandomTypeface::SkRandomTypeface(sk_sp<SkTypeface> proxy, const SkPaint& paint, bool fakeIt)
|
|
: SkTypeface(proxy->fontStyle(), false)
|
|
, fProxy(std::move(proxy))
|
|
, fPaint(paint)
|
|
, fFakeIt(fakeIt) {}
|
|
|
|
std::unique_ptr<SkScalerContext> SkRandomTypeface::onCreateScalerContext(
|
|
const SkScalerContextEffects& effects, const SkDescriptor* desc) const
|
|
{
|
|
return std::make_unique<RandomScalerContext>(
|
|
sk_ref_sp(const_cast<SkRandomTypeface*>(this)), effects, desc, fFakeIt);
|
|
}
|
|
|
|
void SkRandomTypeface::onFilterRec(SkScalerContextRec* rec) const {
|
|
fProxy->filterRec(rec);
|
|
rec->setHinting(SkFontHinting::kNone);
|
|
rec->fMaskFormat = SkMask::kARGB32_Format;
|
|
}
|
|
|
|
void SkRandomTypeface::getGlyphToUnicodeMap(SkUnichar* glyphToUnicode) const {
|
|
fProxy->getGlyphToUnicodeMap(glyphToUnicode);
|
|
}
|
|
|
|
std::unique_ptr<SkAdvancedTypefaceMetrics> SkRandomTypeface::onGetAdvancedMetrics() const {
|
|
return fProxy->getAdvancedMetrics();
|
|
}
|
|
|
|
std::unique_ptr<SkStreamAsset> SkRandomTypeface::onOpenStream(int* ttcIndex) const {
|
|
return fProxy->openStream(ttcIndex);
|
|
}
|
|
|
|
sk_sp<SkTypeface> SkRandomTypeface::onMakeClone(const SkFontArguments& args) const {
|
|
sk_sp<SkTypeface> proxy = fProxy->makeClone(args);
|
|
if (!proxy) {
|
|
return nullptr;
|
|
}
|
|
return sk_make_sp<SkRandomTypeface>(proxy, fPaint, fFakeIt);
|
|
}
|
|
|
|
void SkRandomTypeface::onGetFontDescriptor(SkFontDescriptor* desc, bool* isLocal) const {
|
|
// TODO: anything that uses this typeface isn't correctly serializable, since this typeface
|
|
// cannot be deserialized.
|
|
fProxy->getFontDescriptor(desc, isLocal);
|
|
}
|
|
|
|
void SkRandomTypeface::onCharsToGlyphs(const SkUnichar* uni, int count, SkGlyphID glyphs[]) const {
|
|
fProxy->unicharsToGlyphs(uni, count, glyphs);
|
|
}
|
|
|
|
int SkRandomTypeface::onCountGlyphs() const { return fProxy->countGlyphs(); }
|
|
|
|
int SkRandomTypeface::onGetUPEM() const { return fProxy->getUnitsPerEm(); }
|
|
|
|
void SkRandomTypeface::onGetFamilyName(SkString* familyName) const {
|
|
fProxy->getFamilyName(familyName);
|
|
}
|
|
|
|
bool SkRandomTypeface::onGetPostScriptName(SkString* postScriptName) const {
|
|
return fProxy->getPostScriptName(postScriptName);
|
|
}
|
|
|
|
SkTypeface::LocalizedStrings* SkRandomTypeface::onCreateFamilyNameIterator() const {
|
|
return fProxy->createFamilyNameIterator();
|
|
}
|
|
|
|
void SkRandomTypeface::getPostScriptGlyphNames(SkString* names) const {
|
|
return fProxy->getPostScriptGlyphNames(names);
|
|
}
|
|
|
|
int SkRandomTypeface::onGetVariationDesignPosition(
|
|
SkFontArguments::VariationPosition::Coordinate coordinates[],
|
|
int coordinateCount) const {
|
|
return fProxy->onGetVariationDesignPosition(coordinates, coordinateCount);
|
|
}
|
|
|
|
int SkRandomTypeface::onGetVariationDesignParameters(SkFontParameters::Variation::Axis parameters[],
|
|
int parameterCount) const {
|
|
return fProxy->onGetVariationDesignParameters(parameters, parameterCount);
|
|
}
|
|
|
|
int SkRandomTypeface::onGetTableTags(SkFontTableTag tags[]) const {
|
|
return fProxy->getTableTags(tags);
|
|
}
|
|
|
|
size_t SkRandomTypeface::onGetTableData(SkFontTableTag tag,
|
|
size_t offset,
|
|
size_t length,
|
|
void* data) const {
|
|
return fProxy->getTableData(tag, offset, length, data);
|
|
}
|