Simplify font-chaining (fallbacks) to have fonthost just return the next

logical fontID.
Extend ImageRef to accept an imagedecoder factory, to replace calling the std
one.



git-svn-id: http://skia.googlecode.com/svn/trunk@125 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
reed@android.com 2009-03-17 17:59:53 +00:00
parent 452f844f64
commit a14ea0e930
9 changed files with 158 additions and 105 deletions

View File

@ -117,11 +117,14 @@ public:
*/
static SkScalerContext* CreateScalerContext(const SkDescriptor* desc);
/** Return a scalercontext using the "fallback" font. If there is no
designated fallback, return null.
/** Given a "current" fontID, return the next logical fontID to use
when searching fonts for a given unicode value. Typically the caller
will query a given font, and if a unicode value is not supported, they
will call this, and if 0 is not returned, will search that font, and so
on. This process must be finite, and when the fonthost sees a
font with no logical successor, it must return 0.
*/
static SkScalerContext* CreateFallbackScalerContext(
const SkScalerContext::Rec&);
static uint32_t NextLogicalFont(uint32_t fontID);
///////////////////////////////////////////////////////////////////////////

View File

@ -169,10 +169,16 @@ public:
SkScalerContext(const SkDescriptor* desc);
virtual ~SkScalerContext();
// remember our glyph offset/base
void setBaseGlyphCount(unsigned baseGlyphCount) {
fBaseGlyphCount = baseGlyphCount;
}
/** Return the corresponding glyph for the specified unichar. Since contexts
may be chained (under the hood), the glyphID that is returned may in
fact correspond to a different font/context. In that case, we use the
base-glyph-count to know how to translate back into local glyph space.
*/
uint16_t charToGlyphID(SkUnichar uni);
unsigned getGlyphCount() const { return this->generateGlyphCount(); }
@ -208,12 +214,15 @@ private:
void internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
SkPath* devPath, SkMatrix* fillToDevMatrix);
mutable SkScalerContext* fAuxScalerContext;
// return the next context, treating fNextContext as a cache of the answer
SkScalerContext* getNextContext();
SkScalerContext* getGlyphContext(const SkGlyph& glyph) const;
// return loaded fAuxScalerContext or NULL
SkScalerContext* loadAuxContext() const;
// returns the right context from our link-list for this glyph. If no match
// is found, just returns the original context (this)
SkScalerContext* getGlyphContext(const SkGlyph& glyph);
// link-list of context, to handle missing chars. null-terminated.
SkScalerContext* fNextContext;
};
#define kRec_SkDescriptorTag SkSetFourByteTag('s', 'r', 'e', 'c')

View File

@ -147,7 +147,7 @@ public:
If none is found, the method returns NULL.
*/
static SkImageDecoder* Factory(SkStream*);
/** Decode the image stored in the specified file, and store the result
in bitmap. Return true for success or false on failure.
@ -260,4 +260,24 @@ private:
SkImageDecoder& operator=(const SkImageDecoder&);
};
/** Calling newDecoder with a stream returns a new matching imagedecoder
instance, or NULL if none can be found. The caller must manage its ownership
of the stream as usual, calling unref() when it is done, as the returned
decoder may have called ref() (and if so, the decoder is responsible for
balancing its ownership when it is destroyed).
*/
class SkImageDecoderFactory : public SkRefCnt {
public:
virtual SkImageDecoder* newDecoder(SkStream*) = 0;
};
class SkDefaultImageDecoderFactory : SkImageDecoderFactory {
public:
// calls SkImageDecoder::Factory(stream)
virtual SkImageDecoder* newDecoder(SkStream* stream) {
return SkImageDecoder::Factory(stream);
}
};
#endif

View File

@ -52,6 +52,10 @@ public:
and ignore the bitmap parameter.
*/
bool getInfo(SkBitmap* bm);
SkImageDecoderFactory* getDecoderFactory() const { return fFactory; }
// returns the factory parameter
SkImageDecoderFactory* setDecoderFactory(SkImageDecoderFactory*);
// overrides
virtual void flatten(SkFlattenableWriteBuffer&) const;
@ -81,10 +85,11 @@ private:
// requested state (or further, i.e. has pixels)
bool prepareBitmap(SkImageDecoder::Mode);
SkStream* fStream;
SkBitmap::Config fConfig;
int fSampleSize;
bool fErrorInDecoding;
SkImageDecoderFactory* fFactory; // may be null
SkStream* fStream;
SkBitmap::Config fConfig;
int fSampleSize;
bool fErrorInDecoding;
friend class SkImageRefPool;

View File

@ -95,7 +95,7 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc)
}
fBaseGlyphCount = 0;
fAuxScalerContext = NULL;
fNextContext = NULL;
const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL);
SkASSERT(rec);
@ -121,80 +121,99 @@ SkScalerContext::SkScalerContext(const SkDescriptor* desc)
}
SkScalerContext::~SkScalerContext() {
SkDELETE(fNextContext);
fPathEffect->safeUnref();
fMaskFilter->safeUnref();
fRasterizer->safeUnref();
SkDELETE(fAuxScalerContext);
}
SkScalerContext* SkScalerContext::loadAuxContext() const {
if (NULL == fAuxScalerContext) {
fAuxScalerContext = SkFontHost::CreateFallbackScalerContext(fRec);
if (NULL != fAuxScalerContext) {
fAuxScalerContext->setBaseGlyphCount(this->getGlyphCount());
}
static SkScalerContext* allocNextContext(const SkScalerContext::Rec& rec) {
// fonthost will determine the next possible font to search, based
// on the current font in fRec. It will return NULL if ctx is our
// last font that can be searched (i.e. ultimate fallback font)
uint32_t newFontID = SkFontHost::NextLogicalFont(rec.fFontID);
if (0 == newFontID) {
return NULL;
}
return fAuxScalerContext;
SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
SkDescriptor* desc = ad.getDesc();
desc->init();
SkScalerContext::Rec* newRec =
(SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
sizeof(rec), &rec);
newRec->fFontID = newFontID;
desc->computeChecksum();
return SkFontHost::CreateScalerContext(desc);
}
#ifdef TRACK_MISSING_CHARS
static uint8_t gMissingChars[1 << 13];
#endif
uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
unsigned glyphID = this->generateCharToGlyph(uni);
if (0 == glyphID) { // try auxcontext
SkScalerContext* ctx = this->loadAuxContext();
if (NULL != ctx) {
glyphID = ctx->generateCharToGlyph(uni);
if (0 != glyphID) { // only fiddle with it if its not missing
glyphID += this->getGlyphCount();
if (glyphID > 0xFFFF) {
glyphID = 0;
}
}
/* Return the next context, creating it if its not already created, but return
NULL if the fonthost says there are no more fonts to fallback to.
*/
SkScalerContext* SkScalerContext::getNextContext() {
SkScalerContext* next = fNextContext;
// if next is null, then either it isn't cached yet, or we're at the
// end of our possible chain
if (NULL == next) {
next = allocNextContext(fRec);
if (NULL == next) {
return NULL;
}
// next's base is our base + our local count
next->setBaseGlyphCount(fBaseGlyphCount + this->getGlyphCount());
// cache the answer
fNextContext = next;
}
#ifdef TRACK_MISSING_CHARS
if (0 == glyphID) {
bool announce = false;
if (uni > 0xFFFF) { // we don't record these
announce = true;
} else {
unsigned index = uni >> 3;
unsigned mask = 1 << (uni & 7);
SkASSERT(index < SK_ARRAY_COUNT(gMissingChars));
if ((gMissingChars[index] & mask) == 0) {
gMissingChars[index] |= mask;
announce = true;
}
}
if (announce) {
printf(">>> MISSING CHAR <<< 0x%04X\n", uni);
}
}
#endif
return SkToU16(glyphID);
return next;
}
/* Internal routine to resolve auxContextID into a real context.
Only makes sense to call once the glyph has been given a
valid auxGlyphID.
*/
SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) const {
SkScalerContext* ctx = const_cast<SkScalerContext*>(this);
if (glyph.getGlyphID() >= this->getGlyphCount()) {
ctx = this->loadAuxContext();
if (NULL == ctx) { // if no aux, just return us
ctx = const_cast<SkScalerContext*>(this);
SkScalerContext* SkScalerContext::getGlyphContext(const SkGlyph& glyph) {
unsigned glyphID = glyph.getGlyphID();
SkScalerContext* ctx = this;
for (;;) {
unsigned count = ctx->getGlyphCount();
if (glyphID < count) {
break;
}
glyphID -= count;
ctx = ctx->getNextContext();
if (NULL == ctx) {
SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
// just return the original context (this)
return this;
}
}
return ctx;
}
/* This loops through all available fallback contexts (if needed) until it
finds some context that can handle the unichar. If all fail, returns 0
*/
uint16_t SkScalerContext::charToGlyphID(SkUnichar uni) {
SkScalerContext* ctx = this;
unsigned glyphID;
for (;;) {
glyphID = ctx->generateCharToGlyph(uni);
if (glyphID) {
break; // found it
}
ctx = ctx->getNextContext();
if (NULL == ctx) {
return 0; // no more contexts, return missing glyph
}
}
// add the ctx's base, making glyphID unique for chain of contexts
glyphID += ctx->fBaseGlyphCount;
// check for overflow of 16bits, since our glyphID cannot exceed that
if (glyphID > 0xFFFF) {
glyphID = 0;
}
return SkToU16(glyphID);
}
void SkScalerContext::getAdvance(SkGlyph* glyph) {
// mark us as just having a valid advance
glyph->fMaskFormat = MASK_FORMAT_JUST_ADVANCE;

View File

@ -20,6 +20,7 @@ SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config,
fConfig = config;
fSampleSize = sampleSize;
fPrev = fNext = NULL;
fFactory = NULL;
#ifdef DUMP_IMAGEREF_LIFECYCLE
SkDebugf("add ImageRef %p [%d] data=%d\n",
@ -36,6 +37,7 @@ SkImageRef::~SkImageRef() {
#endif
fStream->unref();
fFactory->safeUnref();
}
bool SkImageRef::getInfo(SkBitmap* bitmap) {
@ -52,6 +54,12 @@ bool SkImageRef::getInfo(SkBitmap* bitmap) {
return true;
}
SkImageDecoderFactory* SkImageRef::setDecoderFactory(
SkImageDecoderFactory* fact) {
SkRefCnt_SafeAssign(fFactory, fact);
return fact;
}
///////////////////////////////////////////////////////////////////////////////
bool SkImageRef::onDecode(SkImageDecoder* codec, SkStream* stream,
@ -84,8 +92,14 @@ bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
SkASSERT(fBitmap.getPixels() == NULL);
fStream->rewind();
SkImageDecoder* codec = SkImageDecoder::Factory(fStream);
SkImageDecoder* codec;
if (fFactory) {
codec = fFactory->newDecoder(fStream);
} else {
codec = SkImageDecoder::Factory(fStream);
}
if (codec) {
SkAutoTDelete<SkImageDecoder> ad(codec);

View File

@ -590,22 +590,14 @@ SkStream* SkFontHost::OpenStream(uint32_t fontID)
return stream;
}
SkScalerContext* SkFontHost::CreateFallbackScalerContext(
const SkScalerContext::Rec& rec)
{
uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
load_system_fonts();
SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
SkDescriptor* desc = ad.getDesc();
desc->init();
SkScalerContext::Rec* newRec =
(SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
sizeof(rec), &rec);
newRec->fFontID = gFallBackTypeface->uniqueID();
desc->computeChecksum();
return SkFontHost::CreateScalerContext(desc);
if (gFallBackTypeface->uniqueID() == fontID) {
// no where to go, just return NULL
return 0;
}
return gFallBackTypeface->uniqueID();
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -446,19 +446,12 @@ SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
return new SkScalerContext_Mac(desc);
}
SkScalerContext* SkFontHost::CreateFallbackScalerContext(const SkScalerContext::Rec& rec)
{
SkAutoDescriptor ad(sizeof(rec) + SkDescriptor::ComputeOverhead(1));
SkDescriptor* desc = ad.getDesc();
desc->init();
SkScalerContext::Rec* newRec =
(SkScalerContext::Rec*)desc->addEntry(kRec_SkDescriptorTag,
sizeof(rec), &rec);
newRec->fFontID = find_default_fontID();
desc->computeChecksum();
return SkFontHost::CreateScalerContext(desc);
uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
uint32_t newFontID = find_default_fontID();
if (newFontID == fontID) {
newFontID = 0;
}
return newFontID;
}
SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,

View File

@ -62,10 +62,8 @@ SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
return NULL;
}
SkScalerContext* SkFontHost::CreateFallbackScalerContext(
const SkScalerContext::Rec&) {
SkASSERT(!"SkFontHost::CreateFallbackScalerContext unimplemented");
return NULL;
uint32_t SkFontHost::NextLogicalFont(uint32_t fontID) {
return 0;
}