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