1 /*
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 <SkBitmap.h>
23 #include <SkCanvas.h>
24 #include <SkPaint.h>
25 #include <SkPath.h>
26 #include <SkRect.h>
27
28 #include "Debug.h"
29 #include "Properties.h"
30 #include "Texture.h"
31 #include "utils/Compare.h"
32 #include "utils/GenerationCache.h"
33
34 namespace android {
35 namespace uirenderer {
36
37 ///////////////////////////////////////////////////////////////////////////////
38 // Defines
39 ///////////////////////////////////////////////////////////////////////////////
40
41 // Debug
42 #if DEBUG_SHAPES
43 #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
44 #else
45 #define SHAPE_LOGD(...)
46 #endif
47
48 ///////////////////////////////////////////////////////////////////////////////
49 // Classes
50 ///////////////////////////////////////////////////////////////////////////////
51
52 /**
53 * Alpha texture used to represent a path.
54 */
55 struct PathTexture: public Texture {
PathTexturePathTexture56 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 /**
74 * Describe a shape in the shape cache.
75 */
76 struct ShapeCacheEntry {
77 enum ShapeType {
78 kShapeNone,
79 kShapeRect,
80 kShapeRoundRect,
81 kShapeCircle,
82 kShapeOval,
83 kShapeArc,
84 kShapePath
85 };
86
ShapeCacheEntryShapeCacheEntry87 ShapeCacheEntry() {
88 shapeType = kShapeNone;
89 join = SkPaint::kDefault_Join;
90 cap = SkPaint::kDefault_Cap;
91 style = SkPaint::kFill_Style;
92 float v = 4.0f;
93 miter = *(uint32_t*) &v;
94 v = 1.0f;
95 strokeWidth = *(uint32_t*) &v;
96 pathEffect = NULL;
97 }
98
ShapeCacheEntryShapeCacheEntry99 ShapeCacheEntry(ShapeType type, SkPaint* paint) {
100 shapeType = type;
101 join = paint->getStrokeJoin();
102 cap = paint->getStrokeCap();
103 float v = paint->getStrokeMiter();
104 miter = *(uint32_t*) &v;
105 v = paint->getStrokeWidth();
106 strokeWidth = *(uint32_t*) &v;
107 style = paint->getStyle();
108 pathEffect = paint->getPathEffect();
109 }
110
~ShapeCacheEntryShapeCacheEntry111 virtual ~ShapeCacheEntry() {
112 }
113
114 ShapeType shapeType;
115 SkPaint::Join join;
116 SkPaint::Cap cap;
117 SkPaint::Style style;
118 uint32_t miter;
119 uint32_t strokeWidth;
120 SkPathEffect* pathEffect;
121
122 bool operator<(const ShapeCacheEntry& rhs) const {
LTE_INTShapeCacheEntry123 LTE_INT(shapeType) {
124 LTE_INT(join) {
125 LTE_INT(cap) {
126 LTE_INT(style) {
127 LTE_INT(miter) {
128 LTE_INT(strokeWidth) {
129 LTE_INT(pathEffect) {
130 return lessThan(rhs);
131 }
132 }
133 }
134 }
135 }
136 }
137 }
138 return false;
139 }
140
141 protected:
lessThanShapeCacheEntry142 virtual bool lessThan(const ShapeCacheEntry& rhs) const {
143 return false;
144 }
145 }; // struct ShapeCacheEntry
146
147
148 struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
RoundRectShapeCacheEntryRoundRectShapeCacheEntry149 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
150 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
151 mWidth = *(uint32_t*) &width;
152 mHeight = *(uint32_t*) &height;
153 mRx = *(uint32_t*) ℞
154 mRy = *(uint32_t*) &ry;
155 }
156
RoundRectShapeCacheEntryRoundRectShapeCacheEntry157 RoundRectShapeCacheEntry(): ShapeCacheEntry() {
158 mWidth = 0;
159 mHeight = 0;
160 mRx = 0;
161 mRy = 0;
162 }
163
lessThanRoundRectShapeCacheEntry164 bool lessThan(const ShapeCacheEntry& r) const {
165 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
166 LTE_INT(mWidth) {
167 LTE_INT(mHeight) {
168 LTE_INT(mRx) {
169 LTE_INT(mRy) {
170 return false;
171 }
172 }
173 }
174 }
175 return false;
176 }
177
178 private:
179 uint32_t mWidth;
180 uint32_t mHeight;
181 uint32_t mRx;
182 uint32_t mRy;
183 }; // RoundRectShapeCacheEntry
184
185 struct CircleShapeCacheEntry: public ShapeCacheEntry {
CircleShapeCacheEntryCircleShapeCacheEntry186 CircleShapeCacheEntry(float radius, SkPaint* paint):
187 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
188 mRadius = *(uint32_t*) &radius;
189 }
190
CircleShapeCacheEntryCircleShapeCacheEntry191 CircleShapeCacheEntry(): ShapeCacheEntry() {
192 mRadius = 0;
193 }
194
lessThanCircleShapeCacheEntry195 bool lessThan(const ShapeCacheEntry& r) const {
196 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
197 LTE_INT(mRadius) {
198 return false;
199 }
200 return false;
201 }
202
203 private:
204 uint32_t mRadius;
205 }; // CircleShapeCacheEntry
206
207 struct OvalShapeCacheEntry: public ShapeCacheEntry {
OvalShapeCacheEntryOvalShapeCacheEntry208 OvalShapeCacheEntry(float width, float height, SkPaint* paint):
209 ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
210 mWidth = *(uint32_t*) &width;
211 mHeight = *(uint32_t*) &height;
212 }
213
OvalShapeCacheEntryOvalShapeCacheEntry214 OvalShapeCacheEntry(): ShapeCacheEntry() {
215 mWidth = mHeight = 0;
216 }
217
lessThanOvalShapeCacheEntry218 bool lessThan(const ShapeCacheEntry& r) const {
219 const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
220 LTE_INT(mWidth) {
221 LTE_INT(mHeight) {
222 return false;
223 }
224 }
225 return false;
226 }
227
228 private:
229 uint32_t mWidth;
230 uint32_t mHeight;
231 }; // OvalShapeCacheEntry
232
233 struct RectShapeCacheEntry: public ShapeCacheEntry {
RectShapeCacheEntryRectShapeCacheEntry234 RectShapeCacheEntry(float width, float height, SkPaint* paint):
235 ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
236 mWidth = *(uint32_t*) &width;
237 mHeight = *(uint32_t*) &height;
238 }
239
RectShapeCacheEntryRectShapeCacheEntry240 RectShapeCacheEntry(): ShapeCacheEntry() {
241 mWidth = mHeight = 0;
242 }
243
lessThanRectShapeCacheEntry244 bool lessThan(const ShapeCacheEntry& r) const {
245 const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
246 LTE_INT(mWidth) {
247 LTE_INT(mHeight) {
248 return false;
249 }
250 }
251 return false;
252 }
253
254 private:
255 uint32_t mWidth;
256 uint32_t mHeight;
257 }; // RectShapeCacheEntry
258
259 struct ArcShapeCacheEntry: public ShapeCacheEntry {
ArcShapeCacheEntryArcShapeCacheEntry260 ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
261 bool useCenter, SkPaint* paint):
262 ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
263 mWidth = *(uint32_t*) &width;
264 mHeight = *(uint32_t*) &height;
265 mStartAngle = *(uint32_t*) &startAngle;
266 mSweepAngle = *(uint32_t*) &sweepAngle;
267 mUseCenter = useCenter ? 1 : 0;
268 }
269
ArcShapeCacheEntryArcShapeCacheEntry270 ArcShapeCacheEntry(): ShapeCacheEntry() {
271 mWidth = 0;
272 mHeight = 0;
273 mStartAngle = 0;
274 mSweepAngle = 0;
275 mUseCenter = 0;
276 }
277
lessThanArcShapeCacheEntry278 bool lessThan(const ShapeCacheEntry& r) const {
279 const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
280 LTE_INT(mWidth) {
281 LTE_INT(mHeight) {
282 LTE_INT(mStartAngle) {
283 LTE_INT(mSweepAngle) {
284 LTE_INT(mUseCenter) {
285 return false;
286 }
287 }
288 }
289 }
290 }
291 return false;
292 }
293
294 private:
295 uint32_t mWidth;
296 uint32_t mHeight;
297 uint32_t mStartAngle;
298 uint32_t mSweepAngle;
299 uint32_t mUseCenter;
300 }; // ArcShapeCacheEntry
301
302 /**
303 * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
304 * Any texture added to the cache causing the cache to grow beyond the maximum
305 * allowed size will also cause the oldest texture to be kicked out.
306 */
307 template<typename Entry>
308 class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
309 public:
310 ShapeCache(const char* name, const char* propertyName, float defaultSize);
311 ~ShapeCache();
312
313 /**
314 * Used as a callback when an entry is removed from the cache.
315 * Do not invoke directly.
316 */
317 void operator()(Entry& path, PathTexture*& texture);
318
319 /**
320 * Clears the cache. This causes all textures to be deleted.
321 */
322 void clear();
323
324 /**
325 * Sets the maximum size of the cache in bytes.
326 */
327 void setMaxSize(uint32_t maxSize);
328 /**
329 * Returns the maximum size of the cache in bytes.
330 */
331 uint32_t getMaxSize();
332 /**
333 * Returns the current size of the cache in bytes.
334 */
335 uint32_t getSize();
336
337 protected:
338 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
339 PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
340 void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
341
342 /**
343 * Ensures there is enough space in the cache for a texture of the specified
344 * dimensions.
345 */
346 void purgeCache(uint32_t width, uint32_t height);
347
348 void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height);
349 void initPaint(SkPaint& paint);
350
351 bool checkTextureSize(uint32_t width, uint32_t height);
352
get(Entry entry)353 PathTexture* get(Entry entry) {
354 return mCache.get(entry);
355 }
356
357 void removeTexture(PathTexture* texture);
358
359 GenerationCache<Entry, PathTexture*> mCache;
360 uint32_t mSize;
361 uint32_t mMaxSize;
362 GLuint mMaxTextureSize;
363
364 char* mName;
365 bool mDebugEnabled;
366
367 private:
368 /**
369 * Generates the texture from a bitmap into the specified texture structure.
370 */
371 void generateTexture(SkBitmap& bitmap, Texture* texture);
372
373 void init();
374 }; // class ShapeCache
375
376 class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
377 public:
378 RoundRectShapeCache();
379
380 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
381 }; // class RoundRectShapeCache
382
383 class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
384 public:
385 CircleShapeCache();
386
387 PathTexture* getCircle(float radius, SkPaint* paint);
388 }; // class CircleShapeCache
389
390 class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
391 public:
392 OvalShapeCache();
393
394 PathTexture* getOval(float width, float height, SkPaint* paint);
395 }; // class OvalShapeCache
396
397 class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
398 public:
399 RectShapeCache();
400
401 PathTexture* getRect(float width, float height, SkPaint* paint);
402 }; // class RectShapeCache
403
404 class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
405 public:
406 ArcShapeCache();
407
408 PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
409 bool useCenter, SkPaint* paint);
410 }; // class ArcShapeCache
411
412 ///////////////////////////////////////////////////////////////////////////////
413 // Constructors/destructor
414 ///////////////////////////////////////////////////////////////////////////////
415
416 template<class Entry>
ShapeCache(const char * name,const char * propertyName,float defaultSize)417 ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
418 mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
419 mSize(0), mMaxSize(MB(defaultSize)) {
420 char property[PROPERTY_VALUE_MAX];
421 if (property_get(propertyName, property, NULL) > 0) {
422 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
423 setMaxSize(MB(atof(property)));
424 } else {
425 INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize);
426 }
427
428 size_t len = strlen(name);
429 mName = new char[len + 1];
430 strcpy(mName, name);
431 mName[len] = '\0';
432
433 init();
434 }
435
436 template<class Entry>
~ShapeCache()437 ShapeCache<Entry>::~ShapeCache() {
438 mCache.clear();
439 delete[] mName;
440 }
441
442 template<class Entry>
init()443 void ShapeCache<Entry>::init() {
444 mCache.setOnEntryRemovedListener(this);
445
446 GLint maxTextureSize;
447 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
448 mMaxTextureSize = maxTextureSize;
449
450 mDebugEnabled = readDebugLevel() & kDebugCaches;
451 }
452
453 ///////////////////////////////////////////////////////////////////////////////
454 // Size management
455 ///////////////////////////////////////////////////////////////////////////////
456
457 template<class Entry>
getSize()458 uint32_t ShapeCache<Entry>::getSize() {
459 return mSize;
460 }
461
462 template<class Entry>
getMaxSize()463 uint32_t ShapeCache<Entry>::getMaxSize() {
464 return mMaxSize;
465 }
466
467 template<class Entry>
setMaxSize(uint32_t maxSize)468 void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
469 mMaxSize = maxSize;
470 while (mSize > mMaxSize) {
471 mCache.removeOldest();
472 }
473 }
474
475 ///////////////////////////////////////////////////////////////////////////////
476 // Callbacks
477 ///////////////////////////////////////////////////////////////////////////////
478
479 template<class Entry>
operator()480 void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
481 removeTexture(texture);
482 }
483
484 ///////////////////////////////////////////////////////////////////////////////
485 // Caching
486 ///////////////////////////////////////////////////////////////////////////////
487
488 template<class Entry>
removeTexture(PathTexture * texture)489 void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
490 if (texture) {
491 const uint32_t size = texture->width * texture->height;
492 mSize -= size;
493
494 SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
495 mName, texture->id, size, mSize);
496 if (mDebugEnabled) {
497 ALOGD("Shape %s deleted, size = %d", mName, size);
498 }
499
500 glDeleteTextures(1, &texture->id);
501 delete texture;
502 }
503 }
504
505 void computePathBounds(const SkPath* path, const SkPaint* paint,
506 float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
507 void computeBounds(const SkRect& bounds, const SkPaint* paint,
508 float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
509
createTexture(float left,float top,float offset,uint32_t width,uint32_t height,uint32_t id)510 static PathTexture* createTexture(float left, float top, float offset,
511 uint32_t width, uint32_t height, uint32_t id) {
512 PathTexture* texture = new PathTexture;
513 texture->left = left;
514 texture->top = top;
515 texture->offset = offset;
516 texture->width = width;
517 texture->height = height;
518 texture->generation = id;
519 return texture;
520 }
521
522 template<class Entry>
purgeCache(uint32_t width,uint32_t height)523 void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
524 const uint32_t size = width * height;
525 // Don't even try to cache a bitmap that's bigger than the cache
526 if (size < mMaxSize) {
527 while (mSize + size > mMaxSize) {
528 mCache.removeOldest();
529 }
530 }
531 }
532
533 template<class Entry>
initBitmap(SkBitmap & bitmap,uint32_t width,uint32_t height)534 void ShapeCache<Entry>::initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
535 bitmap.setConfig(SkBitmap::kA8_Config, width, height);
536 bitmap.allocPixels();
537 bitmap.eraseColor(0);
538 }
539
540 template<class Entry>
initPaint(SkPaint & paint)541 void ShapeCache<Entry>::initPaint(SkPaint& paint) {
542 // Make sure the paint is opaque, color, alpha, filter, etc.
543 // will be applied later when compositing the alpha8 texture
544 paint.setColor(0xff000000);
545 paint.setAlpha(255);
546 paint.setColorFilter(NULL);
547 paint.setMaskFilter(NULL);
548 paint.setShader(NULL);
549 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
550 SkSafeUnref(paint.setXfermode(mode));
551 }
552
553 template<class Entry>
checkTextureSize(uint32_t width,uint32_t height)554 bool ShapeCache<Entry>::checkTextureSize(uint32_t width, uint32_t height) {
555 if (width > mMaxTextureSize || height > mMaxTextureSize) {
556 ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
557 mName, width, height, mMaxTextureSize, mMaxTextureSize);
558 return false;
559 }
560 return true;
561 }
562
563 template<class Entry>
addTexture(const Entry & entry,const SkPath * path,const SkPaint * paint)564 PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
565 const SkPaint* paint) {
566
567 float left, top, offset;
568 uint32_t width, height;
569 computePathBounds(path, paint, left, top, offset, width, height);
570
571 if (!checkTextureSize(width, height)) return NULL;
572
573 purgeCache(width, height);
574
575 SkBitmap bitmap;
576 initBitmap(bitmap, width, height);
577
578 SkPaint pathPaint(*paint);
579 initPaint(pathPaint);
580
581 SkCanvas canvas(bitmap);
582 canvas.translate(-left + offset, -top + offset);
583 canvas.drawPath(*path, pathPaint);
584
585 PathTexture* texture = createTexture(left, top, offset, width, height, path->getGenerationID());
586 addTexture(entry, &bitmap, texture);
587
588 return texture;
589 }
590
591 template<class Entry>
addTexture(const Entry & entry,SkBitmap * bitmap,PathTexture * texture)592 void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
593 generateTexture(*bitmap, texture);
594
595 uint32_t size = texture->width * texture->height;
596 if (size < mMaxSize) {
597 mSize += size;
598 SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
599 mName, texture->id, size, mSize);
600 if (mDebugEnabled) {
601 ALOGD("Shape %s created, size = %d", mName, size);
602 }
603 mCache.put(entry, texture);
604 } else {
605 texture->cleanup = true;
606 }
607 }
608
609 template<class Entry>
clear()610 void ShapeCache<Entry>::clear() {
611 mCache.clear();
612 }
613
614 template<class Entry>
generateTexture(SkBitmap & bitmap,Texture * texture)615 void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
616 SkAutoLockPixels alp(bitmap);
617 if (!bitmap.readyToDraw()) {
618 ALOGE("Cannot generate texture from bitmap");
619 return;
620 }
621
622 glGenTextures(1, &texture->id);
623
624 glBindTexture(GL_TEXTURE_2D, texture->id);
625 // Textures are Alpha8
626 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
627
628 texture->blend = true;
629 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
630 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
631
632 texture->setFilter(GL_LINEAR);
633 texture->setWrap(GL_CLAMP_TO_EDGE);
634 }
635
636 }; // namespace uirenderer
637 }; // namespace android
638
639 #endif // ANDROID_HWUI_SHAPE_CACHE_H
640