Home | History | Annotate | Download | only in gl
      1 #include "SkGLTextCache.h"
      2 #include "SkScalerContext.h"
      3 #include "SkTSearch.h"
      4 
      5 const GLenum gTextTextureFormat = GL_ALPHA;
      6 const GLenum gTextTextureType = GL_UNSIGNED_BYTE;
      7 
      8 SkGLTextCache::Strike::Strike(Strike* next, int width, int height) {
      9     fStrikeWidth = SkNextPow2(SkMax32(kMinStrikeWidth, width));
     10     fStrikeHeight = SkNextPow2(height);
     11     fGlyphCount = 0;
     12     fNextFreeOffsetX = 0;
     13     fNext = next;
     14 
     15     fStrikeWidthShift = SkNextLog2(fStrikeWidth);
     16     fStrikeHeightShift = SkNextLog2(fStrikeHeight);
     17 
     18     if (next) {
     19         SkASSERT(next->fStrikeHeight == fStrikeHeight);
     20     }
     21 
     22     // create an empty texture to receive glyphs
     23     fTexName = 0;
     24     glGenTextures(1, &fTexName);
     25     glBindTexture(GL_TEXTURE_2D, fTexName);
     26     glTexImage2D(GL_TEXTURE_2D, 0, gTextTextureFormat,
     27                  fStrikeWidth, fStrikeHeight, 0,
     28                  gTextTextureFormat, gTextTextureType, NULL);
     29 
     30     SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     31     SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     32     SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     33     SK_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     34 }
     35 
     36 SkGLTextCache::Strike::~Strike() {
     37     if (fTexName != 0) {
     38         glDeleteTextures(1, &fTexName);
     39     }
     40 }
     41 
     42 SkGLTextCache::Strike*
     43 SkGLTextCache::Strike::findGlyph(const SkGlyph& glyph, int* offset) {
     44     Strike* strike = this;
     45     SkDEBUGCODE(const int height = SkNextPow2(glyph.fHeight);)
     46 
     47     do {
     48         SkASSERT(height == strike->fStrikeHeight);
     49 
     50         int index = SkTSearch(strike->fGlyphIDArray, strike->fGlyphCount,
     51                               glyph.fID, sizeof(strike->fGlyphIDArray[0]));
     52         if (index >= 0) {
     53             if (offset) {
     54                 *offset = strike->fGlyphOffsetX[index];
     55             }
     56             return strike;
     57         }
     58         strike = strike->fNext;
     59     } while (NULL != strike);
     60     return NULL;
     61 }
     62 
     63 static void make_a_whole(void* buffer, int index, int count, size_t elemSize) {
     64     SkASSERT(index >= 0 && index <= count);
     65     size_t offset = index * elemSize;
     66     memmove((char*)buffer + offset + elemSize,
     67             (const char*)buffer + offset,
     68             (count - index) * elemSize);
     69 }
     70 
     71 SkGLTextCache::Strike*
     72 SkGLTextCache::Strike::addGlyphAndBind(const SkGlyph& glyph,
     73                                        const uint8_t image[], int* offset) {
     74 #ifdef SK_DEBUG
     75     SkASSERT(this->findGlyph(glyph, NULL) == NULL);
     76     const int height = SkNextPow2(glyph.fHeight);
     77     SkASSERT(height <= fStrikeHeight && height > (fStrikeHeight >> 1));
     78 #endif
     79 
     80     int rowBytes = glyph.rowBytes();
     81     SkASSERT(rowBytes >= glyph.fWidth);
     82 
     83     Strike* strike;
     84     if (fGlyphCount == kMaxGlyphCount ||
     85             fNextFreeOffsetX + rowBytes >= fStrikeWidth) {
     86         // this will bind the next texture for us
     87 //        SkDebugf("--- extend strike %p\n", this);
     88         strike = SkNEW_ARGS(Strike, (this, rowBytes, glyph.fHeight));
     89     } else {
     90         glBindTexture(GL_TEXTURE_2D, fTexName);
     91         strike = this;
     92     }
     93 
     94     uint32_t* idArray = strike->fGlyphIDArray;
     95     uint16_t* offsetArray = strike->fGlyphOffsetX;
     96     const int glyphCount = strike->fGlyphCount;
     97 
     98     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
     99     glTexSubImage2D(GL_TEXTURE_2D, 0, strike->fNextFreeOffsetX, 0, rowBytes,
    100                     glyph.fHeight, gTextTextureFormat, gTextTextureType,
    101                     image);
    102 
    103     // need to insert the offset
    104     int index = SkTSearch(idArray, glyphCount, glyph.fID, sizeof(idArray[0]));
    105     SkASSERT(index < 0);
    106     index = ~index; // this is where we should insert it
    107     make_a_whole(idArray, index, glyphCount, sizeof(idArray));
    108     make_a_whole(offsetArray, index, glyphCount, sizeof(offsetArray[0]));
    109     idArray[index] = glyph.fID;
    110     offsetArray[index] = strike->fNextFreeOffsetX;
    111     if (offset) {
    112         *offset = strike->fNextFreeOffsetX;
    113     }
    114 
    115 #if 0
    116     SkDebugf("--- strike %p glyph %x [%d %d] offset %d count %d\n",
    117              strike, glyph.fID, glyph.fWidth, glyph.fHeight,
    118              strike->fNextFreeOffsetX, glyphCount + 1);
    119 #endif
    120 
    121     // now update our header
    122     strike->fGlyphCount = glyphCount + 1;
    123     strike->fNextFreeOffsetX += glyph.fWidth;
    124     return strike;
    125 }
    126 
    127 ///////////////////////////////////////////////////////////////////////////////
    128 
    129 SkGLTextCache::SkGLTextCache() {
    130     sk_bzero(fStrikeList, sizeof(fStrikeList));
    131 }
    132 
    133 SkGLTextCache::~SkGLTextCache() {
    134     this->deleteAllStrikes(true);
    135 }
    136 
    137 void SkGLTextCache::deleteAllStrikes(bool texturesAreValid) {
    138     for (size_t i = 0; i < SK_ARRAY_COUNT(fStrikeList); i++) {
    139         Strike* strike = fStrikeList[i];
    140         while (strike != NULL) {
    141             Strike* next = strike->fNext;
    142             if (!texturesAreValid) {
    143                 strike->abandonTexture();
    144             }
    145             SkDELETE(strike);
    146             strike = next;
    147         }
    148     }
    149     sk_bzero(fStrikeList, sizeof(fStrikeList));
    150 }
    151 
    152 SkGLTextCache::Strike* SkGLTextCache::findGlyph(const SkGlyph& glyph,
    153                                                 int* offset) {
    154     SkASSERT(glyph.fWidth != 0);
    155     SkASSERT(glyph.fHeight != 0);
    156 
    157     size_t index = SkNextLog2(glyph.fHeight);
    158     if (index >= SK_ARRAY_COUNT(fStrikeList)) {
    159         // too big for us to cache;
    160         return NULL;
    161     }
    162 
    163     Strike* strike = fStrikeList[index];
    164     if (strike) {
    165         strike = strike->findGlyph(glyph, offset);
    166     }
    167     return strike;
    168 }
    169 
    170 SkGLTextCache::Strike* SkGLTextCache::addGlyphAndBind(const SkGlyph& glyph,
    171                                         const uint8_t image[], int* offset) {
    172     SkASSERT(image != NULL);
    173     SkASSERT(glyph.fWidth != 0);
    174     SkASSERT(glyph.fHeight != 0);
    175 
    176     size_t index = SkNextLog2(glyph.fHeight);
    177     if (index >= SK_ARRAY_COUNT(fStrikeList)) {
    178         // too big for us to cache;
    179         return NULL;
    180     }
    181 
    182     Strike* strike = fStrikeList[index];
    183     if (NULL == strike) {
    184         strike = SkNEW_ARGS(Strike, (NULL, glyph.rowBytes(), glyph.fHeight));
    185 //        SkDebugf("--- create strike [%d] %p cache %p\n", index, strike, this);
    186     }
    187     strike = strike->addGlyphAndBind(glyph, image, offset);
    188     fStrikeList[index] = strike;
    189     return strike;
    190 }
    191 
    192