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