blob: 141e1001e418da7a545967fa2a7ac044e7b8f67b [file] [log] [blame]
reed@android.com8a1c16f2008-12-17 15:59:43 +00001#include "SkGLTextCache.h"
2#include "SkScalerContext.h"
3#include "SkTSearch.h"
4
5const GLenum gTextTextureFormat = GL_ALPHA;
6const GLenum gTextTextureType = GL_UNSIGNED_BYTE;
7
8SkGLTextCache::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
36SkGLTextCache::Strike::~Strike() {
37 if (fTexName != 0) {
38 glDeleteTextures(1, &fTexName);
39 }
40}
41
42SkGLTextCache::Strike*
43SkGLTextCache::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
63static 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
71SkGLTextCache::Strike*
72SkGLTextCache::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
129SkGLTextCache::SkGLTextCache() {
130 bzero(fStrikeList, sizeof(fStrikeList));
131}
132
133SkGLTextCache::~SkGLTextCache() {
134 this->deleteAllStrikes(true);
135}
136
137void 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
152SkGLTextCache::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
170SkGLTextCache::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