blob: c62793180838a4648f73d6318429b212aeac9afd [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
Romain Guyff26a0c2011-01-20 11:35:46 -080022#include <SkBitmap.h>
Romain Guy01d58e42011-01-19 21:54:02 -080023#include <SkCanvas.h>
Romain Guyff26a0c2011-01-20 11:35:46 -080024#include <SkPaint.h>
25#include <SkPath.h>
Romain Guy01d58e42011-01-19 21:54:02 -080026#include <SkRect.h>
27
Romain Guyff26a0c2011-01-20 11:35:46 -080028#include "Debug.h"
Romain Guy01d58e42011-01-19 21:54:02 -080029#include "Properties.h"
Romain Guyff26a0c2011-01-20 11:35:46 -080030#include "Texture.h"
31#include "utils/Compare.h"
32#include "utils/GenerationCache.h"
Romain Guy01d58e42011-01-19 21:54:02 -080033
34namespace android {
35namespace uirenderer {
36
37///////////////////////////////////////////////////////////////////////////////
38// Defines
39///////////////////////////////////////////////////////////////////////////////
40
41// Debug
42#if DEBUG_SHAPES
43 #define SHAPE_LOGD(...) LOGD(__VA_ARGS__)
44#else
45 #define SHAPE_LOGD(...)
46#endif
47
48///////////////////////////////////////////////////////////////////////////////
49// Classes
50///////////////////////////////////////////////////////////////////////////////
51
52/**
Romain Guyff26a0c2011-01-20 11:35:46 -080053 * Alpha texture used to represent a path.
54 */
55struct PathTexture: public Texture {
56 PathTexture(): Texture() {
57 }
58
59 /**
60 * Left coordinate of the path bounds.
61 */
62 float left;
63 /**
64 * Top coordinate of the path bounds.
65 */
66 float top;
67 /**
68 * Offset to draw the path at the correct origin.
69 */
70 float offset;
71}; // struct PathTexture
72
73/**
Romain Guy01d58e42011-01-19 21:54:02 -080074 * Describe a shape in the shape cache.
75 */
76struct ShapeCacheEntry {
77 enum ShapeType {
78 kShapeNone,
79 kShapeRoundRect,
80 kShapeCircle,
81 kShapeOval,
Romain Guyff26a0c2011-01-20 11:35:46 -080082 kShapeArc,
83 kShapePath
Romain Guy01d58e42011-01-19 21:54:02 -080084 };
85
86 ShapeCacheEntry() {
87 shapeType = kShapeNone;
88 join = SkPaint::kDefault_Join;
89 cap = SkPaint::kDefault_Cap;
90 style = SkPaint::kFill_Style;
91 miter = 4.0f;
92 strokeWidth = 1.0f;
93 }
94
95 ShapeCacheEntry(const ShapeCacheEntry& entry):
96 shapeType(entry.shapeType), join(entry.join), cap(entry.cap),
97 style(entry.style), miter(entry.miter),
98 strokeWidth(entry.strokeWidth) {
99 }
100
101 ShapeCacheEntry(ShapeType type, SkPaint* paint) {
102 shapeType = type;
103 join = paint->getStrokeJoin();
104 cap = paint->getStrokeCap();
105 float v = paint->getStrokeMiter();
106 miter = *(uint32_t*) &v;
107 v = paint->getStrokeWidth();
108 strokeWidth = *(uint32_t*) &v;
109 style = paint->getStyle();
110 }
111
112 virtual ~ShapeCacheEntry() {
113 }
114
115 // shapeType must be checked in subclasses operator<
116 ShapeType shapeType;
117 SkPaint::Join join;
118 SkPaint::Cap cap;
119 SkPaint::Style style;
120 uint32_t miter;
121 uint32_t strokeWidth;
122
123 bool operator<(const ShapeCacheEntry& rhs) const {
124 LTE_INT(shapeType) {
125 LTE_INT(join) {
126 LTE_INT(cap) {
127 LTE_INT(style) {
128 LTE_INT(miter) {
129 LTE_INT(strokeWidth) {
130 return lessThan(rhs);
131 }
132 }
133 }
134 }
135 }
136 }
137 return false;
138 }
139
140protected:
141 virtual bool lessThan(const ShapeCacheEntry& rhs) const {
142 return false;
143 }
144}; // struct ShapeCacheEntry
145
146
147struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
148 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
149 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
150 mWidth = *(uint32_t*) &width;
151 mHeight = *(uint32_t*) &height;
152 mRx = *(uint32_t*) &rx;
153 mRy = *(uint32_t*) &ry;
154 }
155
156 RoundRectShapeCacheEntry(): ShapeCacheEntry() {
157 mWidth = 0;
158 mHeight = 0;
159 mRx = 0;
160 mRy = 0;
161 }
162
163 RoundRectShapeCacheEntry(const RoundRectShapeCacheEntry& entry):
164 ShapeCacheEntry(entry) {
165 mWidth = entry.mWidth;
166 mHeight = entry.mHeight;
167 mRx = entry.mRx;
168 mRy = entry.mRy;
169 }
170
171 bool lessThan(const ShapeCacheEntry& r) const {
172 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
173 LTE_INT(mWidth) {
174 LTE_INT(mHeight) {
175 LTE_INT(mRx) {
176 LTE_INT(mRy) {
177 return false;
178 }
179 }
180 }
181 }
182 return false;
183 }
184
185private:
186 uint32_t mWidth;
187 uint32_t mHeight;
188 uint32_t mRx;
189 uint32_t mRy;
190}; // RoundRectShapeCacheEntry
191
192struct CircleShapeCacheEntry: public ShapeCacheEntry {
193 CircleShapeCacheEntry(float radius, SkPaint* paint):
194 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
195 mRadius = *(uint32_t*) &radius;
196 }
197
198 CircleShapeCacheEntry(): ShapeCacheEntry() {
199 mRadius = 0;
200 }
201
202 CircleShapeCacheEntry(const CircleShapeCacheEntry& entry):
203 ShapeCacheEntry(entry) {
204 mRadius = entry.mRadius;
205 }
206
207 bool lessThan(const ShapeCacheEntry& r) const {
208 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
209 LTE_INT(mRadius) {
210 return false;
211 }
212 return false;
213 }
214
215private:
216 uint32_t mRadius;
217}; // CircleShapeCacheEntry
218
219/**
220 * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
221 * Any texture added to the cache causing the cache to grow beyond the maximum
222 * allowed size will also cause the oldest texture to be kicked out.
223 */
224template<typename Entry>
225class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
226public:
Romain Guyff26a0c2011-01-20 11:35:46 -0800227 ShapeCache(const char* name, const char* propertyName, float defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800228 ~ShapeCache();
229
230 /**
231 * Used as a callback when an entry is removed from the cache.
232 * Do not invoke directly.
233 */
234 void operator()(Entry& path, PathTexture*& texture);
235
236 /**
237 * Clears the cache. This causes all textures to be deleted.
238 */
239 void clear();
240
241 /**
242 * Sets the maximum size of the cache in bytes.
243 */
244 void setMaxSize(uint32_t maxSize);
245 /**
246 * Returns the maximum size of the cache in bytes.
247 */
248 uint32_t getMaxSize();
249 /**
250 * Returns the current size of the cache in bytes.
251 */
252 uint32_t getSize();
253
254protected:
255 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
256
257 PathTexture* get(Entry entry) {
258 return mCache.get(entry);
259 }
260
Romain Guy01d58e42011-01-19 21:54:02 -0800261 void removeTexture(PathTexture* texture);
262
Romain Guy01d58e42011-01-19 21:54:02 -0800263 GenerationCache<Entry, PathTexture*> mCache;
264 uint32_t mSize;
265 uint32_t mMaxSize;
266 GLuint mMaxTextureSize;
267
Romain Guyff26a0c2011-01-20 11:35:46 -0800268 char* mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800269 bool mDebugEnabled;
Romain Guyff26a0c2011-01-20 11:35:46 -0800270
271private:
272 /**
273 * Generates the texture from a bitmap into the specified texture structure.
274 */
275 void generateTexture(SkBitmap& bitmap, Texture* texture);
276
277 void init();
Romain Guy01d58e42011-01-19 21:54:02 -0800278}; // class ShapeCache
279
280class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
281public:
282 RoundRectShapeCache();
283
284 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
285}; // class RoundRectShapeCache
286
287class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
288public:
289 CircleShapeCache();
290
291 PathTexture* getCircle(float radius, SkPaint* paint);
292}; // class RoundRectShapeCache
293
294
295///////////////////////////////////////////////////////////////////////////////
296// Constructors/destructor
297///////////////////////////////////////////////////////////////////////////////
298
299template<class Entry>
Romain Guyff26a0c2011-01-20 11:35:46 -0800300ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
Romain Guy01d58e42011-01-19 21:54:02 -0800301 mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
Romain Guyff26a0c2011-01-20 11:35:46 -0800302 mSize(0), mMaxSize(MB(defaultSize)) {
Romain Guy01d58e42011-01-19 21:54:02 -0800303 char property[PROPERTY_VALUE_MAX];
Romain Guyff26a0c2011-01-20 11:35:46 -0800304 if (property_get(propertyName, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800305 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
Romain Guy01d58e42011-01-19 21:54:02 -0800306 setMaxSize(MB(atof(property)));
307 } else {
Romain Guyc9855a52011-01-21 21:14:15 -0800308 INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800309 }
Romain Guy01d58e42011-01-19 21:54:02 -0800310
Romain Guyff26a0c2011-01-20 11:35:46 -0800311 size_t len = strlen(name);
312 mName = new char[len + 1];
313 strcpy(mName, name);
314 mName[len] = '\0';
315
Romain Guy01d58e42011-01-19 21:54:02 -0800316 init();
317}
318
319template<class Entry>
320ShapeCache<Entry>::~ShapeCache() {
321 mCache.clear();
Romain Guyff26a0c2011-01-20 11:35:46 -0800322 delete[] mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800323}
324
325template<class Entry>
326void ShapeCache<Entry>::init() {
327 mCache.setOnEntryRemovedListener(this);
328
329 GLint maxTextureSize;
330 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
331 mMaxTextureSize = maxTextureSize;
332
333 mDebugEnabled = readDebugLevel() & kDebugCaches;
334}
335
336///////////////////////////////////////////////////////////////////////////////
337// Size management
338///////////////////////////////////////////////////////////////////////////////
339
340template<class Entry>
341uint32_t ShapeCache<Entry>::getSize() {
342 return mSize;
343}
344
345template<class Entry>
346uint32_t ShapeCache<Entry>::getMaxSize() {
347 return mMaxSize;
348}
349
350template<class Entry>
351void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
352 mMaxSize = maxSize;
353 while (mSize > mMaxSize) {
354 mCache.removeOldest();
355 }
356}
357
358///////////////////////////////////////////////////////////////////////////////
359// Callbacks
360///////////////////////////////////////////////////////////////////////////////
361
362template<class Entry>
363void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
364 removeTexture(texture);
365}
366
367///////////////////////////////////////////////////////////////////////////////
368// Caching
369///////////////////////////////////////////////////////////////////////////////
370
371template<class Entry>
372void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
373 if (texture) {
374 const uint32_t size = texture->width * texture->height;
375 mSize -= size;
376
Romain Guyff26a0c2011-01-20 11:35:46 -0800377 SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
378 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800379 if (mDebugEnabled) {
Romain Guyff26a0c2011-01-20 11:35:46 -0800380 LOGD("Shape %s deleted, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800381 }
382
383 glDeleteTextures(1, &texture->id);
384 delete texture;
385 }
386}
387
388template<class Entry>
389PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
390 const SkPaint* paint) {
391 const SkRect& bounds = path->getBounds();
392
393 const float pathWidth = fmax(bounds.width(), 1.0f);
394 const float pathHeight = fmax(bounds.height(), 1.0f);
395
396 if (pathWidth > mMaxTextureSize || pathHeight > mMaxTextureSize) {
Romain Guyff26a0c2011-01-20 11:35:46 -0800397 LOGW("Shape %s too large to be rendered into a texture", mName);
Romain Guy01d58e42011-01-19 21:54:02 -0800398 return NULL;
399 }
400
401 const float offset = paint->getStrokeWidth() * 1.5f;
402 const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
403 const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
404
405 const uint32_t size = width * height;
406 // Don't even try to cache a bitmap that's bigger than the cache
407 if (size < mMaxSize) {
408 while (mSize + size > mMaxSize) {
409 mCache.removeOldest();
410 }
411 }
412
413 PathTexture* texture = new PathTexture;
414 texture->left = bounds.fLeft;
415 texture->top = bounds.fTop;
416 texture->offset = offset;
417 texture->width = width;
418 texture->height = height;
419 texture->generation = path->getGenerationID();
420
421 SkBitmap bitmap;
422 bitmap.setConfig(SkBitmap::kA8_Config, width, height);
423 bitmap.allocPixels();
424 bitmap.eraseColor(0);
425
426 SkPaint pathPaint(*paint);
427
428 // Make sure the paint is opaque, color, alpha, filter, etc.
429 // will be applied later when compositing the alpha8 texture
430 pathPaint.setColor(0xff000000);
431 pathPaint.setAlpha(255);
432 pathPaint.setColorFilter(NULL);
433 pathPaint.setMaskFilter(NULL);
434 pathPaint.setShader(NULL);
435 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
436 pathPaint.setXfermode(mode)->safeUnref();
437
438 SkCanvas canvas(bitmap);
439 canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
440 canvas.drawPath(*path, pathPaint);
441
442 generateTexture(bitmap, texture);
443
444 if (size < mMaxSize) {
445 mSize += size;
Romain Guyff26a0c2011-01-20 11:35:46 -0800446 SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
447 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800448 if (mDebugEnabled) {
Romain Guyff26a0c2011-01-20 11:35:46 -0800449 LOGD("Shape %s created, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800450 }
451 mCache.put(entry, texture);
452 } else {
453 texture->cleanup = true;
454 }
455
456 return texture;
457}
458
459template<class Entry>
460void ShapeCache<Entry>::clear() {
461 mCache.clear();
462}
463
464template<class Entry>
465void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
466 SkAutoLockPixels alp(bitmap);
467 if (!bitmap.readyToDraw()) {
468 LOGE("Cannot generate texture from bitmap");
469 return;
470 }
471
472 glGenTextures(1, &texture->id);
473
474 glBindTexture(GL_TEXTURE_2D, texture->id);
475 // Textures are Alpha8
476 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
477
478 texture->blend = true;
479 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
480 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
481
482 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
483 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
484
485 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
486 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
487}
488
489}; // namespace uirenderer
490}; // namespace android
491
492#endif // ANDROID_HWUI_SHAPE_CACHE_H