• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(...) LOGD(__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*) &rx;
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 
get(Entry entry)340     PathTexture* get(Entry entry) {
341         return mCache.get(entry);
342     }
343 
344     void removeTexture(PathTexture* texture);
345 
346     GenerationCache<Entry, PathTexture*> mCache;
347     uint32_t mSize;
348     uint32_t mMaxSize;
349     GLuint mMaxTextureSize;
350 
351     char* mName;
352     bool mDebugEnabled;
353 
354 private:
355     /**
356      * Generates the texture from a bitmap into the specified texture structure.
357      */
358     void generateTexture(SkBitmap& bitmap, Texture* texture);
359 
360     void init();
361 }; // class ShapeCache
362 
363 class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
364 public:
365     RoundRectShapeCache();
366 
367     PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
368 }; // class RoundRectShapeCache
369 
370 class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
371 public:
372     CircleShapeCache();
373 
374     PathTexture* getCircle(float radius, SkPaint* paint);
375 }; // class CircleShapeCache
376 
377 class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
378 public:
379     OvalShapeCache();
380 
381     PathTexture* getOval(float width, float height, SkPaint* paint);
382 }; // class OvalShapeCache
383 
384 class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
385 public:
386     RectShapeCache();
387 
388     PathTexture* getRect(float width, float height, SkPaint* paint);
389 }; // class RectShapeCache
390 
391 class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
392 public:
393     ArcShapeCache();
394 
395     PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
396             bool useCenter, SkPaint* paint);
397 }; // class ArcShapeCache
398 
399 ///////////////////////////////////////////////////////////////////////////////
400 // Constructors/destructor
401 ///////////////////////////////////////////////////////////////////////////////
402 
403 template<class Entry>
ShapeCache(const char * name,const char * propertyName,float defaultSize)404 ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
405         mCache(GenerationCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
406         mSize(0), mMaxSize(MB(defaultSize)) {
407     char property[PROPERTY_VALUE_MAX];
408     if (property_get(propertyName, property, NULL) > 0) {
409         INIT_LOGD("  Setting %s cache size to %sMB", name, property);
410         setMaxSize(MB(atof(property)));
411     } else {
412         INIT_LOGD("  Using default %s cache size of %.2fMB", name, defaultSize);
413     }
414 
415     size_t len = strlen(name);
416     mName = new char[len + 1];
417     strcpy(mName, name);
418     mName[len] = '\0';
419 
420     init();
421 }
422 
423 template<class Entry>
~ShapeCache()424 ShapeCache<Entry>::~ShapeCache() {
425     mCache.clear();
426     delete[] mName;
427 }
428 
429 template<class Entry>
init()430 void ShapeCache<Entry>::init() {
431     mCache.setOnEntryRemovedListener(this);
432 
433     GLint maxTextureSize;
434     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
435     mMaxTextureSize = maxTextureSize;
436 
437     mDebugEnabled = readDebugLevel() & kDebugCaches;
438 }
439 
440 ///////////////////////////////////////////////////////////////////////////////
441 // Size management
442 ///////////////////////////////////////////////////////////////////////////////
443 
444 template<class Entry>
getSize()445 uint32_t ShapeCache<Entry>::getSize() {
446     return mSize;
447 }
448 
449 template<class Entry>
getMaxSize()450 uint32_t ShapeCache<Entry>::getMaxSize() {
451     return mMaxSize;
452 }
453 
454 template<class Entry>
setMaxSize(uint32_t maxSize)455 void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
456     mMaxSize = maxSize;
457     while (mSize > mMaxSize) {
458         mCache.removeOldest();
459     }
460 }
461 
462 ///////////////////////////////////////////////////////////////////////////////
463 // Callbacks
464 ///////////////////////////////////////////////////////////////////////////////
465 
466 template<class Entry>
operator()467 void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
468     removeTexture(texture);
469 }
470 
471 ///////////////////////////////////////////////////////////////////////////////
472 // Caching
473 ///////////////////////////////////////////////////////////////////////////////
474 
475 template<class Entry>
removeTexture(PathTexture * texture)476 void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
477     if (texture) {
478         const uint32_t size = texture->width * texture->height;
479         mSize -= size;
480 
481         SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
482                 mName, texture->id, size, mSize);
483         if (mDebugEnabled) {
484             LOGD("Shape %s deleted, size = %d", mName, size);
485         }
486 
487         glDeleteTextures(1, &texture->id);
488         delete texture;
489     }
490 }
491 
492 template<class Entry>
addTexture(const Entry & entry,const SkPath * path,const SkPaint * paint)493 PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
494         const SkPaint* paint) {
495     const SkRect& bounds = path->getBounds();
496 
497     const float pathWidth = fmax(bounds.width(), 1.0f);
498     const float pathHeight = fmax(bounds.height(), 1.0f);
499 
500     const float offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
501 
502     const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5);
503     const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5);
504 
505     if (width > mMaxTextureSize || height > mMaxTextureSize) {
506         LOGW("Shape %s too large to be rendered into a texture", mName);
507         return NULL;
508     }
509 
510     const uint32_t size = width * height;
511     // Don't even try to cache a bitmap that's bigger than the cache
512     if (size < mMaxSize) {
513         while (mSize + size > mMaxSize) {
514             mCache.removeOldest();
515         }
516     }
517 
518     PathTexture* texture = new PathTexture;
519     texture->left = bounds.fLeft;
520     texture->top = bounds.fTop;
521     texture->offset = offset;
522     texture->width = width;
523     texture->height = height;
524     texture->generation = path->getGenerationID();
525 
526     SkBitmap bitmap;
527     bitmap.setConfig(SkBitmap::kA8_Config, width, height);
528     bitmap.allocPixels();
529     bitmap.eraseColor(0);
530 
531     SkPaint pathPaint(*paint);
532 
533     // Make sure the paint is opaque, color, alpha, filter, etc.
534     // will be applied later when compositing the alpha8 texture
535     pathPaint.setColor(0xff000000);
536     pathPaint.setAlpha(255);
537     pathPaint.setColorFilter(NULL);
538     pathPaint.setMaskFilter(NULL);
539     pathPaint.setShader(NULL);
540     SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
541     SkSafeUnref(pathPaint.setXfermode(mode));
542 
543     SkCanvas canvas(bitmap);
544     canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset);
545     canvas.drawPath(*path, pathPaint);
546 
547     generateTexture(bitmap, texture);
548 
549     if (size < mMaxSize) {
550         mSize += size;
551         SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
552                 mName, texture->id, size, mSize);
553         if (mDebugEnabled) {
554             LOGD("Shape %s created, size = %d", mName, size);
555         }
556         mCache.put(entry, texture);
557     } else {
558         texture->cleanup = true;
559     }
560 
561     return texture;
562 }
563 
564 template<class Entry>
clear()565 void ShapeCache<Entry>::clear() {
566     mCache.clear();
567 }
568 
569 template<class Entry>
generateTexture(SkBitmap & bitmap,Texture * texture)570 void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
571     SkAutoLockPixels alp(bitmap);
572     if (!bitmap.readyToDraw()) {
573         LOGE("Cannot generate texture from bitmap");
574         return;
575     }
576 
577     glGenTextures(1, &texture->id);
578 
579     glBindTexture(GL_TEXTURE_2D, texture->id);
580     // Textures are Alpha8
581     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
582 
583     texture->blend = true;
584     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
585             GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
586 
587     texture->setFilter(GL_LINEAR, GL_LINEAR);
588     texture->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
589 }
590 
591 }; // namespace uirenderer
592 }; // namespace android
593 
594 #endif // ANDROID_HWUI_SHAPE_CACHE_H
595