diff options
Diffstat (limited to 'src/gl/SkGLTextCache.cpp')
-rw-r--r-- | src/gl/SkGLTextCache.cpp | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/src/gl/SkGLTextCache.cpp b/src/gl/SkGLTextCache.cpp new file mode 100644 index 0000000..141e100 --- /dev/null +++ b/src/gl/SkGLTextCache.cpp @@ -0,0 +1,191 @@ +#include "SkGLTextCache.h" +#include "SkScalerContext.h" +#include "SkTSearch.h" + +const GLenum gTextTextureFormat = GL_ALPHA; +const GLenum gTextTextureType = GL_UNSIGNED_BYTE; + +SkGLTextCache::Strike::Strike(Strike* next, int width, int height) { + fStrikeWidth = SkNextPow2(SkMax32(kMinStrikeWidth, width)); + fStrikeHeight = SkNextPow2(height); + fGlyphCount = 0; + fNextFreeOffsetX = 0; + fNext = next; + + fStrikeWidthShift = SkNextLog2(fStrikeWidth); + fStrikeHeightShift = SkNextLog2(fStrikeHeight); + + if (next) { + SkASSERT(next->fStrikeHeight == fStrikeHeight); + } + + // create an empty texture to receive glyphs + fTexName = 0; + glGenTextures(1, &fTexName); + glBindTexture(GL_TEXTURE_2D, fTexName); + glTexImage2D(GL_TEXTURE_2D, 0, gTextTextureFormat, + fStrikeWidth, fStrikeHeight, 0, + gTextTextureFormat, gTextTextureType, NULL); + + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +SkGLTextCache::Strike::~Strike() { + if (fTexName != 0) { + glDeleteTextures(1, &fTexName); + } +} + +SkGLTextCache::Strike* +SkGLTextCache::Strike::findGlyph(const SkGlyph& glyph, int* offset) { + Strike* strike = this; + SkDEBUGCODE(const int height = SkNextPow2(glyph.fHeight);) + + do { + SkASSERT(height == strike->fStrikeHeight); + + int index = SkTSearch(strike->fGlyphIDArray, strike->fGlyphCount, + glyph.fID, sizeof(strike->fGlyphIDArray[0])); + if (index >= 0) { + if (offset) { + *offset = strike->fGlyphOffsetX[index]; + } + return strike; + } + strike = strike->fNext; + } while (NULL != strike); + return NULL; +} + +static void make_a_whole(void* buffer, int index, int count, size_t elemSize) { + SkASSERT(index >= 0 && index <= count); + size_t offset = index * elemSize; + memmove((char*)buffer + offset + elemSize, + (const char*)buffer + offset, + (count - index) * elemSize); +} + +SkGLTextCache::Strike* +SkGLTextCache::Strike::addGlyphAndBind(const SkGlyph& glyph, + const uint8_t image[], int* offset) { +#ifdef SK_DEBUG + SkASSERT(this->findGlyph(glyph, NULL) == NULL); + const int height = SkNextPow2(glyph.fHeight); + SkASSERT(height <= fStrikeHeight && height > (fStrikeHeight >> 1)); +#endif + + int rowBytes = glyph.rowBytes(); + SkASSERT(rowBytes >= glyph.fWidth); + + Strike* strike; + if (fGlyphCount == kMaxGlyphCount || + fNextFreeOffsetX + rowBytes >= fStrikeWidth) { + // this will bind the next texture for us +// SkDebugf("--- extend strike %p\n", this); + strike = SkNEW_ARGS(Strike, (this, rowBytes, glyph.fHeight)); + } else { + glBindTexture(GL_TEXTURE_2D, fTexName); + strike = this; + } + + uint32_t* idArray = strike->fGlyphIDArray; + uint16_t* offsetArray = strike->fGlyphOffsetX; + const int glyphCount = strike->fGlyphCount; + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexSubImage2D(GL_TEXTURE_2D, 0, strike->fNextFreeOffsetX, 0, rowBytes, + glyph.fHeight, gTextTextureFormat, gTextTextureType, + image); + + // need to insert the offset + int index = SkTSearch(idArray, glyphCount, glyph.fID, sizeof(idArray[0])); + SkASSERT(index < 0); + index = ~index; // this is where we should insert it + make_a_whole(idArray, index, glyphCount, sizeof(idArray)); + make_a_whole(offsetArray, index, glyphCount, sizeof(offsetArray[0])); + idArray[index] = glyph.fID; + offsetArray[index] = strike->fNextFreeOffsetX; + if (offset) { + *offset = strike->fNextFreeOffsetX; + } + +#if 0 + SkDebugf("--- strike %p glyph %x [%d %d] offset %d count %d\n", + strike, glyph.fID, glyph.fWidth, glyph.fHeight, + strike->fNextFreeOffsetX, glyphCount + 1); +#endif + + // now update our header + strike->fGlyphCount = glyphCount + 1; + strike->fNextFreeOffsetX += glyph.fWidth; + return strike; +} + +/////////////////////////////////////////////////////////////////////////////// + +SkGLTextCache::SkGLTextCache() { + bzero(fStrikeList, sizeof(fStrikeList)); +} + +SkGLTextCache::~SkGLTextCache() { + this->deleteAllStrikes(true); +} + +void SkGLTextCache::deleteAllStrikes(bool texturesAreValid) { + for (size_t i = 0; i < SK_ARRAY_COUNT(fStrikeList); i++) { + Strike* strike = fStrikeList[i]; + while (strike != NULL) { + Strike* next = strike->fNext; + if (!texturesAreValid) { + strike->abandonTexture(); + } + SkDELETE(strike); + strike = next; + } + } + bzero(fStrikeList, sizeof(fStrikeList)); +} + +SkGLTextCache::Strike* SkGLTextCache::findGlyph(const SkGlyph& glyph, + int* offset) { + SkASSERT(glyph.fWidth != 0); + SkASSERT(glyph.fHeight != 0); + + size_t index = SkNextLog2(glyph.fHeight); + if (index >= SK_ARRAY_COUNT(fStrikeList)) { + // too big for us to cache; + return NULL; + } + + Strike* strike = fStrikeList[index]; + if (strike) { + strike = strike->findGlyph(glyph, offset); + } + return strike; +} + +SkGLTextCache::Strike* SkGLTextCache::addGlyphAndBind(const SkGlyph& glyph, + const uint8_t image[], int* offset) { + SkASSERT(image != NULL); + SkASSERT(glyph.fWidth != 0); + SkASSERT(glyph.fHeight != 0); + + size_t index = SkNextLog2(glyph.fHeight); + if (index >= SK_ARRAY_COUNT(fStrikeList)) { + // too big for us to cache; + return NULL; + } + + Strike* strike = fStrikeList[index]; + if (NULL == strike) { + strike = SkNEW_ARGS(Strike, (NULL, glyph.rowBytes(), glyph.fHeight)); +// SkDebugf("--- create strike [%d] %p cache %p\n", index, strike, this); + } + strike = strike->addGlyphAndBind(glyph, image, offset); + fStrikeList[index] = strike; + return strike; +} + |