blob: 58fea08aca76e8f6910769de95776e89d9d1e855 [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
Romain Guyca89e2a2013-03-08 17:44:20 -080020#define ATRACE_TAG ATRACE_TAG_VIEW
21
Romain Guy01d58e42011-01-19 21:54:02 -080022#include <GLES2/gl2.h>
23
Romain Guyff26a0c2011-01-20 11:35:46 -080024#include <SkBitmap.h>
Romain Guy01d58e42011-01-19 21:54:02 -080025#include <SkCanvas.h>
Romain Guyff26a0c2011-01-20 11:35:46 -080026#include <SkPaint.h>
27#include <SkPath.h>
Romain Guy01d58e42011-01-19 21:54:02 -080028#include <SkRect.h>
29
Romain Guy059e12c2012-11-28 17:35:51 -080030#include <utils/JenkinsHash.h>
31#include <utils/LruCache.h>
Romain Guyca89e2a2013-03-08 17:44:20 -080032#include <utils/Trace.h>
Romain Guy059e12c2012-11-28 17:35:51 -080033
Romain Guyff26a0c2011-01-20 11:35:46 -080034#include "Debug.h"
Romain Guy01d58e42011-01-19 21:54:02 -080035#include "Properties.h"
Romain Guyff26a0c2011-01-20 11:35:46 -080036#include "Texture.h"
Romain Guy5dc7fa72013-03-11 20:48:31 -070037#include "thread/Task.h"
Romain Guy01d58e42011-01-19 21:54:02 -080038
39namespace android {
40namespace uirenderer {
41
42///////////////////////////////////////////////////////////////////////////////
43// Defines
44///////////////////////////////////////////////////////////////////////////////
45
46// Debug
47#if DEBUG_SHAPES
Steve Block5baa3a62011-12-20 16:23:08 +000048 #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
Romain Guy01d58e42011-01-19 21:54:02 -080049#else
50 #define SHAPE_LOGD(...)
51#endif
52
53///////////////////////////////////////////////////////////////////////////////
54// Classes
55///////////////////////////////////////////////////////////////////////////////
56
57/**
Romain Guyff26a0c2011-01-20 11:35:46 -080058 * Alpha texture used to represent a path.
59 */
60struct PathTexture: public Texture {
61 PathTexture(): Texture() {
62 }
63
Romain Guyca89e2a2013-03-08 17:44:20 -080064 ~PathTexture() {
Romain Guy5dc7fa72013-03-11 20:48:31 -070065 clearTask();
Romain Guyca89e2a2013-03-08 17:44:20 -080066 }
67
Romain Guyff26a0c2011-01-20 11:35:46 -080068 /**
69 * Left coordinate of the path bounds.
70 */
71 float left;
72 /**
73 * Top coordinate of the path bounds.
74 */
75 float top;
76 /**
77 * Offset to draw the path at the correct origin.
78 */
79 float offset;
Romain Guyca89e2a2013-03-08 17:44:20 -080080
Romain Guy5dc7fa72013-03-11 20:48:31 -070081 sp<Task<SkBitmap*> > task() const {
82 return mTask;
Romain Guyca89e2a2013-03-08 17:44:20 -080083 }
84
Romain Guy5dc7fa72013-03-11 20:48:31 -070085 void setTask(const sp<Task<SkBitmap*> >& task) {
86 mTask = task;
87 }
88
89 void clearTask() {
90 if (mTask != NULL) {
91 mTask.clear();
Romain Guyca89e2a2013-03-08 17:44:20 -080092 }
93 }
94
95private:
Romain Guy5dc7fa72013-03-11 20:48:31 -070096 sp<Task<SkBitmap*> > mTask;
Romain Guyff26a0c2011-01-20 11:35:46 -080097}; // struct PathTexture
98
99/**
Romain Guy01d58e42011-01-19 21:54:02 -0800100 * Describe a shape in the shape cache.
101 */
102struct ShapeCacheEntry {
103 enum ShapeType {
104 kShapeNone,
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800105 kShapeRect,
Romain Guy01d58e42011-01-19 21:54:02 -0800106 kShapeRoundRect,
107 kShapeCircle,
108 kShapeOval,
Romain Guyff26a0c2011-01-20 11:35:46 -0800109 kShapeArc,
110 kShapePath
Romain Guy01d58e42011-01-19 21:54:02 -0800111 };
112
113 ShapeCacheEntry() {
114 shapeType = kShapeNone;
115 join = SkPaint::kDefault_Join;
116 cap = SkPaint::kDefault_Cap;
117 style = SkPaint::kFill_Style;
Romain Guy059e12c2012-11-28 17:35:51 -0800118 miter = 4.0f;
119 strokeWidth = 1.0f;
Romain Guy1af23a32011-03-24 16:03:55 -0700120 pathEffect = NULL;
Romain Guy01d58e42011-01-19 21:54:02 -0800121 }
122
Romain Guy01d58e42011-01-19 21:54:02 -0800123 ShapeCacheEntry(ShapeType type, SkPaint* paint) {
124 shapeType = type;
125 join = paint->getStrokeJoin();
126 cap = paint->getStrokeCap();
Romain Guy059e12c2012-11-28 17:35:51 -0800127 miter = paint->getStrokeMiter();
128 strokeWidth = paint->getStrokeWidth();
Romain Guy01d58e42011-01-19 21:54:02 -0800129 style = paint->getStyle();
Romain Guyb29cfbf2011-03-18 16:24:19 -0700130 pathEffect = paint->getPathEffect();
Romain Guy01d58e42011-01-19 21:54:02 -0800131 }
132
133 virtual ~ShapeCacheEntry() {
134 }
135
Romain Guy059e12c2012-11-28 17:35:51 -0800136 virtual hash_t hash() const {
137 uint32_t hash = JenkinsHashMix(0, shapeType);
138 hash = JenkinsHashMix(hash, join);
139 hash = JenkinsHashMix(hash, cap);
140 hash = JenkinsHashMix(hash, style);
141 hash = JenkinsHashMix(hash, android::hash_type(miter));
142 hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
143 hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
144 return JenkinsHashWhiten(hash);
145 }
146
147 virtual int compare(const ShapeCacheEntry& rhs) const {
148 int deltaInt = shapeType - rhs.shapeType;
149 if (deltaInt != 0) return deltaInt;
150
151 deltaInt = join - rhs.join;
152 if (deltaInt != 0) return deltaInt;
153
154 deltaInt = cap - rhs.cap;
155 if (deltaInt != 0) return deltaInt;
156
157 deltaInt = style - rhs.style;
158 if (deltaInt != 0) return deltaInt;
159
160 if (miter < rhs.miter) return -1;
161 if (miter > rhs.miter) return +1;
162
163 if (strokeWidth < rhs.strokeWidth) return -1;
164 if (strokeWidth > rhs.strokeWidth) return +1;
165
166 if (pathEffect < rhs.pathEffect) return -1;
167 if (pathEffect > rhs.pathEffect) return +1;
168
169 return 0;
170 }
171
172 bool operator==(const ShapeCacheEntry& other) const {
173 return compare(other) == 0;
174 }
175
176 bool operator!=(const ShapeCacheEntry& other) const {
177 return compare(other) != 0;
178 }
179
Romain Guy01d58e42011-01-19 21:54:02 -0800180 ShapeType shapeType;
181 SkPaint::Join join;
182 SkPaint::Cap cap;
183 SkPaint::Style style;
Romain Guy059e12c2012-11-28 17:35:51 -0800184 float miter;
185 float strokeWidth;
Romain Guyb29cfbf2011-03-18 16:24:19 -0700186 SkPathEffect* pathEffect;
Romain Guy01d58e42011-01-19 21:54:02 -0800187}; // struct ShapeCacheEntry
188
Romain Guy059e12c2012-11-28 17:35:51 -0800189// Cache support
190
191inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
192 return lhs.compare(rhs) < 0;
193}
194
195inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
196 return lhs.compare(rhs);
197}
198
199inline hash_t hash_type(const ShapeCacheEntry& entry) {
200 return entry.hash();
201}
Romain Guy01d58e42011-01-19 21:54:02 -0800202
203struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
204 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
205 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800206 mWidth = width;
207 mHeight = height;
208 mRx = rx;
209 mRy = ry;
Romain Guy01d58e42011-01-19 21:54:02 -0800210 }
211
212 RoundRectShapeCacheEntry(): ShapeCacheEntry() {
213 mWidth = 0;
214 mHeight = 0;
215 mRx = 0;
216 mRy = 0;
217 }
218
Romain Guy059e12c2012-11-28 17:35:51 -0800219 hash_t hash() const {
220 uint32_t hash = ShapeCacheEntry::hash();
221 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
222 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
223 hash = JenkinsHashMix(hash, android::hash_type(mRx));
224 hash = JenkinsHashMix(hash, android::hash_type(mRy));
225 return JenkinsHashWhiten(hash);
226 }
227
228 int compare(const ShapeCacheEntry& r) const {
229 int deltaInt = ShapeCacheEntry::compare(r);
230 if (deltaInt != 0) return deltaInt;
231
Romain Guy01d58e42011-01-19 21:54:02 -0800232 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800233
234 if (mWidth < rhs.mWidth) return -1;
235 if (mWidth > rhs.mWidth) return +1;
236
237 if (mHeight < rhs.mHeight) return -1;
238 if (mHeight > rhs.mHeight) return +1;
239
240 if (mRx < rhs.mRx) return -1;
241 if (mRx > rhs.mRx) return +1;
242
243 if (mRy < rhs.mRy) return -1;
244 if (mRy > rhs.mRy) return +1;
245
246 return 0;
Romain Guy01d58e42011-01-19 21:54:02 -0800247 }
248
249private:
Romain Guy059e12c2012-11-28 17:35:51 -0800250 float mWidth;
251 float mHeight;
252 float mRx;
253 float mRy;
Romain Guy01d58e42011-01-19 21:54:02 -0800254}; // RoundRectShapeCacheEntry
255
Romain Guy059e12c2012-11-28 17:35:51 -0800256inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
257 return entry.hash();
258}
259
Romain Guy01d58e42011-01-19 21:54:02 -0800260struct CircleShapeCacheEntry: public ShapeCacheEntry {
261 CircleShapeCacheEntry(float radius, SkPaint* paint):
262 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800263 mRadius = radius;
Romain Guy01d58e42011-01-19 21:54:02 -0800264 }
265
266 CircleShapeCacheEntry(): ShapeCacheEntry() {
267 mRadius = 0;
268 }
269
Romain Guy059e12c2012-11-28 17:35:51 -0800270 hash_t hash() const {
271 uint32_t hash = ShapeCacheEntry::hash();
272 hash = JenkinsHashMix(hash, android::hash_type(mRadius));
273 return JenkinsHashWhiten(hash);
274 }
275
276 int compare(const ShapeCacheEntry& r) const {
277 int deltaInt = ShapeCacheEntry::compare(r);
278 if (deltaInt != 0) return deltaInt;
279
Romain Guy01d58e42011-01-19 21:54:02 -0800280 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800281
282 if (mRadius < rhs.mRadius) return -1;
283 if (mRadius > rhs.mRadius) return +1;
284
285 return 0;
Romain Guy01d58e42011-01-19 21:54:02 -0800286 }
287
288private:
Romain Guy059e12c2012-11-28 17:35:51 -0800289 float mRadius;
Romain Guy01d58e42011-01-19 21:54:02 -0800290}; // CircleShapeCacheEntry
291
Romain Guy059e12c2012-11-28 17:35:51 -0800292inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
293 return entry.hash();
294}
295
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800296struct OvalShapeCacheEntry: public ShapeCacheEntry {
297 OvalShapeCacheEntry(float width, float height, SkPaint* paint):
298 ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800299 mWidth = width;
300 mHeight = height;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800301 }
302
303 OvalShapeCacheEntry(): ShapeCacheEntry() {
304 mWidth = mHeight = 0;
305 }
306
Romain Guy059e12c2012-11-28 17:35:51 -0800307 hash_t hash() const {
308 uint32_t hash = ShapeCacheEntry::hash();
309 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
310 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
311 return JenkinsHashWhiten(hash);
312 }
313
314 int compare(const ShapeCacheEntry& r) const {
315 int deltaInt = ShapeCacheEntry::compare(r);
316 if (deltaInt != 0) return deltaInt;
317
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800318 const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800319
320 if (mWidth < rhs.mWidth) return -1;
321 if (mWidth > rhs.mWidth) return +1;
322
323 if (mHeight < rhs.mHeight) return -1;
324 if (mHeight > rhs.mHeight) return +1;
325
326 return 0;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800327 }
328
329private:
Romain Guy059e12c2012-11-28 17:35:51 -0800330 float mWidth;
331 float mHeight;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800332}; // OvalShapeCacheEntry
333
Romain Guy059e12c2012-11-28 17:35:51 -0800334inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
335 return entry.hash();
336}
337
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800338struct RectShapeCacheEntry: public ShapeCacheEntry {
339 RectShapeCacheEntry(float width, float height, SkPaint* paint):
340 ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800341 mWidth = width;
342 mHeight = height;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800343 }
344
345 RectShapeCacheEntry(): ShapeCacheEntry() {
346 mWidth = mHeight = 0;
347 }
348
Romain Guy059e12c2012-11-28 17:35:51 -0800349 hash_t hash() const {
350 uint32_t hash = ShapeCacheEntry::hash();
351 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
352 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
353 return JenkinsHashWhiten(hash);
354 }
355
356 int compare(const ShapeCacheEntry& r) const {
357 int deltaInt = ShapeCacheEntry::compare(r);
358 if (deltaInt != 0) return deltaInt;
359
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800360 const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800361
362 if (mWidth < rhs.mWidth) return -1;
363 if (mWidth > rhs.mWidth) return +1;
364
365 if (mHeight < rhs.mHeight) return -1;
366 if (mHeight > rhs.mHeight) return +1;
367
368 return 0;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800369 }
370
371private:
Romain Guy059e12c2012-11-28 17:35:51 -0800372 float mWidth;
373 float mHeight;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800374}; // RectShapeCacheEntry
375
Romain Guy059e12c2012-11-28 17:35:51 -0800376inline hash_t hash_type(const RectShapeCacheEntry& entry) {
377 return entry.hash();
378}
379
Romain Guy8b2f5262011-01-23 16:15:02 -0800380struct ArcShapeCacheEntry: public ShapeCacheEntry {
381 ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
382 bool useCenter, SkPaint* paint):
383 ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800384 mWidth = width;
385 mHeight = height;
386 mStartAngle = startAngle;
387 mSweepAngle = sweepAngle;
Romain Guy8b2f5262011-01-23 16:15:02 -0800388 mUseCenter = useCenter ? 1 : 0;
389 }
390
391 ArcShapeCacheEntry(): ShapeCacheEntry() {
392 mWidth = 0;
393 mHeight = 0;
394 mStartAngle = 0;
395 mSweepAngle = 0;
396 mUseCenter = 0;
397 }
398
Romain Guy059e12c2012-11-28 17:35:51 -0800399 hash_t hash() const {
400 uint32_t hash = ShapeCacheEntry::hash();
401 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
402 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
403 hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
404 hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
405 hash = JenkinsHashMix(hash, mUseCenter);
406 return JenkinsHashWhiten(hash);
407 }
408
409 int compare(const ShapeCacheEntry& r) const {
410 int deltaInt = ShapeCacheEntry::compare(r);
411 if (deltaInt != 0) return deltaInt;
412
Romain Guy8b2f5262011-01-23 16:15:02 -0800413 const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800414
415 if (mWidth < rhs.mWidth) return -1;
416 if (mWidth > rhs.mWidth) return +1;
417
418 if (mHeight < rhs.mHeight) return -1;
419 if (mHeight > rhs.mHeight) return +1;
420
421 if (mStartAngle < rhs.mStartAngle) return -1;
422 if (mStartAngle > rhs.mStartAngle) return +1;
423
424 if (mSweepAngle < rhs.mSweepAngle) return -1;
425 if (mSweepAngle > rhs.mSweepAngle) return +1;
426
427 return mUseCenter - rhs.mUseCenter;
Romain Guy8b2f5262011-01-23 16:15:02 -0800428 }
429
430private:
Romain Guy059e12c2012-11-28 17:35:51 -0800431 float mWidth;
432 float mHeight;
433 float mStartAngle;
434 float mSweepAngle;
Romain Guy8b2f5262011-01-23 16:15:02 -0800435 uint32_t mUseCenter;
436}; // ArcShapeCacheEntry
437
Romain Guy059e12c2012-11-28 17:35:51 -0800438inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
439 return entry.hash();
440}
441
Romain Guy01d58e42011-01-19 21:54:02 -0800442/**
443 * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
444 * Any texture added to the cache causing the cache to grow beyond the maximum
445 * allowed size will also cause the oldest texture to be kicked out.
446 */
447template<typename Entry>
448class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
449public:
Romain Guyff26a0c2011-01-20 11:35:46 -0800450 ShapeCache(const char* name, const char* propertyName, float defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800451 ~ShapeCache();
452
453 /**
454 * Used as a callback when an entry is removed from the cache.
455 * Do not invoke directly.
456 */
457 void operator()(Entry& path, PathTexture*& texture);
458
459 /**
460 * Clears the cache. This causes all textures to be deleted.
461 */
462 void clear();
463
464 /**
465 * Sets the maximum size of the cache in bytes.
466 */
467 void setMaxSize(uint32_t maxSize);
468 /**
469 * Returns the maximum size of the cache in bytes.
470 */
471 uint32_t getMaxSize();
472 /**
473 * Returns the current size of the cache in bytes.
474 */
475 uint32_t getSize();
476
Romain Guyca89e2a2013-03-08 17:44:20 -0800477 /**
478 * Trims the contents of the cache, removing items until it's under its
479 * specified limit.
480 *
481 * Trimming is used for caches that support pre-caching from a worker
482 * thread. During pre-caching the maximum limit of the cache can be
483 * exceeded for the duration of the frame. It is therefore required to
484 * trim the cache at the end of the frame to keep the total amount of
485 * memory used under control.
486 *
487 * Only the PathCache currently supports pre-caching.
488 */
489 void trim();
490
491 static void computePathBounds(const SkPath* path, const SkPaint* paint,
492 float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
493 const SkRect& bounds = path->getBounds();
494 computeBounds(bounds, paint, left, top, offset, width, height);
495 }
496
497 static void computeBounds(const SkRect& bounds, const SkPaint* paint,
498 float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
499 const float pathWidth = fmax(bounds.width(), 1.0f);
500 const float pathHeight = fmax(bounds.height(), 1.0f);
501
502 left = bounds.fLeft;
503 top = bounds.fTop;
504
505 offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
506
507 width = uint32_t(pathWidth + offset * 2.0 + 0.5);
508 height = uint32_t(pathHeight + offset * 2.0 + 0.5);
509 }
510
511 static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
512 float left, float top, float offset, uint32_t width, uint32_t height) {
513 initBitmap(bitmap, width, height);
514
515 SkPaint pathPaint(*paint);
516 initPaint(pathPaint);
517
518 SkCanvas canvas(bitmap);
519 canvas.translate(-left + offset, -top + offset);
520 canvas.drawPath(*path, pathPaint);
521 }
522
Romain Guy01d58e42011-01-19 21:54:02 -0800523protected:
524 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
Romain Guyfdd6fc12012-04-27 11:47:13 -0700525 PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
526 void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
527
528 /**
529 * Ensures there is enough space in the cache for a texture of the specified
530 * dimensions.
531 */
532 void purgeCache(uint32_t width, uint32_t height);
533
Romain Guy01d58e42011-01-19 21:54:02 -0800534 PathTexture* get(Entry entry) {
535 return mCache.get(entry);
536 }
537
Romain Guy01d58e42011-01-19 21:54:02 -0800538 void removeTexture(PathTexture* texture);
539
Romain Guyca89e2a2013-03-08 17:44:20 -0800540 bool checkTextureSize(uint32_t width, uint32_t height) {
541 if (width > mMaxTextureSize || height > mMaxTextureSize) {
542 ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
543 mName, width, height, mMaxTextureSize, mMaxTextureSize);
544 return false;
545 }
546 return true;
547 }
548
549 static PathTexture* createTexture(float left, float top, float offset,
Romain Guy5dc7fa72013-03-11 20:48:31 -0700550 uint32_t width, uint32_t height, uint32_t id) {
551 PathTexture* texture = new PathTexture();
Romain Guyca89e2a2013-03-08 17:44:20 -0800552 texture->left = left;
553 texture->top = top;
554 texture->offset = offset;
555 texture->width = width;
556 texture->height = height;
557 texture->generation = id;
558 return texture;
559 }
560
561 static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
562 bitmap.setConfig(SkBitmap::kA8_Config, width, height);
563 bitmap.allocPixels();
564 bitmap.eraseColor(0);
565 }
566
567 static void initPaint(SkPaint& paint) {
568 // Make sure the paint is opaque, color, alpha, filter, etc.
569 // will be applied later when compositing the alpha8 texture
570 paint.setColor(0xff000000);
571 paint.setAlpha(255);
572 paint.setColorFilter(NULL);
573 paint.setMaskFilter(NULL);
574 paint.setShader(NULL);
575 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
576 SkSafeUnref(paint.setXfermode(mode));
577 }
578
Romain Guy059e12c2012-11-28 17:35:51 -0800579 LruCache<Entry, PathTexture*> mCache;
Romain Guy01d58e42011-01-19 21:54:02 -0800580 uint32_t mSize;
581 uint32_t mMaxSize;
582 GLuint mMaxTextureSize;
583
Romain Guyff26a0c2011-01-20 11:35:46 -0800584 char* mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800585 bool mDebugEnabled;
Romain Guyff26a0c2011-01-20 11:35:46 -0800586
587private:
588 /**
589 * Generates the texture from a bitmap into the specified texture structure.
590 */
591 void generateTexture(SkBitmap& bitmap, Texture* texture);
592
593 void init();
Romain Guy01d58e42011-01-19 21:54:02 -0800594}; // class ShapeCache
595
596class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
597public:
598 RoundRectShapeCache();
599
600 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
601}; // class RoundRectShapeCache
602
603class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
604public:
605 CircleShapeCache();
606
607 PathTexture* getCircle(float radius, SkPaint* paint);
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800608}; // class CircleShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800609
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800610class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
611public:
612 OvalShapeCache();
613
614 PathTexture* getOval(float width, float height, SkPaint* paint);
615}; // class OvalShapeCache
616
617class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
618public:
619 RectShapeCache();
620
621 PathTexture* getRect(float width, float height, SkPaint* paint);
622}; // class RectShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800623
Romain Guy8b2f5262011-01-23 16:15:02 -0800624class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
625public:
626 ArcShapeCache();
627
628 PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
629 bool useCenter, SkPaint* paint);
630}; // class ArcShapeCache
631
Romain Guy01d58e42011-01-19 21:54:02 -0800632///////////////////////////////////////////////////////////////////////////////
633// Constructors/destructor
634///////////////////////////////////////////////////////////////////////////////
635
636template<class Entry>
Romain Guyff26a0c2011-01-20 11:35:46 -0800637ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
Romain Guy059e12c2012-11-28 17:35:51 -0800638 mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
Romain Guyff26a0c2011-01-20 11:35:46 -0800639 mSize(0), mMaxSize(MB(defaultSize)) {
Romain Guy01d58e42011-01-19 21:54:02 -0800640 char property[PROPERTY_VALUE_MAX];
Romain Guyff26a0c2011-01-20 11:35:46 -0800641 if (property_get(propertyName, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800642 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
Romain Guy01d58e42011-01-19 21:54:02 -0800643 setMaxSize(MB(atof(property)));
644 } else {
Romain Guyc9855a52011-01-21 21:14:15 -0800645 INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800646 }
Romain Guy01d58e42011-01-19 21:54:02 -0800647
Romain Guyff26a0c2011-01-20 11:35:46 -0800648 size_t len = strlen(name);
649 mName = new char[len + 1];
650 strcpy(mName, name);
651 mName[len] = '\0';
652
Romain Guy01d58e42011-01-19 21:54:02 -0800653 init();
654}
655
656template<class Entry>
657ShapeCache<Entry>::~ShapeCache() {
658 mCache.clear();
Romain Guyff26a0c2011-01-20 11:35:46 -0800659 delete[] mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800660}
661
662template<class Entry>
663void ShapeCache<Entry>::init() {
664 mCache.setOnEntryRemovedListener(this);
665
666 GLint maxTextureSize;
667 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
668 mMaxTextureSize = maxTextureSize;
669
670 mDebugEnabled = readDebugLevel() & kDebugCaches;
671}
672
673///////////////////////////////////////////////////////////////////////////////
674// Size management
675///////////////////////////////////////////////////////////////////////////////
676
677template<class Entry>
678uint32_t ShapeCache<Entry>::getSize() {
679 return mSize;
680}
681
682template<class Entry>
683uint32_t ShapeCache<Entry>::getMaxSize() {
684 return mMaxSize;
685}
686
687template<class Entry>
688void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
689 mMaxSize = maxSize;
690 while (mSize > mMaxSize) {
691 mCache.removeOldest();
692 }
693}
694
695///////////////////////////////////////////////////////////////////////////////
696// Callbacks
697///////////////////////////////////////////////////////////////////////////////
698
699template<class Entry>
700void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
701 removeTexture(texture);
702}
703
704///////////////////////////////////////////////////////////////////////////////
705// Caching
706///////////////////////////////////////////////////////////////////////////////
707
708template<class Entry>
709void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
710 if (texture) {
711 const uint32_t size = texture->width * texture->height;
712 mSize -= size;
713
Romain Guyff26a0c2011-01-20 11:35:46 -0800714 SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
715 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800716 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000717 ALOGD("Shape %s deleted, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800718 }
719
720 glDeleteTextures(1, &texture->id);
721 delete texture;
722 }
723}
724
Romain Guyfdd6fc12012-04-27 11:47:13 -0700725template<class Entry>
726void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
727 const uint32_t size = width * height;
728 // Don't even try to cache a bitmap that's bigger than the cache
729 if (size < mMaxSize) {
730 while (mSize + size > mMaxSize) {
731 mCache.removeOldest();
732 }
733 }
734}
735
736template<class Entry>
Romain Guyca89e2a2013-03-08 17:44:20 -0800737void ShapeCache<Entry>::trim() {
738 while (mSize > mMaxSize) {
739 mCache.removeOldest();
Romain Guyfdd6fc12012-04-27 11:47:13 -0700740 }
Romain Guyfdd6fc12012-04-27 11:47:13 -0700741}
Romain Guy33f6beb2012-02-16 19:24:51 -0800742
Romain Guy01d58e42011-01-19 21:54:02 -0800743template<class Entry>
744PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
745 const SkPaint* paint) {
Romain Guyca89e2a2013-03-08 17:44:20 -0800746 ATRACE_CALL();
Romain Guy01d58e42011-01-19 21:54:02 -0800747
Romain Guy33f6beb2012-02-16 19:24:51 -0800748 float left, top, offset;
749 uint32_t width, height;
750 computePathBounds(path, paint, left, top, offset, width, height);
Romain Guy98029c82011-06-17 15:47:07 -0700751
Romain Guyfdd6fc12012-04-27 11:47:13 -0700752 if (!checkTextureSize(width, height)) return NULL;
Romain Guy01d58e42011-01-19 21:54:02 -0800753
Romain Guyfdd6fc12012-04-27 11:47:13 -0700754 purgeCache(width, height);
Romain Guy01d58e42011-01-19 21:54:02 -0800755
756 SkBitmap bitmap;
Romain Guyca89e2a2013-03-08 17:44:20 -0800757 drawPath(path, paint, bitmap, left, top, offset, width, height);
Romain Guy01d58e42011-01-19 21:54:02 -0800758
Romain Guyca89e2a2013-03-08 17:44:20 -0800759 PathTexture* texture = createTexture(left, top, offset, width, height,
760 path->getGenerationID());
Romain Guyfdd6fc12012-04-27 11:47:13 -0700761 addTexture(entry, &bitmap, texture);
Romain Guy01d58e42011-01-19 21:54:02 -0800762
Romain Guyfdd6fc12012-04-27 11:47:13 -0700763 return texture;
764}
765
766template<class Entry>
767void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
768 generateTexture(*bitmap, texture);
769
770 uint32_t size = texture->width * texture->height;
Romain Guy01d58e42011-01-19 21:54:02 -0800771 if (size < mMaxSize) {
772 mSize += size;
Romain Guyff26a0c2011-01-20 11:35:46 -0800773 SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
774 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800775 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000776 ALOGD("Shape %s created, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800777 }
778 mCache.put(entry, texture);
779 } else {
780 texture->cleanup = true;
781 }
Romain Guy01d58e42011-01-19 21:54:02 -0800782}
783
784template<class Entry>
785void ShapeCache<Entry>::clear() {
786 mCache.clear();
787}
788
789template<class Entry>
790void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
791 SkAutoLockPixels alp(bitmap);
792 if (!bitmap.readyToDraw()) {
Steve Block3762c312012-01-06 19:20:56 +0000793 ALOGE("Cannot generate texture from bitmap");
Romain Guy01d58e42011-01-19 21:54:02 -0800794 return;
795 }
796
797 glGenTextures(1, &texture->id);
798
799 glBindTexture(GL_TEXTURE_2D, texture->id);
800 // Textures are Alpha8
801 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
802
803 texture->blend = true;
804 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
805 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
806
Romain Guyd21b6e12011-11-30 20:21:23 -0800807 texture->setFilter(GL_LINEAR);
808 texture->setWrap(GL_CLAMP_TO_EDGE);
Romain Guy01d58e42011-01-19 21:54:02 -0800809}
810
811}; // namespace uirenderer
812}; // namespace android
813
814#endif // ANDROID_HWUI_SHAPE_CACHE_H