blob: 910d01d85f6502ee0073b9e44db4680e6e278acd [file] [log] [blame]
Romain Guy01d58e42011-01-19 21:54:02 -08001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ANDROID_HWUI_SHAPE_CACHE_H
18#define ANDROID_HWUI_SHAPE_CACHE_H
19
20#include <GLES2/gl2.h>
21
22#include <SkCanvas.h>
23#include <SkRect.h>
24
25#include "PathCache.h"
26#include "Properties.h"
27
28namespace android {
29namespace uirenderer {
30
31///////////////////////////////////////////////////////////////////////////////
32// Defines
33///////////////////////////////////////////////////////////////////////////////
34
35// Debug
36#if DEBUG_SHAPES
37 #define SHAPE_LOGD(...) LOGD(__VA_ARGS__)
38#else
39 #define SHAPE_LOGD(...)
40#endif
41
42///////////////////////////////////////////////////////////////////////////////
43// Classes
44///////////////////////////////////////////////////////////////////////////////
45
46/**
47 * Describe a shape in the shape cache.
48 */
49struct ShapeCacheEntry {
50 enum ShapeType {
51 kShapeNone,
52 kShapeRoundRect,
53 kShapeCircle,
54 kShapeOval,
55 kShapeArc
56 };
57
58 ShapeCacheEntry() {
59 shapeType = kShapeNone;
60 join = SkPaint::kDefault_Join;
61 cap = SkPaint::kDefault_Cap;
62 style = SkPaint::kFill_Style;
63 miter = 4.0f;
64 strokeWidth = 1.0f;
65 }
66
67 ShapeCacheEntry(const ShapeCacheEntry& entry):
68 shapeType(entry.shapeType), join(entry.join), cap(entry.cap),
69 style(entry.style), miter(entry.miter),
70 strokeWidth(entry.strokeWidth) {
71 }
72
73 ShapeCacheEntry(ShapeType type, SkPaint* paint) {
74 shapeType = type;
75 join = paint->getStrokeJoin();
76 cap = paint->getStrokeCap();
77 float v = paint->getStrokeMiter();
78 miter = *(uint32_t*) &v;
79 v = paint->getStrokeWidth();
80 strokeWidth = *(uint32_t*) &v;
81 style = paint->getStyle();
82 }
83
84 virtual ~ShapeCacheEntry() {
85 }
86
87 // shapeType must be checked in subclasses operator<
88 ShapeType shapeType;
89 SkPaint::Join join;
90 SkPaint::Cap cap;
91 SkPaint::Style style;
92 uint32_t miter;
93 uint32_t strokeWidth;
94
95 bool operator<(const ShapeCacheEntry& rhs) const {
96 LTE_INT(shapeType) {
97 LTE_INT(join) {
98 LTE_INT(cap) {
99 LTE_INT(style) {
100 LTE_INT(miter) {
101 LTE_INT(strokeWidth) {
102 return lessThan(rhs);
103 }
104 }
105 }
106 }
107 }
108 }
109 return false;
110 }
111
112protected:
113 virtual bool lessThan(const ShapeCacheEntry& rhs) const {
114 return false;
115 }
116}; // struct ShapeCacheEntry
117
118
119struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
120 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
121 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
122 mWidth = *(uint32_t*) &width;
123 mHeight = *(uint32_t*) &height;
124 mRx = *(uint32_t*) &rx;
125 mRy = *(uint32_t*) &ry;
126 }
127
128 RoundRectShapeCacheEntry(): ShapeCacheEntry() {
129 mWidth = 0;
130 mHeight = 0;
131 mRx = 0;
132 mRy = 0;
133 }
134
135 RoundRectShapeCacheEntry(const RoundRectShapeCacheEntry& entry):
136 ShapeCacheEntry(entry) {
137 mWidth = entry.mWidth;
138 mHeight = entry.mHeight;
139 mRx = entry.mRx;
140 mRy = entry.mRy;
141 }
142
143 bool lessThan(const ShapeCacheEntry& r) const {
144 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
145 LTE_INT(mWidth) {
146 LTE_INT(mHeight) {
147 LTE_INT(mRx) {
148 LTE_INT(mRy) {
149 return false;
150 }
151 }
152 }
153 }
154 return false;
155 }
156
157private:
158 uint32_t mWidth;
159 uint32_t mHeight;
160 uint32_t mRx;
161 uint32_t mRy;
162}; // RoundRectShapeCacheEntry
163
164struct CircleShapeCacheEntry: public ShapeCacheEntry {
165 CircleShapeCacheEntry(float radius, SkPaint* paint):
166 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
167 mRadius = *(uint32_t*) &radius;
168 }
169
170 CircleShapeCacheEntry(): ShapeCacheEntry() {
171 mRadius = 0;
172 }
173
174 CircleShapeCacheEntry(const CircleShapeCacheEntry& entry):
175 ShapeCacheEntry(entry) {
176 mRadius = entry.mRadius;
177 }
178
179 bool lessThan(const ShapeCacheEntry& r) const {
180 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
181 LTE_INT(mRadius) {
182 return false;
183 }
184 return false;
185 }
186
187private:
188 uint32_t mRadius;
189}; // CircleShapeCacheEntry
190
191/**
192 * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
193 * Any texture added to the cache causing the cache to grow beyond the maximum
194 * allowed size will also cause the oldest texture to be kicked out.
195 */
196template<typename Entry>
197class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
198public:
199 ShapeCache();
200 ShapeCache(uint32_t maxByteSize);
201 ~ShapeCache();
202
203 /**
204 * Used as a callback when an entry is removed from the cache.
205 * Do not invoke directly.
206 */
207 void operator()(Entry& path, PathTexture*& texture);
208
209 /**
210 * Clears the cache. This causes all textures to be deleted.
211 */
212 void clear();
213
214 /**
215 * Sets the maximum size of the cache in bytes.
216 */
217 void setMaxSize(uint32_t maxSize);
218 /**
219 * Returns the maximum size of the cache in bytes.
220 */
221 uint32_t getMaxSize();
222 /**
223 * Returns the current size of the cache in bytes.
224 */
225 uint32_t getSize();
226
227protected:
228 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
229
230 PathTexture* get(Entry entry) {
231 return mCache.get(entry);
232 }
233
234private:
235 /**
236 * Generates the texture from a bitmap into the specified texture structure.
237 */
238 void generateTexture(SkBitmap& bitmap, Texture* texture);
239
240 void removeTexture(PathTexture* texture);
241
242 void init();
243
244 GenerationCache<Entry, PathTexture*> mCache;
245 uint32_t mSize;
246 uint32_t mMaxSize;
247 GLuint mMaxTextureSize;
248
249 bool mDebugEnabled;
250}; // class ShapeCache
251
252class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
253public:
254 RoundRectShapeCache();
255
256 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
257}; // class RoundRectShapeCache
258
259class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
260public:
261 CircleShapeCache();
262
263 PathTexture* getCircle(float radius, SkPaint* paint);
264}; // class RoundRectShapeCache
265
266
267///////////////////////////////////////////////////////////////////////////////
268// Constructors/destructor
269///////////////////////////////////////////////////////////////////////////////
270
271template<class Entry>
272ShapeCache<Entry>::ShapeCache():
273 mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
274 mSize(0), mMaxSize(MB(DEFAULT_SHAPE_CACHE_SIZE)) {
275 char property[PROPERTY_VALUE_MAX];
276 if (property_get(PROPERTY_SHAPE_CACHE_SIZE, property, NULL) > 0) {
277 LOGD(" Setting shape cache size to %sMB", property);
278 setMaxSize(MB(atof(property)));
279 } else {
280 LOGD(" Using default shape cache size of %.2fMB", DEFAULT_SHAPE_CACHE_SIZE);
281 }
282 init();
283}
284
285template<class Entry>
286ShapeCache<Entry>::ShapeCache(uint32_t maxByteSize):
287 mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
288 mSize(0), mMaxSize(maxByteSize) {
289 init();
290}
291
292template<class Entry>
293ShapeCache<Entry>::~ShapeCache() {
294 mCache.clear();
295}
296
297template<class Entry>
298void ShapeCache<Entry>::init() {
299 mCache.setOnEntryRemovedListener(this);
300
301 GLint maxTextureSize;
302 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
303 mMaxTextureSize = maxTextureSize;
304
305 mDebugEnabled = readDebugLevel() & kDebugCaches;
306}
307
308///////////////////////////////////////////////////////////////////////////////
309// Size management
310///////////////////////////////////////////////////////////////////////////////
311
312template<class Entry>
313uint32_t ShapeCache<Entry>::getSize() {
314 return mSize;
315}
316
317template<class Entry>
318uint32_t ShapeCache<Entry>::getMaxSize() {
319 return mMaxSize;
320}
321
322template<class Entry>
323void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
324 mMaxSize = maxSize;
325 while (mSize > mMaxSize) {
326 mCache.removeOldest();
327 }
328}
329
330///////////////////////////////////////////////////////////////////////////////
331// Callbacks
332///////////////////////////////////////////////////////////////////////////////
333
334template<class Entry>
335void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
336 removeTexture(texture);
337}
338
339///////////////////////////////////////////////////////////////////////////////
340// Caching
341///////////////////////////////////////////////////////////////////////////////
342
343template<class Entry>
344void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
345 if (texture) {
346 const uint32_t size = texture->width * texture->height;
347 mSize -= size;
348
349 SHAPE_LOGD("ShapeCache::callback: delete path: name, size, mSize = %d, %d, %d",
350 texture->id, size, mSize);
351 if (mDebugEnabled) {
352 LOGD("Path deleted, size = %d", size);
353 }
354
355 glDeleteTextures(1, &texture->id);
356 delete texture;
357 }
358}
359
360template<class Entry>
361PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
362 const SkPaint* paint) {
363 const SkRect& bounds = path->getBounds();
364
365 const float pathWidth = fmax(bounds.width(), 1.0f);
366 const float pathHeight = fmax(bounds.height(), 1.0f);
367
368 if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
369 LOGW("Shape too large to be rendered into a texture");
370 return NULL;
371 }
372
373 const float offset = paint->getStrokeWidth() * 1.5f;
374 const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
375 const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
376
377 const uint32_t size = width * height;
378 // Don't even try to cache a bitmap that's bigger than the cache
379 if (size < mMaxSize) {
380 while (mSize + size > mMaxSize) {
381 mCache.removeOldest();
382 }
383 }
384
385 PathTexture* texture = new PathTexture;
386 texture->left = bounds.fLeft;
387 texture->top = bounds.fTop;
388 texture->offset = offset;
389 texture->width = width;
390 texture->height = height;
391 texture->generation = path->getGenerationID();
392
393 SkBitmap bitmap;
394 bitmap.setConfig(SkBitmap::kA8_Config, width, height);
395 bitmap.allocPixels();
396 bitmap.eraseColor(0);
397
398 SkPaint pathPaint(*paint);
399
400 // Make sure the paint is opaque, color, alpha, filter, etc.
401 // will be applied later when compositing the alpha8 texture
402 pathPaint.setColor(0xff000000);
403 pathPaint.setAlpha(255);
404 pathPaint.setColorFilter(NULL);
405 pathPaint.setMaskFilter(NULL);
406 pathPaint.setShader(NULL);
407 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
408 pathPaint.setXfermode(mode)->safeUnref();
409
410 SkCanvas canvas(bitmap);
411 canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
412 canvas.drawPath(*path, pathPaint);
413
414 generateTexture(bitmap, texture);
415
416 if (size < mMaxSize) {
417 mSize += size;
418 SHAPE_LOGD("ShapeCache::get: create path: name, size, mSize = %d, %d, %d",
419 texture->id, size, mSize);
420 if (mDebugEnabled) {
421 LOGD("Shape created, size = %d", size);
422 }
423 mCache.put(entry, texture);
424 } else {
425 texture->cleanup = true;
426 }
427
428 return texture;
429}
430
431template<class Entry>
432void ShapeCache<Entry>::clear() {
433 mCache.clear();
434}
435
436template<class Entry>
437void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
438 SkAutoLockPixels alp(bitmap);
439 if (!bitmap.readyToDraw()) {
440 LOGE("Cannot generate texture from bitmap");
441 return;
442 }
443
444 glGenTextures(1, &texture->id);
445
446 glBindTexture(GL_TEXTURE_2D, texture->id);
447 // Textures are Alpha8
448 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
449
450 texture->blend = true;
451 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
452 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
453
454 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
455 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
456
457 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
458 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
459}
460
461}; // namespace uirenderer
462}; // namespace android
463
464#endif // ANDROID_HWUI_SHAPE_CACHE_H