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    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
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
367private:
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
376class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
377public:
378    RoundRectShapeCache();
379
380    PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
381}; // class RoundRectShapeCache
382
383class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
384public:
385    CircleShapeCache();
386
387    PathTexture* getCircle(float radius, SkPaint* paint);
388}; // class CircleShapeCache
389
390class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
391public:
392    OvalShapeCache();
393
394    PathTexture* getOval(float width, float height, SkPaint* paint);
395}; // class OvalShapeCache
396
397class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
398public:
399    RectShapeCache();
400
401    PathTexture* getRect(float width, float height, SkPaint* paint);
402}; // class RectShapeCache
403
404class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
405public:
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
416template<class Entry>
417ShapeCache<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
436template<class Entry>
437ShapeCache<Entry>::~ShapeCache() {
438    mCache.clear();
439    delete[] mName;
440}
441
442template<class Entry>
443void 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
457template<class Entry>
458uint32_t ShapeCache<Entry>::getSize() {
459    return mSize;
460}
461
462template<class Entry>
463uint32_t ShapeCache<Entry>::getMaxSize() {
464    return mMaxSize;
465}
466
467template<class Entry>
468void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
469    mMaxSize = maxSize;
470    while (mSize > mMaxSize) {
471        mCache.removeOldest();
472    }
473}
474
475///////////////////////////////////////////////////////////////////////////////
476// Callbacks
477///////////////////////////////////////////////////////////////////////////////
478
479template<class Entry>
480void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
481    removeTexture(texture);
482}
483
484///////////////////////////////////////////////////////////////////////////////
485// Caching
486///////////////////////////////////////////////////////////////////////////////
487
488template<class Entry>
489void 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
505void computePathBounds(const SkPath* path, const SkPaint* paint,
506        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
507void computeBounds(const SkRect& bounds, const SkPaint* paint,
508        float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
509
510static 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
522template<class Entry>
523void 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
533template<class Entry>
534void 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
540template<class Entry>
541void 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
553template<class Entry>
554bool 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
563template<class Entry>
564PathTexture* 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
591template<class Entry>
592void 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
609template<class Entry>
610void ShapeCache<Entry>::clear() {
611    mCache.clear();
612}
613
614template<class Entry>
615void 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