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