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