blob: c7d93da718e7b2fa7a4a5101a06c77506edca078 [file] [log] [blame]
Chris Craik05f3d6e2014-06-02 16:27:04 -07001/*
2 * Copyright (C) 2014 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
Chris Craik05f3d6e2014-06-02 16:27:04 -070017#include <utils/JenkinsHash.h>
18#include <utils/Trace.h>
19
20#include "Caches.h"
Chris Craik05f3d6e2014-06-02 16:27:04 -070021#include "PathTessellator.h"
22#include "ShadowTessellator.h"
23#include "TessellationCache.h"
24
25#include "thread/Signal.h"
26#include "thread/Task.h"
27#include "thread/TaskProcessor.h"
28
29namespace android {
30namespace uirenderer {
31
32///////////////////////////////////////////////////////////////////////////////
33// Cache entries
34///////////////////////////////////////////////////////////////////////////////
35
36TessellationCache::Description::Description()
sergeyv7224e2b2016-04-07 18:06:53 -070037 : type(Type::None)
Chris Craik6ac174b2014-06-17 13:47:05 -070038 , scaleX(1.0f)
39 , scaleY(1.0f)
Chris Craiked4ef0b2014-06-12 13:27:30 -070040 , aa(false)
Chris Craik05f3d6e2014-06-02 16:27:04 -070041 , cap(SkPaint::kDefault_Cap)
42 , style(SkPaint::kFill_Style)
43 , strokeWidth(1.0f) {
sergeyv7224e2b2016-04-07 18:06:53 -070044 // Shape bits should be set to zeroes, because they are used for hash calculation.
Chris Craik05f3d6e2014-06-02 16:27:04 -070045 memset(&shape, 0, sizeof(Shape));
46}
47
John Reck1bcacfd2017-11-03 10:12:19 -070048TessellationCache::Description::Description(Type type, const Matrix4& transform,
49 const SkPaint& paint)
Chris Craik05f3d6e2014-06-02 16:27:04 -070050 : type(type)
Chris Craik6ac174b2014-06-17 13:47:05 -070051 , aa(paint.isAntiAlias())
52 , cap(paint.getStrokeCap())
53 , style(paint.getStyle())
54 , strokeWidth(paint.getStrokeWidth()) {
55 PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
sergeyv7224e2b2016-04-07 18:06:53 -070056 // Shape bits should be set to zeroes, because they are used for hash calculation.
Chris Craik05f3d6e2014-06-02 16:27:04 -070057 memset(&shape, 0, sizeof(Shape));
58}
59
sergeyv7224e2b2016-04-07 18:06:53 -070060bool TessellationCache::Description::operator==(const TessellationCache::Description& rhs) const {
61 if (type != rhs.type) return false;
62 if (scaleX != rhs.scaleX) return false;
63 if (scaleY != rhs.scaleY) return false;
64 if (aa != rhs.aa) return false;
65 if (cap != rhs.cap) return false;
66 if (style != rhs.style) return false;
67 if (strokeWidth != rhs.strokeWidth) return false;
68 if (type == Type::None) return true;
69 const Shape::RoundRect& lRect = shape.roundRect;
70 const Shape::RoundRect& rRect = rhs.shape.roundRect;
71
72 if (lRect.width != rRect.width) return false;
73 if (lRect.height != rRect.height) return false;
74 if (lRect.rx != rRect.rx) return false;
75 return lRect.ry == rRect.ry;
76}
77
Chris Craik05f3d6e2014-06-02 16:27:04 -070078hash_t TessellationCache::Description::hash() const {
sergeyv7224e2b2016-04-07 18:06:53 -070079 uint32_t hash = JenkinsHashMix(0, static_cast<int>(type));
Chris Craiked4ef0b2014-06-12 13:27:30 -070080 hash = JenkinsHashMix(hash, aa);
Chris Craik05f3d6e2014-06-02 16:27:04 -070081 hash = JenkinsHashMix(hash, cap);
82 hash = JenkinsHashMix(hash, style);
83 hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
Chris Craik6ac174b2014-06-17 13:47:05 -070084 hash = JenkinsHashMix(hash, android::hash_type(scaleX));
85 hash = JenkinsHashMix(hash, android::hash_type(scaleY));
John Reck1bcacfd2017-11-03 10:12:19 -070086 hash = JenkinsHashMixBytes(hash, (uint8_t*)&shape, sizeof(Shape));
Chris Craik05f3d6e2014-06-02 16:27:04 -070087 return JenkinsHashWhiten(hash);
88}
89
Chris Craik6ac174b2014-06-17 13:47:05 -070090void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const {
91 matrix->loadScale(scaleX, scaleY, 1.0f);
92 paint->setAntiAlias(aa);
93 paint->setStrokeCap(cap);
94 paint->setStyle(style);
95 paint->setStrokeWidth(strokeWidth);
96}
97
John Reck1bcacfd2017-11-03 10:12:19 -070098TessellationCache::ShadowDescription::ShadowDescription() : nodeKey(nullptr) {
sergeyv7224e2b2016-04-07 18:06:53 -070099 memset(&matrixData, 0, sizeof(matrixData));
Chris Craik05f3d6e2014-06-02 16:27:04 -0700100}
101
John Reck1bcacfd2017-11-03 10:12:19 -0700102TessellationCache::ShadowDescription::ShadowDescription(const SkPath* nodeKey,
103 const Matrix4* drawTransform)
Chris Craik05f3d6e2014-06-02 16:27:04 -0700104 : nodeKey(nodeKey) {
sergeyv7224e2b2016-04-07 18:06:53 -0700105 memcpy(&matrixData, drawTransform->data, sizeof(matrixData));
106}
107
108bool TessellationCache::ShadowDescription::operator==(
109 const TessellationCache::ShadowDescription& rhs) const {
John Reck1bcacfd2017-11-03 10:12:19 -0700110 return nodeKey == rhs.nodeKey && memcmp(&matrixData, &rhs.matrixData, sizeof(matrixData)) == 0;
Chris Craik05f3d6e2014-06-02 16:27:04 -0700111}
112
113hash_t TessellationCache::ShadowDescription::hash() const {
John Reck1bcacfd2017-11-03 10:12:19 -0700114 uint32_t hash = JenkinsHashMixBytes(0, (uint8_t*)&nodeKey, sizeof(const void*));
115 hash = JenkinsHashMixBytes(hash, (uint8_t*)&matrixData, sizeof(matrixData));
Chris Craik05f3d6e2014-06-02 16:27:04 -0700116 return JenkinsHashWhiten(hash);
117}
118
119///////////////////////////////////////////////////////////////////////////////
120// General purpose tessellation task processing
121///////////////////////////////////////////////////////////////////////////////
122
123class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
124public:
Chris Craik6ac174b2014-06-17 13:47:05 -0700125 TessellationTask(Tessellator tessellator, const Description& description)
John Reck1bcacfd2017-11-03 10:12:19 -0700126 : tessellator(tessellator), description(description) {}
Chris Craik05f3d6e2014-06-02 16:27:04 -0700127
128 ~TessellationTask() {}
129
130 Tessellator tessellator;
131 Description description;
Chris Craik05f3d6e2014-06-02 16:27:04 -0700132};
133
134class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
135public:
John Reck1bcacfd2017-11-03 10:12:19 -0700136 explicit TessellationProcessor(Caches& caches) : TaskProcessor<VertexBuffer*>(&caches.tasks) {}
Chris Craik05f3d6e2014-06-02 16:27:04 -0700137 ~TessellationProcessor() {}
138
Chris Craikd41c4d82015-01-05 15:51:13 -0800139 virtual void onProcess(const sp<Task<VertexBuffer*> >& task) override {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700140 TessellationTask* t = static_cast<TessellationTask*>(task.get());
141 ATRACE_NAME("shape tessellation");
Chris Craik6ac174b2014-06-17 13:47:05 -0700142 VertexBuffer* buffer = t->tessellator(t->description);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700143 t->setResult(buffer);
144 }
145};
146
John Reck1aa5d2d2014-07-24 13:38:28 -0700147class TessellationCache::Buffer {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700148public:
John Reck1bcacfd2017-11-03 10:12:19 -0700149 explicit Buffer(const sp<Task<VertexBuffer*> >& task) : mTask(task), mBuffer(nullptr) {}
Chris Craik05f3d6e2014-06-02 16:27:04 -0700150
151 ~Buffer() {
152 mTask.clear();
153 delete mBuffer;
154 }
155
156 unsigned int getSize() {
157 blockOnPrecache();
158 return mBuffer->getSize();
159 }
160
161 const VertexBuffer* getVertexBuffer() {
162 blockOnPrecache();
163 return mBuffer;
164 }
165
166private:
167 void blockOnPrecache() {
Chris Craikd41c4d82015-01-05 15:51:13 -0800168 if (mTask != nullptr) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700169 mBuffer = mTask->getResult();
Chris Craikd41c4d82015-01-05 15:51:13 -0800170 LOG_ALWAYS_FATAL_IF(mBuffer == nullptr, "Failed to precache");
Chris Craik05f3d6e2014-06-02 16:27:04 -0700171 mTask.clear();
172 }
173 }
174 sp<Task<VertexBuffer*> > mTask;
175 VertexBuffer* mBuffer;
176};
177
178///////////////////////////////////////////////////////////////////////////////
179// Shadow tessellation task processing
180///////////////////////////////////////////////////////////////////////////////
181
Chris Craik05f3d6e2014-06-02 16:27:04 -0700182static void mapPointFakeZ(Vector3& point, const mat4* transformXY, const mat4* transformZ) {
183 // map z coordinate with true 3d matrix
184 point.z = transformZ->mapZ(point);
185
186 // map x,y coordinates with draw/Skia matrix
187 transformXY->mapPoint(point.x, point.y);
188}
189
Chris Craikfca52b752015-04-28 11:45:59 -0700190static void reverseVertexArray(Vertex* polygon, int len) {
191 int n = len / 2;
192 for (int i = 0; i < n; i++) {
193 Vertex tmp = polygon[i];
194 int k = len - 1 - i;
195 polygon[i] = polygon[k];
196 polygon[k] = tmp;
197 }
198}
199
John Reck1bcacfd2017-11-03 10:12:19 -0700200void tessellateShadows(const Matrix4* drawTransform, const Rect* localClip, bool isCasterOpaque,
201 const SkPath* casterPerimeter, const Matrix4* casterTransformXY,
202 const Matrix4* casterTransformZ, const Vector3& lightCenter,
203 float lightRadius, VertexBuffer& ambientBuffer, VertexBuffer& spotBuffer) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700204 // tessellate caster outline into a 2d polygon
John Reck272a6852015-07-29 16:48:58 -0700205 std::vector<Vertex> casterVertices2d;
ztenghui21ef8202015-05-28 16:06:28 -0700206 const float casterRefinementThreshold = 2.0f;
John Reck1bcacfd2017-11-03 10:12:19 -0700207 PathTessellator::approximatePathOutlineVertices(*casterPerimeter, casterRefinementThreshold,
208 casterVertices2d);
Chris Craikfca52b752015-04-28 11:45:59 -0700209
210 // Shadow requires CCW for now. TODO: remove potential double-reverse
John Reck272a6852015-07-29 16:48:58 -0700211 reverseVertexArray(&casterVertices2d.front(), casterVertices2d.size());
Chris Craik05f3d6e2014-06-02 16:27:04 -0700212
213 if (casterVertices2d.size() == 0) return;
214
215 // map 2d caster poly into 3d
216 const int casterVertexCount = casterVertices2d.size();
217 Vector3 casterPolygon[casterVertexCount];
218 float minZ = FLT_MAX;
219 float maxZ = -FLT_MAX;
220 for (int i = 0; i < casterVertexCount; i++) {
221 const Vertex& point2d = casterVertices2d[i];
John Reck1aa5d2d2014-07-24 13:38:28 -0700222 casterPolygon[i] = (Vector3){point2d.x, point2d.y, 0};
Chris Craik05f3d6e2014-06-02 16:27:04 -0700223 mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
Chris Craike6a15ee2015-07-07 18:42:17 -0700224 minZ = std::min(minZ, casterPolygon[i].z);
225 maxZ = std::max(maxZ, casterPolygon[i].z);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700226 }
227
228 // map the centroid of the caster into 3d
John Reck1bcacfd2017-11-03 10:12:19 -0700229 Vector2 centroid = ShadowTessellator::centroid2d(
230 reinterpret_cast<const Vector2*>(&casterVertices2d.front()), casterVertexCount);
John Reck1aa5d2d2014-07-24 13:38:28 -0700231 Vector3 centroid3d = {centroid.x, centroid.y, 0};
Chris Craik05f3d6e2014-06-02 16:27:04 -0700232 mapPointFakeZ(centroid3d, casterTransformXY, casterTransformZ);
233
234 // if the caster intersects the z=0 plane, lift it in Z so it doesn't
235 if (minZ < SHADOW_MIN_CASTER_Z) {
236 float casterLift = SHADOW_MIN_CASTER_Z - minZ;
237 for (int i = 0; i < casterVertexCount; i++) {
238 casterPolygon[i].z += casterLift;
239 }
240 centroid3d.z += casterLift;
241 }
242
243 // Check whether we want to draw the shadow at all by checking the caster's bounds against clip.
244 // We only have ortho projection, so we can just ignore the Z in caster for
245 // simple rejection calculation.
246 Rect casterBounds(casterPerimeter->getBounds());
247 casterTransformXY->mapRect(casterBounds);
248
249 // actual tessellation of both shadows
John Reck1bcacfd2017-11-03 10:12:19 -0700250 ShadowTessellator::tessellateAmbientShadow(isCasterOpaque, casterPolygon, casterVertexCount,
251 centroid3d, casterBounds, *localClip, maxZ,
252 ambientBuffer);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700253
John Reck1bcacfd2017-11-03 10:12:19 -0700254 ShadowTessellator::tessellateSpotShadow(isCasterOpaque, casterPolygon, casterVertexCount,
255 centroid3d, *drawTransform, lightCenter, lightRadius,
256 casterBounds, *localClip, spotBuffer);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700257}
258
Chris Craikd8165e82016-02-03 15:52:25 -0800259class ShadowProcessor : public TaskProcessor<TessellationCache::vertexBuffer_pair_t> {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700260public:
Chih-Hung Hsiehc6baf562016-04-27 11:29:23 -0700261 explicit ShadowProcessor(Caches& caches)
Chris Craikd8165e82016-02-03 15:52:25 -0800262 : TaskProcessor<TessellationCache::vertexBuffer_pair_t>(&caches.tasks) {}
Chris Craik05f3d6e2014-06-02 16:27:04 -0700263 ~ShadowProcessor() {}
264
Chris Craikd8165e82016-02-03 15:52:25 -0800265 virtual void onProcess(const sp<Task<TessellationCache::vertexBuffer_pair_t> >& task) override {
Chris Craik6e068c012016-01-15 16:15:30 -0800266 TessellationCache::ShadowTask* t = static_cast<TessellationCache::ShadowTask*>(task.get());
Chris Craik05f3d6e2014-06-02 16:27:04 -0700267 ATRACE_NAME("shadow tessellation");
268
Chris Craik1b3be082014-06-11 13:44:19 -0700269 tessellateShadows(&t->drawTransform, &t->localClip, t->opaque, &t->casterPerimeter,
John Reck1bcacfd2017-11-03 10:12:19 -0700270 &t->transformXY, &t->transformZ, t->lightCenter, t->lightRadius,
271 t->ambientBuffer, t->spotBuffer);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700272
Chris Craikd8165e82016-02-03 15:52:25 -0800273 t->setResult(TessellationCache::vertexBuffer_pair_t(&t->ambientBuffer, &t->spotBuffer));
Chris Craik05f3d6e2014-06-02 16:27:04 -0700274 }
275};
276
277///////////////////////////////////////////////////////////////////////////////
278// Cache constructor/destructor
279///////////////////////////////////////////////////////////////////////////////
280
281TessellationCache::TessellationCache()
John Reck8dc02f92017-07-17 09:55:02 -0700282 : mMaxSize(MB(1))
Chris Craik05f3d6e2014-06-02 16:27:04 -0700283 , mCache(LruCache<Description, Buffer*>::kUnlimitedCapacity)
John Reck1bcacfd2017-11-03 10:12:19 -0700284 , mShadowCache(
285 LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*>::kUnlimitedCapacity) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700286 mCache.setOnEntryRemovedListener(&mBufferRemovedListener);
287 mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener);
Chris Craik2507c342015-05-04 14:36:49 -0700288 mDebugEnabled = Properties::debugLevel & kDebugCaches;
Chris Craik05f3d6e2014-06-02 16:27:04 -0700289}
290
291TessellationCache::~TessellationCache() {
292 mCache.clear();
293}
294
295///////////////////////////////////////////////////////////////////////////////
296// Size management
297///////////////////////////////////////////////////////////////////////////////
298
299uint32_t TessellationCache::getSize() {
300 LruCache<Description, Buffer*>::Iterator iter(mCache);
301 uint32_t size = 0;
302 while (iter.next()) {
303 size += iter.value()->getSize();
304 }
305 return size;
306}
307
308uint32_t TessellationCache::getMaxSize() {
309 return mMaxSize;
310}
311
Chris Craik05f3d6e2014-06-02 16:27:04 -0700312///////////////////////////////////////////////////////////////////////////////
313// Caching
314///////////////////////////////////////////////////////////////////////////////
315
Chris Craik05f3d6e2014-06-02 16:27:04 -0700316void TessellationCache::trim() {
317 uint32_t size = getSize();
318 while (size > mMaxSize) {
319 size -= mCache.peekOldestValue()->getSize();
320 mCache.removeOldest();
321 }
322 mShadowCache.clear();
323}
324
325void TessellationCache::clear() {
326 mCache.clear();
327 mShadowCache.clear();
328}
329
330///////////////////////////////////////////////////////////////////////////////
331// Callbacks
332///////////////////////////////////////////////////////////////////////////////
333
Andreas Gampe64bb4132014-11-22 00:35:09 +0000334void TessellationCache::BufferRemovedListener::operator()(Description& description,
John Reck1bcacfd2017-11-03 10:12:19 -0700335 Buffer*& buffer) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700336 delete buffer;
337}
338
339///////////////////////////////////////////////////////////////////////////////
340// Shadows
341///////////////////////////////////////////////////////////////////////////////
342
343void TessellationCache::precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
John Reck1bcacfd2017-11-03 10:12:19 -0700344 bool opaque, const SkPath* casterPerimeter,
345 const Matrix4* transformXY, const Matrix4* transformZ,
346 const Vector3& lightCenter, float lightRadius) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700347 ShadowDescription key(casterPerimeter, drawTransform);
348
Mykola Kondratenkob1596332015-03-12 15:20:38 +0100349 if (mShadowCache.get(key)) return;
John Reck1bcacfd2017-11-03 10:12:19 -0700350 sp<ShadowTask> task = new ShadowTask(drawTransform, localClip, opaque, casterPerimeter,
351 transformXY, transformZ, lightCenter, lightRadius);
Chris Craikd41c4d82015-01-05 15:51:13 -0800352 if (mShadowProcessor == nullptr) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700353 mShadowProcessor = new ShadowProcessor(Caches::getInstance());
354 }
Chris Craikdee66b62015-04-20 14:54:49 -0700355 mShadowProcessor->add(task);
John Reck1bcacfd2017-11-03 10:12:19 -0700356 task->incStrong(nullptr); // not using sp<>s, so manually ref while in the cache
Chris Craik05f3d6e2014-06-02 16:27:04 -0700357 mShadowCache.put(key, task.get());
358}
359
Chris Craik6e068c012016-01-15 16:15:30 -0800360sp<TessellationCache::ShadowTask> TessellationCache::getShadowTask(
John Reck1bcacfd2017-11-03 10:12:19 -0700361 const Matrix4* drawTransform, const Rect& localClip, bool opaque,
362 const SkPath* casterPerimeter, const Matrix4* transformXY, const Matrix4* transformZ,
Chris Craik6e068c012016-01-15 16:15:30 -0800363 const Vector3& lightCenter, float lightRadius) {
364 ShadowDescription key(casterPerimeter, drawTransform);
365 ShadowTask* task = static_cast<ShadowTask*>(mShadowCache.get(key));
366 if (!task) {
John Reck1bcacfd2017-11-03 10:12:19 -0700367 precacheShadows(drawTransform, localClip, opaque, casterPerimeter, transformXY, transformZ,
368 lightCenter, lightRadius);
Chris Craik6e068c012016-01-15 16:15:30 -0800369 task = static_cast<ShadowTask*>(mShadowCache.get(key));
370 }
371 LOG_ALWAYS_FATAL_IF(task == nullptr, "shadow not precached");
372 return task;
373}
374
Chris Craik05f3d6e2014-06-02 16:27:04 -0700375///////////////////////////////////////////////////////////////////////////////
376// Tessellation precaching
377///////////////////////////////////////////////////////////////////////////////
378
John Reck1bcacfd2017-11-03 10:12:19 -0700379TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(const Description& entry,
380 Tessellator tessellator) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700381 Buffer* buffer = mCache.get(entry);
382 if (!buffer) {
383 // not cached, enqueue a task to fill the buffer
Chris Craik6ac174b2014-06-17 13:47:05 -0700384 sp<TessellationTask> task = new TessellationTask(tessellator, entry);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700385 buffer = new Buffer(task);
386
Chris Craikd41c4d82015-01-05 15:51:13 -0800387 if (mProcessor == nullptr) {
Chris Craik05f3d6e2014-06-02 16:27:04 -0700388 mProcessor = new TessellationProcessor(Caches::getInstance());
389 }
Chris Craikdee66b62015-04-20 14:54:49 -0700390 mProcessor->add(task);
George Burgess IV13d7dda2017-07-25 14:22:07 -0700391 bool inserted = mCache.put(entry, buffer);
392 // Note to the static analyzer that this insert should always succeed.
393 LOG_ALWAYS_FATAL_IF(!inserted, "buffers shouldn't spontaneously appear in the cache");
Chris Craik05f3d6e2014-06-02 16:27:04 -0700394 }
395 return buffer;
396}
397
Chris Craik6ac174b2014-06-17 13:47:05 -0700398static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
John Reck1bcacfd2017-11-03 10:12:19 -0700399 const SkPath& path) {
Chris Craik6ac174b2014-06-17 13:47:05 -0700400 Matrix4 matrix;
401 SkPaint paint;
402 description.setupMatrixAndPaint(&matrix, &paint);
403 VertexBuffer* buffer = new VertexBuffer();
404 PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
405 return buffer;
406}
407
Chris Craik05f3d6e2014-06-02 16:27:04 -0700408///////////////////////////////////////////////////////////////////////////////
Chris Craik6ac174b2014-06-17 13:47:05 -0700409// RoundRect
Chris Craik05f3d6e2014-06-02 16:27:04 -0700410///////////////////////////////////////////////////////////////////////////////
411
Chris Craik6ac174b2014-06-17 13:47:05 -0700412static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
John Reck1bcacfd2017-11-03 10:12:19 -0700413 SkRect rect =
414 SkRect::MakeWH(description.shape.roundRect.width, description.shape.roundRect.height);
Chris Craik6ac174b2014-06-17 13:47:05 -0700415 float rx = description.shape.roundRect.rx;
416 float ry = description.shape.roundRect.ry;
417 if (description.style == SkPaint::kStrokeAndFill_Style) {
418 float outset = description.strokeWidth / 2;
Chris Craik05f3d6e2014-06-02 16:27:04 -0700419 rect.outset(outset, outset);
420 rx += outset;
421 ry += outset;
422 }
423 SkPath path;
424 path.addRoundRect(rect, rx, ry);
Chris Craik6ac174b2014-06-17 13:47:05 -0700425 return tessellatePath(description, path);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700426}
427
John Reck1bcacfd2017-11-03 10:12:19 -0700428TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
429 const SkPaint& paint, float width,
430 float height, float rx, float ry) {
sergeyv7224e2b2016-04-07 18:06:53 -0700431 Description entry(Description::Type::RoundRect, transform, paint);
Chris Craik6ac174b2014-06-17 13:47:05 -0700432 entry.shape.roundRect.width = width;
433 entry.shape.roundRect.height = height;
434 entry.shape.roundRect.rx = rx;
435 entry.shape.roundRect.ry = ry;
436 return getOrCreateBuffer(entry, &tessellateRoundRect);
Chris Craik05f3d6e2014-06-02 16:27:04 -0700437}
Chris Craik6ac174b2014-06-17 13:47:05 -0700438const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
John Reck1bcacfd2017-11-03 10:12:19 -0700439 float width, float height, float rx, float ry) {
Chris Craik6ac174b2014-06-17 13:47:05 -0700440 return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
Chris Craik05f3d6e2014-06-02 16:27:04 -0700441}
442
John Reck1bcacfd2017-11-03 10:12:19 -0700443}; // namespace uirenderer
444}; // namespace android