reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame] | 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 | 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 | 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 | |