blob: 67ae85b459ce44317fae982e21d9d23a21221c0e [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>
33#include <utils/CallStack.h>
Romain Guy059e12c2012-11-28 17:35:51 -080034
Romain Guyff26a0c2011-01-20 11:35:46 -080035#include "Debug.h"
Romain Guy01d58e42011-01-19 21:54:02 -080036#include "Properties.h"
Romain Guyff26a0c2011-01-20 11:35:46 -080037#include "Texture.h"
Romain Guyca89e2a2013-03-08 17:44:20 -080038#include "thread/Future.h"
Romain Guy01d58e42011-01-19 21:54:02 -080039
40namespace android {
41namespace uirenderer {
42
43///////////////////////////////////////////////////////////////////////////////
44// Defines
45///////////////////////////////////////////////////////////////////////////////
46
47// Debug
48#if DEBUG_SHAPES
Steve Block5baa3a62011-12-20 16:23:08 +000049 #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__)
Romain Guy01d58e42011-01-19 21:54:02 -080050#else
51 #define SHAPE_LOGD(...)
52#endif
53
54///////////////////////////////////////////////////////////////////////////////
55// Classes
56///////////////////////////////////////////////////////////////////////////////
57
58/**
Romain Guyff26a0c2011-01-20 11:35:46 -080059 * Alpha texture used to represent a path.
60 */
61struct PathTexture: public Texture {
62 PathTexture(): Texture() {
63 }
64
Romain Guyca89e2a2013-03-08 17:44:20 -080065 PathTexture(bool hasFuture): Texture() {
66 if (hasFuture) {
67 mFuture = new Future<SkBitmap*>();
68 }
69 }
70
71 ~PathTexture() {
72 clearFuture();
73 }
74
Romain Guyff26a0c2011-01-20 11:35:46 -080075 /**
76 * Left coordinate of the path bounds.
77 */
78 float left;
79 /**
80 * Top coordinate of the path bounds.
81 */
82 float top;
83 /**
84 * Offset to draw the path at the correct origin.
85 */
86 float offset;
Romain Guyca89e2a2013-03-08 17:44:20 -080087
88 sp<Future<SkBitmap*> > future() const {
89 return mFuture;
90 }
91
92 void clearFuture() {
93 if (mFuture != NULL) {
94 delete mFuture->get();
95 mFuture.clear();
96 }
97 }
98
99private:
100 sp<Future<SkBitmap*> > mFuture;
Romain Guyff26a0c2011-01-20 11:35:46 -0800101}; // struct PathTexture
102
103/**
Romain Guy01d58e42011-01-19 21:54:02 -0800104 * Describe a shape in the shape cache.
105 */
106struct ShapeCacheEntry {
107 enum ShapeType {
108 kShapeNone,
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800109 kShapeRect,
Romain Guy01d58e42011-01-19 21:54:02 -0800110 kShapeRoundRect,
111 kShapeCircle,
112 kShapeOval,
Romain Guyff26a0c2011-01-20 11:35:46 -0800113 kShapeArc,
114 kShapePath
Romain Guy01d58e42011-01-19 21:54:02 -0800115 };
116
117 ShapeCacheEntry() {
118 shapeType = kShapeNone;
119 join = SkPaint::kDefault_Join;
120 cap = SkPaint::kDefault_Cap;
121 style = SkPaint::kFill_Style;
Romain Guy059e12c2012-11-28 17:35:51 -0800122 miter = 4.0f;
123 strokeWidth = 1.0f;
Romain Guy1af23a32011-03-24 16:03:55 -0700124 pathEffect = NULL;
Romain Guy01d58e42011-01-19 21:54:02 -0800125 }
126
Romain Guy01d58e42011-01-19 21:54:02 -0800127 ShapeCacheEntry(ShapeType type, SkPaint* paint) {
128 shapeType = type;
129 join = paint->getStrokeJoin();
130 cap = paint->getStrokeCap();
Romain Guy059e12c2012-11-28 17:35:51 -0800131 miter = paint->getStrokeMiter();
132 strokeWidth = paint->getStrokeWidth();
Romain Guy01d58e42011-01-19 21:54:02 -0800133 style = paint->getStyle();
Romain Guyb29cfbf2011-03-18 16:24:19 -0700134 pathEffect = paint->getPathEffect();
Romain Guy01d58e42011-01-19 21:54:02 -0800135 }
136
137 virtual ~ShapeCacheEntry() {
138 }
139
Romain Guy059e12c2012-11-28 17:35:51 -0800140 virtual hash_t hash() const {
141 uint32_t hash = JenkinsHashMix(0, shapeType);
142 hash = JenkinsHashMix(hash, join);
143 hash = JenkinsHashMix(hash, cap);
144 hash = JenkinsHashMix(hash, style);
145 hash = JenkinsHashMix(hash, android::hash_type(miter));
146 hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
147 hash = JenkinsHashMix(hash, android::hash_type(pathEffect));
148 return JenkinsHashWhiten(hash);
149 }
150
151 virtual int compare(const ShapeCacheEntry& rhs) const {
152 int deltaInt = shapeType - rhs.shapeType;
153 if (deltaInt != 0) return deltaInt;
154
155 deltaInt = join - rhs.join;
156 if (deltaInt != 0) return deltaInt;
157
158 deltaInt = cap - rhs.cap;
159 if (deltaInt != 0) return deltaInt;
160
161 deltaInt = style - rhs.style;
162 if (deltaInt != 0) return deltaInt;
163
164 if (miter < rhs.miter) return -1;
165 if (miter > rhs.miter) return +1;
166
167 if (strokeWidth < rhs.strokeWidth) return -1;
168 if (strokeWidth > rhs.strokeWidth) return +1;
169
170 if (pathEffect < rhs.pathEffect) return -1;
171 if (pathEffect > rhs.pathEffect) return +1;
172
173 return 0;
174 }
175
176 bool operator==(const ShapeCacheEntry& other) const {
177 return compare(other) == 0;
178 }
179
180 bool operator!=(const ShapeCacheEntry& other) const {
181 return compare(other) != 0;
182 }
183
Romain Guy01d58e42011-01-19 21:54:02 -0800184 ShapeType shapeType;
185 SkPaint::Join join;
186 SkPaint::Cap cap;
187 SkPaint::Style style;
Romain Guy059e12c2012-11-28 17:35:51 -0800188 float miter;
189 float strokeWidth;
Romain Guyb29cfbf2011-03-18 16:24:19 -0700190 SkPathEffect* pathEffect;
Romain Guy01d58e42011-01-19 21:54:02 -0800191}; // struct ShapeCacheEntry
192
Romain Guy059e12c2012-11-28 17:35:51 -0800193// Cache support
194
195inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
196 return lhs.compare(rhs) < 0;
197}
198
199inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) {
200 return lhs.compare(rhs);
201}
202
203inline hash_t hash_type(const ShapeCacheEntry& entry) {
204 return entry.hash();
205}
Romain Guy01d58e42011-01-19 21:54:02 -0800206
207struct RoundRectShapeCacheEntry: public ShapeCacheEntry {
208 RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint):
209 ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800210 mWidth = width;
211 mHeight = height;
212 mRx = rx;
213 mRy = ry;
Romain Guy01d58e42011-01-19 21:54:02 -0800214 }
215
216 RoundRectShapeCacheEntry(): ShapeCacheEntry() {
217 mWidth = 0;
218 mHeight = 0;
219 mRx = 0;
220 mRy = 0;
221 }
222
Romain Guy059e12c2012-11-28 17:35:51 -0800223 hash_t hash() const {
224 uint32_t hash = ShapeCacheEntry::hash();
225 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
226 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
227 hash = JenkinsHashMix(hash, android::hash_type(mRx));
228 hash = JenkinsHashMix(hash, android::hash_type(mRy));
229 return JenkinsHashWhiten(hash);
230 }
231
232 int compare(const ShapeCacheEntry& r) const {
233 int deltaInt = ShapeCacheEntry::compare(r);
234 if (deltaInt != 0) return deltaInt;
235
Romain Guy01d58e42011-01-19 21:54:02 -0800236 const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800237
238 if (mWidth < rhs.mWidth) return -1;
239 if (mWidth > rhs.mWidth) return +1;
240
241 if (mHeight < rhs.mHeight) return -1;
242 if (mHeight > rhs.mHeight) return +1;
243
244 if (mRx < rhs.mRx) return -1;
245 if (mRx > rhs.mRx) return +1;
246
247 if (mRy < rhs.mRy) return -1;
248 if (mRy > rhs.mRy) return +1;
249
250 return 0;
Romain Guy01d58e42011-01-19 21:54:02 -0800251 }
252
253private:
Romain Guy059e12c2012-11-28 17:35:51 -0800254 float mWidth;
255 float mHeight;
256 float mRx;
257 float mRy;
Romain Guy01d58e42011-01-19 21:54:02 -0800258}; // RoundRectShapeCacheEntry
259
Romain Guy059e12c2012-11-28 17:35:51 -0800260inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) {
261 return entry.hash();
262}
263
Romain Guy01d58e42011-01-19 21:54:02 -0800264struct CircleShapeCacheEntry: public ShapeCacheEntry {
265 CircleShapeCacheEntry(float radius, SkPaint* paint):
266 ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800267 mRadius = radius;
Romain Guy01d58e42011-01-19 21:54:02 -0800268 }
269
270 CircleShapeCacheEntry(): ShapeCacheEntry() {
271 mRadius = 0;
272 }
273
Romain Guy059e12c2012-11-28 17:35:51 -0800274 hash_t hash() const {
275 uint32_t hash = ShapeCacheEntry::hash();
276 hash = JenkinsHashMix(hash, android::hash_type(mRadius));
277 return JenkinsHashWhiten(hash);
278 }
279
280 int compare(const ShapeCacheEntry& r) const {
281 int deltaInt = ShapeCacheEntry::compare(r);
282 if (deltaInt != 0) return deltaInt;
283
Romain Guy01d58e42011-01-19 21:54:02 -0800284 const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800285
286 if (mRadius < rhs.mRadius) return -1;
287 if (mRadius > rhs.mRadius) return +1;
288
289 return 0;
Romain Guy01d58e42011-01-19 21:54:02 -0800290 }
291
292private:
Romain Guy059e12c2012-11-28 17:35:51 -0800293 float mRadius;
Romain Guy01d58e42011-01-19 21:54:02 -0800294}; // CircleShapeCacheEntry
295
Romain Guy059e12c2012-11-28 17:35:51 -0800296inline hash_t hash_type(const CircleShapeCacheEntry& entry) {
297 return entry.hash();
298}
299
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800300struct OvalShapeCacheEntry: public ShapeCacheEntry {
301 OvalShapeCacheEntry(float width, float height, SkPaint* paint):
302 ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800303 mWidth = width;
304 mHeight = height;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800305 }
306
307 OvalShapeCacheEntry(): ShapeCacheEntry() {
308 mWidth = mHeight = 0;
309 }
310
Romain Guy059e12c2012-11-28 17:35:51 -0800311 hash_t hash() const {
312 uint32_t hash = ShapeCacheEntry::hash();
313 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
314 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
315 return JenkinsHashWhiten(hash);
316 }
317
318 int compare(const ShapeCacheEntry& r) const {
319 int deltaInt = ShapeCacheEntry::compare(r);
320 if (deltaInt != 0) return deltaInt;
321
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800322 const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800323
324 if (mWidth < rhs.mWidth) return -1;
325 if (mWidth > rhs.mWidth) return +1;
326
327 if (mHeight < rhs.mHeight) return -1;
328 if (mHeight > rhs.mHeight) return +1;
329
330 return 0;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800331 }
332
333private:
Romain Guy059e12c2012-11-28 17:35:51 -0800334 float mWidth;
335 float mHeight;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800336}; // OvalShapeCacheEntry
337
Romain Guy059e12c2012-11-28 17:35:51 -0800338inline hash_t hash_type(const OvalShapeCacheEntry& entry) {
339 return entry.hash();
340}
341
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800342struct RectShapeCacheEntry: public ShapeCacheEntry {
343 RectShapeCacheEntry(float width, float height, SkPaint* paint):
344 ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800345 mWidth = width;
346 mHeight = height;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800347 }
348
349 RectShapeCacheEntry(): ShapeCacheEntry() {
350 mWidth = mHeight = 0;
351 }
352
Romain Guy059e12c2012-11-28 17:35:51 -0800353 hash_t hash() const {
354 uint32_t hash = ShapeCacheEntry::hash();
355 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
356 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
357 return JenkinsHashWhiten(hash);
358 }
359
360 int compare(const ShapeCacheEntry& r) const {
361 int deltaInt = ShapeCacheEntry::compare(r);
362 if (deltaInt != 0) return deltaInt;
363
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800364 const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800365
366 if (mWidth < rhs.mWidth) return -1;
367 if (mWidth > rhs.mWidth) return +1;
368
369 if (mHeight < rhs.mHeight) return -1;
370 if (mHeight > rhs.mHeight) return +1;
371
372 return 0;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800373 }
374
375private:
Romain Guy059e12c2012-11-28 17:35:51 -0800376 float mWidth;
377 float mHeight;
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800378}; // RectShapeCacheEntry
379
Romain Guy059e12c2012-11-28 17:35:51 -0800380inline hash_t hash_type(const RectShapeCacheEntry& entry) {
381 return entry.hash();
382}
383
Romain Guy8b2f5262011-01-23 16:15:02 -0800384struct ArcShapeCacheEntry: public ShapeCacheEntry {
385 ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle,
386 bool useCenter, SkPaint* paint):
387 ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) {
Romain Guy059e12c2012-11-28 17:35:51 -0800388 mWidth = width;
389 mHeight = height;
390 mStartAngle = startAngle;
391 mSweepAngle = sweepAngle;
Romain Guy8b2f5262011-01-23 16:15:02 -0800392 mUseCenter = useCenter ? 1 : 0;
393 }
394
395 ArcShapeCacheEntry(): ShapeCacheEntry() {
396 mWidth = 0;
397 mHeight = 0;
398 mStartAngle = 0;
399 mSweepAngle = 0;
400 mUseCenter = 0;
401 }
402
Romain Guy059e12c2012-11-28 17:35:51 -0800403 hash_t hash() const {
404 uint32_t hash = ShapeCacheEntry::hash();
405 hash = JenkinsHashMix(hash, android::hash_type(mWidth));
406 hash = JenkinsHashMix(hash, android::hash_type(mHeight));
407 hash = JenkinsHashMix(hash, android::hash_type(mStartAngle));
408 hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle));
409 hash = JenkinsHashMix(hash, mUseCenter);
410 return JenkinsHashWhiten(hash);
411 }
412
413 int compare(const ShapeCacheEntry& r) const {
414 int deltaInt = ShapeCacheEntry::compare(r);
415 if (deltaInt != 0) return deltaInt;
416
Romain Guy8b2f5262011-01-23 16:15:02 -0800417 const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r;
Romain Guy059e12c2012-11-28 17:35:51 -0800418
419 if (mWidth < rhs.mWidth) return -1;
420 if (mWidth > rhs.mWidth) return +1;
421
422 if (mHeight < rhs.mHeight) return -1;
423 if (mHeight > rhs.mHeight) return +1;
424
425 if (mStartAngle < rhs.mStartAngle) return -1;
426 if (mStartAngle > rhs.mStartAngle) return +1;
427
428 if (mSweepAngle < rhs.mSweepAngle) return -1;
429 if (mSweepAngle > rhs.mSweepAngle) return +1;
430
431 return mUseCenter - rhs.mUseCenter;
Romain Guy8b2f5262011-01-23 16:15:02 -0800432 }
433
434private:
Romain Guy059e12c2012-11-28 17:35:51 -0800435 float mWidth;
436 float mHeight;
437 float mStartAngle;
438 float mSweepAngle;
Romain Guy8b2f5262011-01-23 16:15:02 -0800439 uint32_t mUseCenter;
440}; // ArcShapeCacheEntry
441
Romain Guy059e12c2012-11-28 17:35:51 -0800442inline hash_t hash_type(const ArcShapeCacheEntry& entry) {
443 return entry.hash();
444}
445
Romain Guy01d58e42011-01-19 21:54:02 -0800446/**
447 * A simple LRU shape cache. The cache has a maximum size expressed in bytes.
448 * Any texture added to the cache causing the cache to grow beyond the maximum
449 * allowed size will also cause the oldest texture to be kicked out.
450 */
451template<typename Entry>
452class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> {
453public:
Romain Guyff26a0c2011-01-20 11:35:46 -0800454 ShapeCache(const char* name, const char* propertyName, float defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800455 ~ShapeCache();
456
457 /**
458 * Used as a callback when an entry is removed from the cache.
459 * Do not invoke directly.
460 */
461 void operator()(Entry& path, PathTexture*& texture);
462
463 /**
464 * Clears the cache. This causes all textures to be deleted.
465 */
466 void clear();
467
468 /**
469 * Sets the maximum size of the cache in bytes.
470 */
471 void setMaxSize(uint32_t maxSize);
472 /**
473 * Returns the maximum size of the cache in bytes.
474 */
475 uint32_t getMaxSize();
476 /**
477 * Returns the current size of the cache in bytes.
478 */
479 uint32_t getSize();
480
Romain Guyca89e2a2013-03-08 17:44:20 -0800481 /**
482 * Trims the contents of the cache, removing items until it's under its
483 * specified limit.
484 *
485 * Trimming is used for caches that support pre-caching from a worker
486 * thread. During pre-caching the maximum limit of the cache can be
487 * exceeded for the duration of the frame. It is therefore required to
488 * trim the cache at the end of the frame to keep the total amount of
489 * memory used under control.
490 *
491 * Only the PathCache currently supports pre-caching.
492 */
493 void trim();
494
495 static void computePathBounds(const SkPath* path, const SkPaint* paint,
496 float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
497 const SkRect& bounds = path->getBounds();
498 computeBounds(bounds, paint, left, top, offset, width, height);
499 }
500
501 static void computeBounds(const SkRect& bounds, const SkPaint* paint,
502 float& left, float& top, float& offset, uint32_t& width, uint32_t& height) {
503 const float pathWidth = fmax(bounds.width(), 1.0f);
504 const float pathHeight = fmax(bounds.height(), 1.0f);
505
506 left = bounds.fLeft;
507 top = bounds.fTop;
508
509 offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
510
511 width = uint32_t(pathWidth + offset * 2.0 + 0.5);
512 height = uint32_t(pathHeight + offset * 2.0 + 0.5);
513 }
514
515 static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
516 float left, float top, float offset, uint32_t width, uint32_t height) {
517 initBitmap(bitmap, width, height);
518
519 SkPaint pathPaint(*paint);
520 initPaint(pathPaint);
521
522 SkCanvas canvas(bitmap);
523 canvas.translate(-left + offset, -top + offset);
524 canvas.drawPath(*path, pathPaint);
525 }
526
Romain Guy01d58e42011-01-19 21:54:02 -0800527protected:
528 PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint);
Romain Guyfdd6fc12012-04-27 11:47:13 -0700529 PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap);
530 void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture);
531
532 /**
533 * Ensures there is enough space in the cache for a texture of the specified
534 * dimensions.
535 */
536 void purgeCache(uint32_t width, uint32_t height);
537
Romain Guy01d58e42011-01-19 21:54:02 -0800538 PathTexture* get(Entry entry) {
539 return mCache.get(entry);
540 }
541
Romain Guy01d58e42011-01-19 21:54:02 -0800542 void removeTexture(PathTexture* texture);
543
Romain Guyca89e2a2013-03-08 17:44:20 -0800544 bool checkTextureSize(uint32_t width, uint32_t height) {
545 if (width > mMaxTextureSize || height > mMaxTextureSize) {
546 ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)",
547 mName, width, height, mMaxTextureSize, mMaxTextureSize);
548 return false;
549 }
550 return true;
551 }
552
553 static PathTexture* createTexture(float left, float top, float offset,
554 uint32_t width, uint32_t height, uint32_t id, bool hasFuture = false) {
555 PathTexture* texture = new PathTexture(hasFuture);
556 texture->left = left;
557 texture->top = top;
558 texture->offset = offset;
559 texture->width = width;
560 texture->height = height;
561 texture->generation = id;
562 return texture;
563 }
564
565 static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
566 bitmap.setConfig(SkBitmap::kA8_Config, width, height);
567 bitmap.allocPixels();
568 bitmap.eraseColor(0);
569 }
570
571 static void initPaint(SkPaint& paint) {
572 // Make sure the paint is opaque, color, alpha, filter, etc.
573 // will be applied later when compositing the alpha8 texture
574 paint.setColor(0xff000000);
575 paint.setAlpha(255);
576 paint.setColorFilter(NULL);
577 paint.setMaskFilter(NULL);
578 paint.setShader(NULL);
579 SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode);
580 SkSafeUnref(paint.setXfermode(mode));
581 }
582
Romain Guy059e12c2012-11-28 17:35:51 -0800583 LruCache<Entry, PathTexture*> mCache;
Romain Guy01d58e42011-01-19 21:54:02 -0800584 uint32_t mSize;
585 uint32_t mMaxSize;
586 GLuint mMaxTextureSize;
587
Romain Guyff26a0c2011-01-20 11:35:46 -0800588 char* mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800589 bool mDebugEnabled;
Romain Guyff26a0c2011-01-20 11:35:46 -0800590
591private:
592 /**
593 * Generates the texture from a bitmap into the specified texture structure.
594 */
595 void generateTexture(SkBitmap& bitmap, Texture* texture);
596
597 void init();
Romain Guy01d58e42011-01-19 21:54:02 -0800598}; // class ShapeCache
599
600class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> {
601public:
602 RoundRectShapeCache();
603
604 PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint);
605}; // class RoundRectShapeCache
606
607class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> {
608public:
609 CircleShapeCache();
610
611 PathTexture* getCircle(float radius, SkPaint* paint);
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800612}; // class CircleShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800613
Romain Guyc1cd9ba32011-01-23 14:18:41 -0800614class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> {
615public:
616 OvalShapeCache();
617
618 PathTexture* getOval(float width, float height, SkPaint* paint);
619}; // class OvalShapeCache
620
621class RectShapeCache: public ShapeCache<RectShapeCacheEntry> {
622public:
623 RectShapeCache();
624
625 PathTexture* getRect(float width, float height, SkPaint* paint);
626}; // class RectShapeCache
Romain Guy01d58e42011-01-19 21:54:02 -0800627
Romain Guy8b2f5262011-01-23 16:15:02 -0800628class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> {
629public:
630 ArcShapeCache();
631
632 PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
633 bool useCenter, SkPaint* paint);
634}; // class ArcShapeCache
635
Romain Guy01d58e42011-01-19 21:54:02 -0800636///////////////////////////////////////////////////////////////////////////////
637// Constructors/destructor
638///////////////////////////////////////////////////////////////////////////////
639
640template<class Entry>
Romain Guyff26a0c2011-01-20 11:35:46 -0800641ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize):
Romain Guy059e12c2012-11-28 17:35:51 -0800642 mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity),
Romain Guyff26a0c2011-01-20 11:35:46 -0800643 mSize(0), mMaxSize(MB(defaultSize)) {
Romain Guy01d58e42011-01-19 21:54:02 -0800644 char property[PROPERTY_VALUE_MAX];
Romain Guyff26a0c2011-01-20 11:35:46 -0800645 if (property_get(propertyName, property, NULL) > 0) {
Romain Guyc9855a52011-01-21 21:14:15 -0800646 INIT_LOGD(" Setting %s cache size to %sMB", name, property);
Romain Guy01d58e42011-01-19 21:54:02 -0800647 setMaxSize(MB(atof(property)));
648 } else {
Romain Guyc9855a52011-01-21 21:14:15 -0800649 INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800650 }
Romain Guy01d58e42011-01-19 21:54:02 -0800651
Romain Guyff26a0c2011-01-20 11:35:46 -0800652 size_t len = strlen(name);
653 mName = new char[len + 1];
654 strcpy(mName, name);
655 mName[len] = '\0';
656
Romain Guy01d58e42011-01-19 21:54:02 -0800657 init();
658}
659
660template<class Entry>
661ShapeCache<Entry>::~ShapeCache() {
662 mCache.clear();
Romain Guyff26a0c2011-01-20 11:35:46 -0800663 delete[] mName;
Romain Guy01d58e42011-01-19 21:54:02 -0800664}
665
666template<class Entry>
667void ShapeCache<Entry>::init() {
668 mCache.setOnEntryRemovedListener(this);
669
670 GLint maxTextureSize;
671 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
672 mMaxTextureSize = maxTextureSize;
673
674 mDebugEnabled = readDebugLevel() & kDebugCaches;
675}
676
677///////////////////////////////////////////////////////////////////////////////
678// Size management
679///////////////////////////////////////////////////////////////////////////////
680
681template<class Entry>
682uint32_t ShapeCache<Entry>::getSize() {
683 return mSize;
684}
685
686template<class Entry>
687uint32_t ShapeCache<Entry>::getMaxSize() {
688 return mMaxSize;
689}
690
691template<class Entry>
692void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) {
693 mMaxSize = maxSize;
694 while (mSize > mMaxSize) {
695 mCache.removeOldest();
696 }
697}
698
699///////////////////////////////////////////////////////////////////////////////
700// Callbacks
701///////////////////////////////////////////////////////////////////////////////
702
703template<class Entry>
704void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) {
705 removeTexture(texture);
706}
707
708///////////////////////////////////////////////////////////////////////////////
709// Caching
710///////////////////////////////////////////////////////////////////////////////
711
712template<class Entry>
713void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
714 if (texture) {
715 const uint32_t size = texture->width * texture->height;
716 mSize -= size;
717
Romain Guyff26a0c2011-01-20 11:35:46 -0800718 SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d",
719 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800720 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000721 ALOGD("Shape %s deleted, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800722 }
723
724 glDeleteTextures(1, &texture->id);
725 delete texture;
726 }
727}
728
Romain Guyfdd6fc12012-04-27 11:47:13 -0700729template<class Entry>
730void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
731 const uint32_t size = width * height;
732 // Don't even try to cache a bitmap that's bigger than the cache
733 if (size < mMaxSize) {
734 while (mSize + size > mMaxSize) {
735 mCache.removeOldest();
736 }
737 }
738}
739
740template<class Entry>
Romain Guyca89e2a2013-03-08 17:44:20 -0800741void ShapeCache<Entry>::trim() {
742 while (mSize > mMaxSize) {
743 mCache.removeOldest();
Romain Guyfdd6fc12012-04-27 11:47:13 -0700744 }
Romain Guyfdd6fc12012-04-27 11:47:13 -0700745}
Romain Guy33f6beb2012-02-16 19:24:51 -0800746
Romain Guy01d58e42011-01-19 21:54:02 -0800747template<class Entry>
748PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path,
749 const SkPaint* paint) {
Romain Guyca89e2a2013-03-08 17:44:20 -0800750 ATRACE_CALL();
Romain Guy01d58e42011-01-19 21:54:02 -0800751
Romain Guy33f6beb2012-02-16 19:24:51 -0800752 float left, top, offset;
753 uint32_t width, height;
754 computePathBounds(path, paint, left, top, offset, width, height);
Romain Guy98029c82011-06-17 15:47:07 -0700755
Romain Guyfdd6fc12012-04-27 11:47:13 -0700756 if (!checkTextureSize(width, height)) return NULL;
Romain Guy01d58e42011-01-19 21:54:02 -0800757
Romain Guyfdd6fc12012-04-27 11:47:13 -0700758 purgeCache(width, height);
Romain Guy01d58e42011-01-19 21:54:02 -0800759
760 SkBitmap bitmap;
Romain Guyca89e2a2013-03-08 17:44:20 -0800761 drawPath(path, paint, bitmap, left, top, offset, width, height);
Romain Guy01d58e42011-01-19 21:54:02 -0800762
Romain Guyca89e2a2013-03-08 17:44:20 -0800763 PathTexture* texture = createTexture(left, top, offset, width, height,
764 path->getGenerationID());
Romain Guyfdd6fc12012-04-27 11:47:13 -0700765 addTexture(entry, &bitmap, texture);
Romain Guy01d58e42011-01-19 21:54:02 -0800766
Romain Guyfdd6fc12012-04-27 11:47:13 -0700767 return texture;
768}
769
770template<class Entry>
771void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) {
772 generateTexture(*bitmap, texture);
773
774 uint32_t size = texture->width * texture->height;
Romain Guy01d58e42011-01-19 21:54:02 -0800775 if (size < mMaxSize) {
776 mSize += size;
Romain Guyff26a0c2011-01-20 11:35:46 -0800777 SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d",
778 mName, texture->id, size, mSize);
Romain Guy01d58e42011-01-19 21:54:02 -0800779 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000780 ALOGD("Shape %s created, size = %d", mName, size);
Romain Guy01d58e42011-01-19 21:54:02 -0800781 }
782 mCache.put(entry, texture);
783 } else {
784 texture->cleanup = true;
785 }
Romain Guy01d58e42011-01-19 21:54:02 -0800786}
787
788template<class Entry>
789void ShapeCache<Entry>::clear() {
790 mCache.clear();
791}
792
793template<class Entry>
794void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) {
795 SkAutoLockPixels alp(bitmap);
796 if (!bitmap.readyToDraw()) {
Steve Block3762c312012-01-06 19:20:56 +0000797 ALOGE("Cannot generate texture from bitmap");
Romain Guy01d58e42011-01-19 21:54:02 -0800798 return;
799 }
800
801 glGenTextures(1, &texture->id);
802
803 glBindTexture(GL_TEXTURE_2D, texture->id);
804 // Textures are Alpha8
805 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
806
807 texture->blend = true;
808 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0,
809 GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels());
810
Romain Guyd21b6e12011-11-30 20:21:23 -0800811 texture->setFilter(GL_LINEAR);
812 texture->setWrap(GL_CLAMP_TO_EDGE);
Romain Guy01d58e42011-01-19 21:54:02 -0800813}
814
815}; // namespace uirenderer
816}; // namespace android
817
818#endif // ANDROID_HWUI_SHAPE_CACHE_H