• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Strike(Strike * next,int width,int height)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 
~Strike()36 SkGLTextCache::Strike::~Strike() {
37     if (fTexName != 0) {
38         glDeleteTextures(1, &fTexName);
39     }
40 }
41 
42 SkGLTextCache::Strike*
findGlyph(const SkGlyph & glyph,int * offset)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 
make_a_whole(void * buffer,int index,int count,size_t elemSize)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*
addGlyphAndBind(const SkGlyph & glyph,const uint8_t image[],int * offset)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 
SkGLTextCache()129 SkGLTextCache::SkGLTextCache() {
130     sk_bzero(fStrikeList, sizeof(fStrikeList));
131 }
132 
~SkGLTextCache()133 SkGLTextCache::~SkGLTextCache() {
134     this->deleteAllStrikes(true);
135 }
136 
deleteAllStrikes(bool texturesAreValid)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 
findGlyph(const SkGlyph & glyph,int * offset)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 
addGlyphAndBind(const SkGlyph & glyph,const uint8_t image[],int * offset)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