C++程序  |  192行  |  6.09 KB

#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;
}