blob: 47cab83e2f475df2458ad32687393bba972eae4c [file] [log] [blame]
Romain Guy01d58e42011-01-19 21:54:02 -08001/*
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
Romain Guyff26a0c2011-01-20 11:35:46 -080022#include <SkBitmap.h>
Romain Guy01d58e42011-01-19 21:54:02 -080023#include <SkCanvas.h>
Romain Guyff26a0c2011-01-20 11:35:46 -080024#include <SkPaint.h>
25#include <SkPath.h>
Romain Guy01d58e42011-01-19 21:54:02 -080026#include <SkRect.h>
27
Romain Guy059e12c2012-11-28 17:35:51 -080028#include <utils/JenkinsHash.h>
29#include <utils/LruCache.h>
30
Romain Guyff26a0c2011-01-20 11:35:46 -080031#include "Debug.h"
Romain Guy01d58e42011-01-19 21:54:02 -080032#include "Properties.h"
Romain Guyff26a0c2011-01-20 11:35:46 -080033#include "Texture.h"
Romain Guy01d58e42011-01-19 21:54:02 -080034
35namespace android {
36namespace uirenderer {
37
38///////////////////////////////////////////////////////////////////////////////
39// Defines
40///////////////////////////////////////////////////////////////////////////////
41
42// Debug
43#if DEBUG_SHAPES
Steve Block5baa3a62011-12-20 16:23:08 +000044 #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
Romain Guy01d58e42011-01-19 21:54:02 -080045#else
46 #define SHAPE_LOGD(...)
47#endif
48
49///////////////////////////////////////////////////////////////////////////////
50// Classes
51///////////////////////////////////////////////////////////////////////////////
52
53/**
Romain Guyff26a0c2011-01-20 11:35:46 -080054 * Alpha texture used to represent a path.
55 */
56struct PathTexture: public Texture {
57 PathTexture(): Texture() {
58 }
59
60 /**
61 * Left coordinate of the path bounds.
62 */
63 float left;
64 /**
65 * Top coordinate of the path bounds.
66 */
67 float top;
68 /**
69 * Offset to draw the path at the correct origin.
70 */
71 float offset;
72}; // struct PathTexture
73
74/**
Romain Guy01d58e42011-01-19 21:54:02 -080075 * Describe a shape in the shape cache.
76 */
77struct ShapeCacheEntry {
78 enum ShapeType {
79 kShapeNone,
Romain Guyc1cd9ba32011-01-23 14:18:41 -080080 kShapeRect,
Romain Guy01d58e42011-01-19 21:54:02 -080081 kShapeRoundRect,
82 kShapeCircle,
83 kShapeOval,
Romain Guyff26a0c2011-01-20 11:35:46 -080084 kShapeArc,
85 kShapePath
Romain Guy01d58e42011-01-19 21:54:02 -080086 };
87
88 ShapeCacheEntry() {
89 shapeType = kShapeNone;
90 join = SkPaint::kDefault_Join;
91 cap = SkPaint::kDefault_Cap;
92 style = SkPaint::kFill_Style;
Romain Guy059e12c2012-11-28 17:35:51 -080093 miter = 4.0f;
94 strokeWidth = 1.0f;
Romain Guy1af23a32011-03-24 16:03:55 -070095 pathEffect = NULL;
Romain Guy01d58e42011-01-19 21:54:02 -080096 }
97
Romain Guy01d58e42011-01-19 21:54:02 -080098 ShapeCacheEntry(ShapeType type, SkPaint* paint) {
99 shapeType = type;
100 join = paint->getStrokeJoin();
101 cap = paint->getStrokeCap();
Romain Guy059e12c2012-11-28 17:35:51 -0800102 miter = paint->getStrokeMiter();
103 strokeWidth = paint->getStrokeWidth();
Romain Guy01d58e42011-01-19 21:54:02 -0800104 style = paint->getStyle();
Romain Guyb29cfbf2011-03-18 16:24:19 -0700105 pathEffect = paint->getPathEffect();
Romain Guy01d58e42011-01-19 21:54:02 -0800106 }
107
108 virtual ~ShapeCacheEntry() {
109 }
110
Romain Guy059e12c2012-11-28 17:35:51 -0800111 virtual hash_t hash() const {
112 uint32_t hash = JenkinsHashMix(0, shapeType);
113 hash = JenkinsHashMix(hash, join);
114 hash = JenkinsHashMix(hash, cap);
115 hash = JenkinsHashMix(hash, style);
116 hash = JenkinsHashMix(hash, android::hash_type(miter));
117 hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
118 hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
119 return JenkinsHashWhiten(hash);
120 }
121
122 virtual int compare(const ShapeCacheEntry& rhs) const {
123 int deltaInt = shapeType - rhs.shapeType;
124 if (deltaInt != 0) return deltaInt;
125
126 deltaInt = join - rhs.join;
127 if (deltaInt != 0) return deltaInt;
128
129 deltaInt = cap - rhs.cap;
130 if (deltaInt != 0) return deltaInt;
131
132 deltaInt = style - rhs.style;
133 if (deltaInt != 0) return deltaInt;
134
135 if (miter < rhs.miter) return -1;
136 if (miter > rhs.miter) return +1;
137
138 if (strokeWidth < rhs.strokeWidth) return -1;
139 if (strokeWidth > rhs.strokeWidth) return +1;
140
141 if (pathEffect < rhs.pathEffect) return -1;
142 if (pathEffect > rhs.pathEffect) return +1;
143
144 return 0;
145 }
146
147 bool operator==(const ShapeCacheEntry& other) const {
148 return compare(other) == 0;
149 }
150
151 bool operator!=(const ShapeCacheEntry& other) const {
152 return compare(other) != 0;
153 }
154
Romain Guy01d58e42011-01-19 21:54:02 -0800155 ShapeType shapeType;
156 SkPaint::Join join;
157 SkPaint::Cap cap;
158 SkPaint::Style style;
Romain Guy059e12c2012-11-28 17:35:51 -0800159 float miter;
160 float strokeWidth;
Romain Guyb29cfbf2011-03-18 16:24:19 -0700161 SkPathEffect* pathEffect;
Romain Guy01d58e42011-01-19 21:54:02 -0800162}; // struct ShapeCacheEntry
163
Romain Guy059e12c2012-11-28 17:35:51 -0800164// Cache support
165
166inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
167 return lhs.compare(rhs) < 0;
168}
169
170inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
171 return lhs.compare(rhs);
172}
173
174inline hash_t hash_type(const ShapeCacheEntry& entry) {
175 return entry.hash();
176}
Romain Guy01d58e42011-01-19 21:54:02 -0800177
178struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
179 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
180 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800181 mWidth = width;
182 mHeight = height;
183 mRx = rx;
184 mRy = ry;
Romain Guy01d58e42011-01-19 21:54:02 -0800185 }
186
187 RoundRectShapeCacheEntry(): ShapeCacheEntry() {
188 mWidth = 0;
189 mHeight = 0;
190 mRx = 0;
191 mRy = 0;
192 }
193
Romain Guy059e12c2012-11-28 17:35:51 -0800194 hash_t hash() const {
195 uint32_t hash = ShapeCacheEntry::hash();
196 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
197 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
198 hash = JenkinsHashMix(hash, android::hash_type(mRx));
199 hash = JenkinsHashMix(hash, android::hash_type(mRy));
200 return JenkinsHashWhiten(hash);
201 }
202
203 int compare(const ShapeCacheEntry& r) const {
204 int deltaInt = ShapeCacheEntry::compare(r);
205 if (deltaInt != 0) return deltaInt;
206
Romain Guy01d58e42011-01-19 21:54:02 -0800207 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800208
209 if (mWidth < rhs.mWidth) return -1;
210 if (mWidth > rhs.mWidth) return +1;
211
212 if (mHeight < rhs.mHeight) return -1;
213 if (mHeight > rhs.mHeight) return +1;
214
215 if (mRx < rhs.mRx) return -1;
216 if (mRx > rhs.mRx) return +1;
217
218 if (mRy < rhs.mRy) return -1;
219 if (mRy > rhs.mRy) return +1;
220
221 return 0;
Romain Guy01d58e42011-01-19 21:54:02 -0800222 }
223
224private:
Romain Guy059e12c2012-11-28 17:35:51 -0800225 float mWidth;
226 float mHeight;
227 float mRx;
228 float mRy;
Romain Guy01d58e42011-01-19 21:54:02 -0800229}; // RoundRectShapeCacheEntry
230
Romain Guy059e12c2012-11-28 17:35:51 -0800231inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
232 return entry.hash();
233}
234
Romain Guy01d58e42011-01-19 21:54:02 -0800235struct CircleShapeCacheEntry: public ShapeCacheEntry {
236 CircleShapeCacheEntry(float radius, SkPaint* paint):
237 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800238 mRadius = radius;
Romain Guy01d58e42011-01-19 21:54:02 -0800239 }
240
241 CircleShapeCacheEntry(): ShapeCacheEntry() {
242 mRadius = 0;
243 }
244
Romain Guy059e12c2012-11-28 17:35:51 -0800245 hash_t hash() const {
246 uint32_t hash = ShapeCacheEntry::hash();
247 hash = JenkinsHashMix(hash, android::hash_type(mRadius));
248 return JenkinsHashWhiten(hash);
249 }
250
251 int compare(const ShapeCacheEntry& r) const {
252 int deltaInt = ShapeCacheEntry::compare(r);
253 if (deltaInt != 0) return deltaInt;
254
Romain Guy01d58e42011-01-19 21:54:02 -0800255 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800256
257 if (mRadius < rhs.mRadius) return -1;
258 if (mRadius > rhs.mRadius) return +1;
259
260 return 0;
Romain Guy01d58e42011-01-19 21:54:02 -0800261 }
262
263private:
Romain Guy059e12c2012-11-28 17:35:51 -0800264 float mRadius;
Romain Guy01d58e42011-01-19 21:54:02 -0800265}; // CircleShapeCacheEntry
266
Romain Guy059e12c2012-11-28 17:35:51 -0800267inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
268 return entry.hash();
269}
270
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800271struct OvalShapeCacheEntry: public ShapeCacheEntry {
272 OvalShapeCacheEntry(float width, float height, SkPaint* paint):
273 ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800274 mWidth = width;
275 mHeight = height;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800276 }
277
278 OvalShapeCacheEntry(): ShapeCacheEntry() {
279 mWidth = mHeight = 0;
280 }
281
Romain Guy059e12c2012-11-28 17:35:51 -0800282 hash_t hash() const {
283 uint32_t hash = ShapeCacheEntry::hash();
284 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
285 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
286 return JenkinsHashWhiten(hash);
287 }
288
289 int compare(const ShapeCacheEntry& r) const {
290 int deltaInt = ShapeCacheEntry::compare(r);
291 if (deltaInt != 0) return deltaInt;
292
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800293 const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800294
295 if (mWidth < rhs.mWidth) return -1;
296 if (mWidth > rhs.mWidth) return +1;
297
298 if (mHeight < rhs.mHeight) return -1;
299 if (mHeight > rhs.mHeight) return +1;
300
301 return 0;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800302 }
303
304private:
Romain Guy059e12c2012-11-28 17:35:51 -0800305 float mWidth;
306 float mHeight;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800307}; // OvalShapeCacheEntry
308
Romain Guy059e12c2012-11-28 17:35:51 -0800309inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
310 return entry.hash();
311}
312
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800313struct RectShapeCacheEntry: public ShapeCacheEntry {
314 RectShapeCacheEntry(float width, float height, SkPaint* paint):
315 ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800316 mWidth = width;
317 mHeight = height;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800318 }
319
320 RectShapeCacheEntry(): ShapeCacheEntry() {
321 mWidth = mHeight = 0;
322 }
323
Romain Guy059e12c2012-11-28 17:35:51 -0800324 hash_t hash() const {
325 uint32_t hash = ShapeCacheEntry::hash();
326 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
327 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
328 return JenkinsHashWhiten(hash);
329 }
330
331 int compare(const ShapeCacheEntry& r) const {
332 int deltaInt = ShapeCacheEntry::compare(r);
333 if (deltaInt != 0) return deltaInt;
334
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800335 const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800336
337 if (mWidth < rhs.mWidth) return -1;
338 if (mWidth > rhs.mWidth) return +1;
339
340 if (mHeight < rhs.mHeight) return -1;
341 if (mHeight > rhs.mHeight) return +1;
342
343 return 0;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800344 }
345
346private:
Romain Guy059e12c2012-11-28 17:35:51 -0800347 float mWidth;
348 float mHeight;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800349}; // RectShapeCacheEntry
350
Romain Guy059e12c2012-11-28 17:35:51 -0800351inline hash_t hash_type(const RectShapeCacheEntry& entry) {
352 return entry.hash();
353}
354
Romain Guy8b2f5262011-01-23 16:15:02 -0800355struct ArcShapeCacheEntry: public ShapeCacheEntry {
356 ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
357 bool useCenter, SkPaint* paint):
358 ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800359 mWidth = width;
360 mHeight = height;
361 mStartAngle = startAngle;
362 mSweepAngle = sweepAngle;
Romain Guy8b2f5262011-01-23 16:15:02 -0800363 mUseCenter = useCenter ? 1 : 0;
364 }
365
366 ArcShapeCacheEntry(): ShapeCacheEntry() {
367 mWidth = 0;
368 mHeight = 0;
369 mStartAngle = 0;
370 mSweepAngle = 0;
371 mUseCenter = 0;
372 }
373
Romain Guy059e12c2012-11-28 17:35:51 -0800374 hash_t hash() const {
375 uint32_t hash = ShapeCacheEntry::hash();
376 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
377 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
378 hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
379 hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
380 hash = JenkinsHashMix(hash, mUseCenter);
381 return JenkinsHashWhiten(hash);
382 }
383
384 int compare(const ShapeCacheEntry& r) const {
385 int deltaInt = ShapeCacheEntry::compare(r);
386 if (deltaInt != 0) return deltaInt;
387
Romain Guy8b2f5262011-01-23 16:15:02 -0800388 const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800389
390 if (mWidth < rhs.mWidth) return -1;
391 if (mWidth > rhs.mWidth) return +1;
392
393 if (mHeight < rhs.mHeight) return -1;
394 if (mHeight > rhs.mHeight) return +1;
395
396 if (mStartAngle < rhs.mStartAngle) return -1;
397 if (mStartAngle > rhs.mStartAngle) return +1;
398
399 if (mSweepAngle < rhs.mSweepAngle) return -1;
400 if (mSweepAngle > rhs.mSweepAngle) return +1;
401
402 return mUseCenter - rhs.mUseCenter;
Romain Guy8b2f5262011-01-23 16:15:02 -0800403 }
404
405private:
Romain Guy059e12c2012-11-28 17:35:51 -0800406 float mWidth;
407 float mHeight;
408 float mStartAngle;
409 float mSweepAngle;
Romain Guy8b2f5262011-01-23 16:15:02 -0800410 uint32_t mUseCenter;
411}; // ArcShapeCacheEntry
412
Romain Guy059e12c2012-11-28 17:35:51 -0800413inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
414 return entry.hash();
415}
416
Romain Guy01d58e42011-01-19 21:54:02 -0800417/**
418 * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
419 * Any texture added to the cache causing the cache to grow beyond the maximum
420 * allowed size will also cause the oldest texture to be kicked out.
421 */
422template<typename Entry>
423class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
424public:
Romain Guyff26a0c2011-01-20 11:35:46 -0800425 ShapeCache(const char* name, const char* propertyName, float defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800426 ~ShapeCache();
427
428 /**
429 * Used as a callback when an entry is removed from the cache.
430 * Do not invoke directly.
431 */
432 void operator()(Entry& path, PathTexture*& texture);
433
434 /**
435 * Clears the cache. This causes all textures to be deleted.
436 */
437 void clear();
438
439 /**
440 * Sets the maximum size of the cache in bytes.
441 */
442 void setMaxSize(uint32_t maxSize);
443 /**
444 * Returns the maximum size of the cache in bytes.
445 */
446 uint32_t getMaxSize();
447 /**
448 * Returns the current size of the cache in bytes.
449 */
450 uint32_t getSize();
451
452protected:
453 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
Romain Guyfdd6fc12012-04-27 11:47:13 -0700454 PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
455 void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
456
457 /**
458 * Ensures there is enough space in the cache for a texture of the specified
459 * dimensions.
460 */
461 void purgeCache(uint32_t width, uint32_t height);
462
463 void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height);
464 void initPaint(SkPaint& paint);
465
466 bool checkTextureSize(uint32_t width, uint32_t height);
Romain Guy01d58e42011-01-19 21:54:02 -0800467
468 PathTexture* get(Entry entry) {
469 return mCache.get(entry);
470 }
471
Romain Guy01d58e42011-01-19 21:54:02 -0800472 void removeTexture(PathTexture* texture);
473
Romain Guy059e12c2012-11-28 17:35:51 -0800474 LruCache<Entry, PathTexture*> mCache;
Romain Guy01d58e42011-01-19 21:54:02 -0800475 uint32_t mSize;
476 uint32_t mMaxSize;
477 GLuint mMaxTextureSize;
478
Romain Guyff26a0c2011-01-20 11:35:46 -0800479 char* mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800480 bool mDebugEnabled;
Romain Guyff26a0c2011-01-20 11:35:46 -0800481
482private:
483 /**
484 * Generates the texture from a bitmap into the specified texture structure.
485 */
486 void generateTexture(SkBitmap& bitmap, Texture* texture);
487
488 void init();
Romain Guy01d58e42011-01-19 21:54:02 -0800489}; // class ShapeCache
490
491class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
492public:
493 RoundRectShapeCache();
494
495 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
496}; // class RoundRectShapeCache
497
498class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
499public:
500 CircleShapeCache();
501
502 PathTexture* getCircle(float radius, SkPaint* paint);
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800503}; // class CircleShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800504
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800505class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
506public:
507 OvalShapeCache();
508
509 PathTexture* getOval(float width, float height, SkPaint* paint);
510}; // class OvalShapeCache
511
512class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
513public:
514 RectShapeCache();
515
516 PathTexture* getRect(float width, float height, SkPaint* paint);
517}; // class RectShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800518
Romain Guy8b2f5262011-01-23 16:15:02 -0800519class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
520public:
521 ArcShapeCache();
522
523 PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
524 bool useCenter, SkPaint* paint);
525}; // class ArcShapeCache
526
Romain Guy01d58e42011-01-19 21:54:02 -0800527///////////////////////////////////////////////////////////////////////////////
528// Constructors/destructor
529///////////////////////////////////////////////////////////////////////////////
530
531template<class Entry>
Romain Guyff26a0c2011-01-20 11:35:46 -0800532ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
Romain Guy059e12c2012-11-28 17:35:51 -0800533 mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
Romain Guyff26a0c2011-01-20 11:35:46 -0800534 mSize(0), mMaxSize(MB(defaultSize)) {
Romain Guy01d58e42011-01-19 21:54:02 -0800535 char property[PROPERTY_VALUE_MAX];
Romain Guyff26a0c2011-01-20 11:35:46 -0800536 if (property_get(propertyName, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800537 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
Romain Guy01d58e42011-01-19 21:54:02 -0800538 setMaxSize(MB(atof(property)));
539 } else {
Romain Guyc9855a52011-01-21 21:14:15 -0800540 INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800541 }
Romain Guy01d58e42011-01-19 21:54:02 -0800542
Romain Guyff26a0c2011-01-20 11:35:46 -0800543 size_t len = strlen(name);
544 mName = new char[len + 1];
545 strcpy(mName, name);
546 mName[len] = '\0';
547
Romain Guy01d58e42011-01-19 21:54:02 -0800548 init();
549}
550
551template<class Entry>
552ShapeCache<Entry>::~ShapeCache() {
553 mCache.clear();
Romain Guyff26a0c2011-01-20 11:35:46 -0800554 delete[] mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800555}
556
557template<class Entry>
558void ShapeCache<Entry>::init() {
559 mCache.setOnEntryRemovedListener(this);
560
561 GLint maxTextureSize;
562 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
563 mMaxTextureSize = maxTextureSize;
564
565 mDebugEnabled = readDebugLevel() & kDebugCaches;
566}
567
568///////////////////////////////////////////////////////////////////////////////
569// Size management
570///////////////////////////////////////////////////////////////////////////////
571
572template<class Entry>
573uint32_t ShapeCache<Entry>::getSize() {
574 return mSize;
575}
576
577template<class Entry>
578uint32_t ShapeCache<Entry>::getMaxSize() {
579 return mMaxSize;
580}
581
582template<class Entry>
583void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
584 mMaxSize = maxSize;
585 while (mSize > mMaxSize) {
586 mCache.removeOldest();
587 }
588}
589
590///////////////////////////////////////////////////////////////////////////////
591// Callbacks
592///////////////////////////////////////////////////////////////////////////////
593
594template<class Entry>
595void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
596 removeTexture(texture);
597}
598
599///////////////////////////////////////////////////////////////////////////////
600// Caching
601///////////////////////////////////////////////////////////////////////////////
602
603template<class Entry>
604void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
605 if (texture) {
606 const uint32_t size = texture->width * texture->height;
607 mSize -= size;
608
Romain Guyff26a0c2011-01-20 11:35:46 -0800609 SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
610 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800611 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000612 ALOGD("Shape %s deleted, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800613 }
614
615 glDeleteTextures(1, &texture->id);
616 delete texture;
617 }
618}
619
Romain Guyfdd6fc12012-04-27 11:47:13 -0700620void computePathBounds(const SkPath* path, const SkPaint* paint,
Romain Guy33f6beb2012-02-16 19:24:51 -0800621 float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
Romain Guyfdd6fc12012-04-27 11:47:13 -0700622void computeBounds(const SkRect& bounds, const SkPaint* paint,
623 float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
624
625static PathTexture* createTexture(float left, float top, float offset,
626 uint32_t width, uint32_t height, uint32_t id) {
627 PathTexture* texture = new PathTexture;
628 texture->left = left;
629 texture->top = top;
630 texture->offset = offset;
631 texture->width = width;
632 texture->height = height;
633 texture->generation = id;
634 return texture;
635}
636
637template<class Entry>
638void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
639 const uint32_t size = width * height;
640 // Don't even try to cache a bitmap that's bigger than the cache
641 if (size < mMaxSize) {
642 while (mSize + size > mMaxSize) {
643 mCache.removeOldest();
644 }
645 }
646}
647
648template<class Entry>
649void ShapeCache<Entry>::initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
650 bitmap.setConfig(SkBitmap::kA8_Config, width, height);
651 bitmap.allocPixels();
652 bitmap.eraseColor(0);
653}
654
655template<class Entry>
656void ShapeCache<Entry>::initPaint(SkPaint& paint) {
657 // Make sure the paint is opaque, color, alpha, filter, etc.
658 // will be applied later when compositing the alpha8 texture
659 paint.setColor(0xff000000);
660 paint.setAlpha(255);
661 paint.setColorFilter(NULL);
662 paint.setMaskFilter(NULL);
663 paint.setShader(NULL);
664 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
665 SkSafeUnref(paint.setXfermode(mode));
666}
667
668template<class Entry>
669bool ShapeCache<Entry>::checkTextureSize(uint32_t width, uint32_t height) {
670 if (width > mMaxTextureSize || height > mMaxTextureSize) {
671 ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
672 mName, width, height, mMaxTextureSize, mMaxTextureSize);
673 return false;
674 }
675 return true;
676}
Romain Guy33f6beb2012-02-16 19:24:51 -0800677
Romain Guy01d58e42011-01-19 21:54:02 -0800678template<class Entry>
679PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
680 const SkPaint* paint) {
Romain Guy01d58e42011-01-19 21:54:02 -0800681
Romain Guy33f6beb2012-02-16 19:24:51 -0800682 float left, top, offset;
683 uint32_t width, height;
684 computePathBounds(path, paint, left, top, offset, width, height);
Romain Guy98029c82011-06-17 15:47:07 -0700685
Romain Guyfdd6fc12012-04-27 11:47:13 -0700686 if (!checkTextureSize(width, height)) return NULL;
Romain Guy01d58e42011-01-19 21:54:02 -0800687
Romain Guyfdd6fc12012-04-27 11:47:13 -0700688 purgeCache(width, height);
Romain Guy01d58e42011-01-19 21:54:02 -0800689
690 SkBitmap bitmap;
Romain Guyfdd6fc12012-04-27 11:47:13 -0700691 initBitmap(bitmap, width, height);
Romain Guy01d58e42011-01-19 21:54:02 -0800692
693 SkPaint pathPaint(*paint);
Romain Guyfdd6fc12012-04-27 11:47:13 -0700694 initPaint(pathPaint);
Romain Guy01d58e42011-01-19 21:54:02 -0800695
696 SkCanvas canvas(bitmap);
Romain Guy33f6beb2012-02-16 19:24:51 -0800697 canvas.translate(-left + offset, -top + offset);
Romain Guy01d58e42011-01-19 21:54:02 -0800698 canvas.drawPath(*path, pathPaint);
699
Romain Guyfdd6fc12012-04-27 11:47:13 -0700700 PathTexture* texture = createTexture(left, top, offset, width, height, path->getGenerationID());
701 addTexture(entry, &bitmap, texture);
Romain Guy01d58e42011-01-19 21:54:02 -0800702
Romain Guyfdd6fc12012-04-27 11:47:13 -0700703 return texture;
704}
705
706template<class Entry>
707void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
708 generateTexture(*bitmap, texture);
709
710 uint32_t size = texture->width * texture->height;
Romain Guy01d58e42011-01-19 21:54:02 -0800711 if (size < mMaxSize) {
712 mSize += size;
Romain Guyff26a0c2011-01-20 11:35:46 -0800713 SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
714 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800715 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000716 ALOGD("Shape %s created, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800717 }
718 mCache.put(entry, texture);
719 } else {
720 texture->cleanup = true;
721 }
Romain Guy01d58e42011-01-19 21:54:02 -0800722}
723
724template<class Entry>
725void ShapeCache<Entry>::clear() {
726 mCache.clear();
727}
728
729template<class Entry>
730void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
731 SkAutoLockPixels alp(bitmap);
732 if (!bitmap.readyToDraw()) {
Steve Block3762c312012-01-06 19:20:56 +0000733 ALOGE("Cannot generate texture from bitmap");
Romain Guy01d58e42011-01-19 21:54:02 -0800734 return;
735 }
736
737 glGenTextures(1, &texture->id);
738
739 glBindTexture(GL_TEXTURE_2D, texture->id);
740 // Textures are Alpha8
741 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
742
743 texture->blend = true;
744 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
745 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
746
Romain Guyd21b6e12011-11-30 20:21:23 -0800747 texture->setFilter(GL_LINEAR);
748 texture->setWrap(GL_CLAMP_TO_EDGE);
Romain Guy01d58e42011-01-19 21:54:02 -0800749}
750
751}; // namespace uirenderer
752}; // namespace android
753
754#endif // ANDROID_HWUI_SHAPE_CACHE_H