ShapeCache.h revision 1afd5bab4e0eaba8b5bc2ab5c7b556cd602cf2e7
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
34namespace android {
35namespace 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 */
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/**
74 * Describe a shape in the shape cache.
75 */
76struct ShapeCacheEntry {
77    enum ShapeType {
78        kShapeNone,
79        kShapeRect,
80        kShapeRoundRect,
81        kShapeCircle,
82        kShapeOval,
83        kShapeArc,
84        kShapePath
85    };
86
87    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
99    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
111    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 {
123        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
141protected:
142    virtual bool lessThan(const ShapeCacheEntry& rhs) const {
143        return false;
144    }
145}; // struct ShapeCacheEntry
146
147
148struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
149    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
157    RoundRectShapeCacheEntry(): ShapeCacheEntry() {
158        mWidth = 0;
159        mHeight = 0;
160        mRx = 0;
161        mRy = 0;
162    }
163
164    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
178private:
179    uint32_t mWidth;
180    uint32_t mHeight;
181    uint32_t mRx;
182    uint32_t mRy;
183}; // RoundRectShapeCacheEntry
184
185struct CircleShapeCacheEntry: public ShapeCacheEntry {
186    CircleShapeCacheEntry(float radius, SkPaint* paint):
187            ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
188        mRadius = *(uint32_t*) &radius;
189    }
190
191    CircleShapeCacheEntry(): ShapeCacheEntry() {
192        mRadius = 0;
193    }
194
195    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
203private:
204    uint32_t mRadius;
205}; // CircleShapeCacheEntry
206
207struct OvalShapeCacheEntry: public ShapeCacheEntry {
208    OvalShapeCacheEntry(float width, float height, SkPaint* paint):
209            ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
210        mWidth = *(uint32_t*) &width;
211        mHeight = *(uint32_t*) &height;
212    }
213
214    OvalShapeCacheEntry(): ShapeCacheEntry() {
215        mWidth = mHeight = 0;
216    }
217
218    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
228private:
229    uint32_t mWidth;
230    uint32_t mHeight;
231}; // OvalShapeCacheEntry
232
233struct RectShapeCacheEntry: public ShapeCacheEntry {
234    RectShapeCacheEntry(float width, float height, SkPaint* paint):
235            ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
236        mWidth = *(uint32_t*) &width;
237        mHeight = *(uint32_t*) &height;
238    }
239
240    RectShapeCacheEntry(): ShapeCacheEntry() {
241        mWidth = mHeight = 0;
242    }
243
244    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
254private:
255    uint32_t mWidth;
256    uint32_t mHeight;
257}; // RectShapeCacheEntry
258
259struct ArcShapeCacheEntry: public ShapeCacheEntry {
260    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
270    ArcShapeCacheEntry(): ShapeCacheEntry() {
271        mWidth = 0;
272        mHeight = 0;
273        mStartAngle = 0;
274        mSweepAngle = 0;
275        mUseCenter = 0;
276    }
277
278    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
294private:
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 */
307template<typename Entry>
308class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
309public:
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
337protected:
338    PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
339
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
354private:
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
363class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
364public:
365    RoundRectShapeCache();
366
367    PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
368}; // class RoundRectShapeCache
369
370class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
371public:
372    CircleShapeCache();
373
374    PathTexture* getCircle(float radius, SkPaint* paint);
375}; // class CircleShapeCache
376
377class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
378public:
379    OvalShapeCache();
380
381    PathTexture* getOval(float width, float height, SkPaint* paint);
382}; // class OvalShapeCache
383
384class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
385public:
386    RectShapeCache();
387
388    PathTexture* getRect(float width, float height, SkPaint* paint);
389}; // class RectShapeCache
390
391class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
392public:
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
403template<class Entry>
404ShapeCache<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
423template<class Entry>
424ShapeCache<Entry>::~ShapeCache() {
425    mCache.clear();
426    delete[] mName;
427}
428
429template<class Entry>
430void 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
444template<class Entry>
445uint32_t ShapeCache<Entry>::getSize() {
446    return mSize;
447}
448
449template<class Entry>
450uint32_t ShapeCache<Entry>::getMaxSize() {
451    return mMaxSize;
452}
453
454template<class Entry>
455void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
456    mMaxSize = maxSize;
457    while (mSize > mMaxSize) {
458        mCache.removeOldest();
459    }
460}
461
462///////////////////////////////////////////////////////////////////////////////
463// Callbacks
464///////////////////////////////////////////////////////////////////////////////
465
466template<class Entry>
467void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
468    removeTexture(texture);
469}
470
471///////////////////////////////////////////////////////////////////////////////
472// Caching
473///////////////////////////////////////////////////////////////////////////////
474
475template<class Entry>
476void 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            ALOGD("Shape %s deleted, size = %d", mName, size);
485        }
486
487        glDeleteTextures(1, &texture->id);
488        delete texture;
489    }
490}
491
492template<class Entry>
493PathTexture* 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            ALOGD("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
564template<class Entry>
565void ShapeCache<Entry>::clear() {
566    mCache.clear();
567}
568
569template<class Entry>
570void 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