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