PathTessellator.cpp revision 74cf7e6a25c6d7b331c231b7bc2512044f9d2950
14eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato/* 24eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Copyright (C) 2012 The Android Open Source Project 34eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * 44eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Licensed under the Apache License, Version 2.0 (the "License"); 54eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * you may not use this file except in compliance with the License. 64eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * You may obtain a copy of the License at 74eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * 84eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * http://www.apache.org/licenses/LICENSE-2.0 94eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * 104eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Unless required by applicable law or agreed to in writing, software 114eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * distributed under the License is distributed on an "AS IS" BASIS, 124eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * See the License for the specific language governing permissions and 144eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * limitations under the License. 154eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato */ 164eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 174eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define LOG_TAG "OpenGLRenderer" 184eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define LOG_NDEBUG 1 194eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define ATRACE_TAG ATRACE_TAG_VIEW 204eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 214eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define VERTEX_DEBUG 0 224eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 234eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#if VERTEX_DEBUG 244eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define DEBUG_DUMP_ALPHA_BUFFER() \ 254eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ 264eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato ALOGD("point %d at %f %f, alpha %f", \ 274eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato i, buffer[i].x, buffer[i].y, buffer[i].alpha); \ 284eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 294eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define DEBUG_DUMP_BUFFER() \ 304eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ 314eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \ 324eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 334eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#else 344eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define DEBUG_DUMP_ALPHA_BUFFER() 354eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define DEBUG_DUMP_BUFFER() 364eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#endif 374eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 384eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include <SkPath.h> 394eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include <SkPaint.h> 404eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 414eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include <stdlib.h> 424eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include <stdint.h> 434eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include <sys/types.h> 444eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 454eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include <utils/Log.h> 464eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include <utils/Trace.h> 474eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 484eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include "PathTessellator.h" 494eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include "Matrix.h" 504eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include "Vector.h" 514eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include "Vertex.h" 524eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#include "utils/MathUtils.h" 534eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 544eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onoratonamespace android { 554eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onoratonamespace uirenderer { 564eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 574eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f) 584eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define ROUND_CAP_THRESH 0.25f 594eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define PI 3.1415926535897932f 604eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato#define MAX_DEPTH 15 614eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 624eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato/** 634eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Extracts the x and y scale from the transform as positive values, and clamps them 644eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato */ 654eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onoratovoid PathTessellator::extractTessellationScales(const Matrix4& transform, 664eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float* scaleX, float* scaleY) { 674eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (CC_LIKELY(transform.isPureTranslate())) { 684eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato *scaleX = 1.0f; 694eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato *scaleY = 1.0f; 704eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } else { 714eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float m00 = transform.data[Matrix4::kScaleX]; 724eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float m01 = transform.data[Matrix4::kSkewY]; 734eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float m10 = transform.data[Matrix4::kSkewX]; 744eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float m11 = transform.data[Matrix4::kScaleY]; 754eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01)); 764eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11)); 774eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 784eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato} 794eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 804eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato/** 814eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset 824eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices 834eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * will be offset by 1.0 844eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * 854eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Note that we can't add and normalize the two vectors, that would result in a rectangle having an 864eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * offset of (sqrt(2)/2, sqrt(2)/2) at each corner, instead of (1, 1) 874eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * 884eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * NOTE: assumes angles between normals 90 degrees or less 894eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato */ 904eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onoratoinline static Vector2 totalOffsetFromNormals(const Vector2& normalA, const Vector2& normalB) { 914eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato return (normalA + normalB) / (1 + fabs(normalA.dot(normalB))); 924eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato} 934eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 944eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato/** 954eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Structure used for storing useful information about the SkPaint and scale used for tessellating 964eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato */ 974eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onoratostruct PaintInfo { 984eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onoratopublic: 994eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato PaintInfo(const SkPaint* paint, const mat4& transform) : 1004eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), 1014eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { 1024eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato // compute inverse scales 1034eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (CC_LIKELY(transform.isPureTranslate())) { 1044eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato inverseScaleX = 1.0f; 1054eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato inverseScaleY = 1.0f; 1064eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } else { 1074eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float scaleX, scaleY; 1084eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); 1094eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato inverseScaleX = 1.0f / scaleX; 1104eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato inverseScaleY = 1.0f / scaleY; 1114eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1124eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1134eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY && 1144eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 2 * halfStrokeWidth < inverseScaleX) { 1154eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato // AA, with non-hairline stroke, width < 1 pixel. Scale alpha and treat as hairline. 1164eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato maxAlpha *= (2 * halfStrokeWidth) / inverseScaleX; 1174eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato halfStrokeWidth = 0.0f; 1184eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1194eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1204eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1214eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato SkPaint::Style style; 1224eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato SkPaint::Cap cap; 1234eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato bool isAA; 1244eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float inverseScaleX; 1254eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float inverseScaleY; 1264eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float halfStrokeWidth; 1274eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float maxAlpha; 1284eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1294eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato inline void scaleOffsetForStrokeWidth(Vector2& offset) const { 1304eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (halfStrokeWidth == 0.0f) { 1314eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato // hairline - compensate for scale 1324eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato offset.x *= 0.5f * inverseScaleX; 1334eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato offset.y *= 0.5f * inverseScaleY; 1344eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } else { 1354eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato offset *= halfStrokeWidth; 1364eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1374eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1384eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1394eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato /** 1404eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * NOTE: the input will not always be a normal, especially for sharp edges - it should be the 1414eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * result of totalOffsetFromNormals (see documentation there) 1424eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato */ 1434eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato inline Vector2 deriveAAOffset(const Vector2& offset) const { 1444eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato return (Vector2){offset.x * 0.5f * inverseScaleX, offset.y * 0.5f * inverseScaleY}; 1454eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1464eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1474eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato /** 1484eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Returns the number of cap divisions beyond the minimum 2 (kButt_Cap/kSquareCap will return 0) 1494eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Should only be used when stroking and drawing caps 1504eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato */ 1514eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato inline int capExtraDivisions() const { 1524eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (cap == SkPaint::kRound_Cap) { 1534eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (halfStrokeWidth == 0.0f) return 2; 1544eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1554eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato // ROUND_CAP_THRESH is the maximum error for polygonal approximation of the round cap 1564eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato const float errConst = (-ROUND_CAP_THRESH / halfStrokeWidth + 1); 1574eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato const float targetCosVal = 2 * errConst * errConst - 1; 1584eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato int neededDivisions = (int)(ceilf(PI / acos(targetCosVal)/2)) * 2; 1594eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato return neededDivisions; 1604eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1614eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato return 0; 1624eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1634eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1644eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato /** 1654eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * Outset the bounds of point data (for line endpoints or points) to account for AA stroke 1664eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato * geometry. 1674eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato */ 1684eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato void expandBoundsForStroke(Rect* bounds) const { 1694eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato float outset = halfStrokeWidth; 1704eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (outset == 0) outset = 0.5f; 1714eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato bounds->outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(), 1724eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato outset * inverseScaleY + Vertex::GeometryFudgeFactor()); 1734eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato } 1744eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato}; 1754eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1764eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onoratovoid getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { 1774eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size()); 1784eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato 1794eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato int currentIndex = 0; 1804eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato // zig zag between all previous points on the inside of the hull to create a 1814eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato // triangle strip that fills the hull 1824eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato int srcAindex = 0; 1834eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato int srcBindex = perimeter.size() - 1; 1844eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato while (srcAindex <= srcBindex) { 1854eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato buffer[currentIndex++] = perimeter[srcAindex]; 1864eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato if (srcAindex == srcBindex) break; 1874eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato buffer[currentIndex++] = perimeter[srcBindex]; 1884eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato srcAindex++; 1894eb64fdbcf899a81d0a6a04dc3658d03d9df8247Joe Onorato srcBindex--; 190 } 191} 192 193/* 194 * Fills a vertexBuffer with non-alpha vertices, zig-zagging at each perimeter point to create a 195 * tri-strip as wide as the stroke. 196 * 197 * Uses an additional 2 vertices at the end to wrap around, closing the tri-strip 198 * (for a total of perimeter.size() * 2 + 2 vertices) 199 */ 200void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, 201 VertexBuffer& vertexBuffer) { 202 Vertex* buffer = vertexBuffer.alloc<Vertex>(perimeter.size() * 2 + 2); 203 204 int currentIndex = 0; 205 const Vertex* last = &(perimeter[perimeter.size() - 1]); 206 const Vertex* current = &(perimeter[0]); 207 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 208 lastNormal.normalize(); 209 for (unsigned int i = 0; i < perimeter.size(); i++) { 210 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 211 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 212 nextNormal.normalize(); 213 214 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 215 paintInfo.scaleOffsetForStrokeWidth(totalOffset); 216 217 Vertex::set(&buffer[currentIndex++], 218 current->x + totalOffset.x, 219 current->y + totalOffset.y); 220 221 Vertex::set(&buffer[currentIndex++], 222 current->x - totalOffset.x, 223 current->y - totalOffset.y); 224 225 last = current; 226 current = next; 227 lastNormal = nextNormal; 228 } 229 230 // wrap around to beginning 231 buffer[currentIndex++] = buffer[0]; 232 buffer[currentIndex++] = buffer[1]; 233 234 DEBUG_DUMP_BUFFER(); 235} 236 237static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center, 238 const Vector2& normal, Vertex* buffer, int& currentIndex, bool begin) { 239 Vector2 strokeOffset = normal; 240 paintInfo.scaleOffsetForStrokeWidth(strokeOffset); 241 242 Vector2 referencePoint = {center.x, center.y}; 243 if (paintInfo.cap == SkPaint::kSquare_Cap) { 244 Vector2 rotated = {-strokeOffset.y, strokeOffset.x}; 245 referencePoint += rotated * (begin ? -1 : 1); 246 } 247 248 Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset); 249 Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset); 250} 251 252/** 253 * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except: 254 * 255 * 1 - Doesn't need to wrap around, since the input vertices are unclosed 256 * 257 * 2 - can zig-zag across 'extra' vertices at either end, to create round caps 258 */ 259void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, 260 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) { 261 const int extra = paintInfo.capExtraDivisions(); 262 const int allocSize = (vertices.size() + extra) * 2; 263 Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize); 264 265 const int lastIndex = vertices.size() - 1; 266 if (extra > 0) { 267 // tessellate both round caps 268 float beginTheta = atan2( 269 - (vertices[0].x - vertices[1].x), 270 vertices[0].y - vertices[1].y); 271 float endTheta = atan2( 272 - (vertices[lastIndex].x - vertices[lastIndex - 1].x), 273 vertices[lastIndex].y - vertices[lastIndex - 1].y); 274 const float dTheta = PI / (extra + 1); 275 const float radialScale = 2.0f / (1 + cos(dTheta)); 276 277 int capOffset; 278 for (int i = 0; i < extra; i++) { 279 if (i < extra / 2) { 280 capOffset = extra - 2 * i - 1; 281 } else { 282 capOffset = 2 * i - extra; 283 } 284 285 beginTheta += dTheta; 286 Vector2 beginRadialOffset = {cos(beginTheta), sin(beginTheta)}; 287 paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset); 288 Vertex::set(&buffer[capOffset], 289 vertices[0].x + beginRadialOffset.x, 290 vertices[0].y + beginRadialOffset.y); 291 292 endTheta += dTheta; 293 Vector2 endRadialOffset = {cos(endTheta), sin(endTheta)}; 294 paintInfo.scaleOffsetForStrokeWidth(endRadialOffset); 295 Vertex::set(&buffer[allocSize - 1 - capOffset], 296 vertices[lastIndex].x + endRadialOffset.x, 297 vertices[lastIndex].y + endRadialOffset.y); 298 } 299 } 300 301 int currentIndex = extra; 302 const Vertex* last = &(vertices[0]); 303 const Vertex* current = &(vertices[1]); 304 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 305 lastNormal.normalize(); 306 307 storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true); 308 309 for (unsigned int i = 1; i < vertices.size() - 1; i++) { 310 const Vertex* next = &(vertices[i + 1]); 311 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 312 nextNormal.normalize(); 313 314 Vector2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal); 315 paintInfo.scaleOffsetForStrokeWidth(strokeOffset); 316 317 Vector2 center = {current->x, current->y}; 318 Vertex::set(&buffer[currentIndex++], center + strokeOffset); 319 Vertex::set(&buffer[currentIndex++], center - strokeOffset); 320 321 current = next; 322 lastNormal = nextNormal; 323 } 324 325 storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false); 326 327 DEBUG_DUMP_BUFFER(); 328} 329 330/** 331 * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation 332 * 333 * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of 334 * the shape (using 2 * perimeter.size() vertices) 335 * 336 * 2 - wrap around to the beginning to complete the perimeter (2 vertices) 337 * 338 * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices) 339 */ 340void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, 341 VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) { 342 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2); 343 344 // generate alpha points - fill Alpha vertex gaps in between each point with 345 // alpha 0 vertex, offset by a scaled normal. 346 int currentIndex = 0; 347 const Vertex* last = &(perimeter[perimeter.size() - 1]); 348 const Vertex* current = &(perimeter[0]); 349 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 350 lastNormal.normalize(); 351 for (unsigned int i = 0; i < perimeter.size(); i++) { 352 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 353 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 354 nextNormal.normalize(); 355 356 // AA point offset from original point is that point's normal, such that each side is offset 357 // by .5 pixels 358 Vector2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal)); 359 360 AlphaVertex::set(&buffer[currentIndex++], 361 current->x + totalOffset.x, 362 current->y + totalOffset.y, 363 0.0f); 364 AlphaVertex::set(&buffer[currentIndex++], 365 current->x - totalOffset.x, 366 current->y - totalOffset.y, 367 maxAlpha); 368 369 last = current; 370 current = next; 371 lastNormal = nextNormal; 372 } 373 374 // wrap around to beginning 375 buffer[currentIndex++] = buffer[0]; 376 buffer[currentIndex++] = buffer[1]; 377 378 // zig zag between all previous points on the inside of the hull to create a 379 // triangle strip that fills the hull, repeating the first inner point to 380 // create degenerate tris to start inside path 381 int srcAindex = 0; 382 int srcBindex = perimeter.size() - 1; 383 while (srcAindex <= srcBindex) { 384 buffer[currentIndex++] = buffer[srcAindex * 2 + 1]; 385 if (srcAindex == srcBindex) break; 386 buffer[currentIndex++] = buffer[srcBindex * 2 + 1]; 387 srcAindex++; 388 srcBindex--; 389 } 390 391 DEBUG_DUMP_BUFFER(); 392} 393 394/** 395 * Stores geometry for a single, AA-perimeter (potentially rounded) cap 396 * 397 * For explanation of constants and general methodoloyg, see comments for 398 * getStrokeVerticesFromUnclosedVerticesAA() below. 399 */ 400inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices, 401 AlphaVertex* buffer, bool isFirst, Vector2 normal, int offset) { 402 const int extra = paintInfo.capExtraDivisions(); 403 const int extraOffset = (extra + 1) / 2; 404 const int capIndex = isFirst 405 ? 2 * offset + 6 + 2 * (extra + extraOffset) 406 : offset + 2 + 2 * extraOffset; 407 if (isFirst) normal *= -1; 408 409 // TODO: this normal should be scaled by radialScale if extra != 0, see totalOffsetFromNormals() 410 Vector2 AAOffset = paintInfo.deriveAAOffset(normal); 411 412 Vector2 strokeOffset = normal; 413 paintInfo.scaleOffsetForStrokeWidth(strokeOffset); 414 Vector2 outerOffset = strokeOffset + AAOffset; 415 Vector2 innerOffset = strokeOffset - AAOffset; 416 417 Vector2 capAAOffset = {0, 0}; 418 if (paintInfo.cap != SkPaint::kRound_Cap) { 419 // if the cap is square or butt, the inside primary cap vertices will be inset in two 420 // directions - both normal to the stroke, and parallel to it. 421 capAAOffset = (Vector2){-AAOffset.y, AAOffset.x}; 422 } 423 424 // determine referencePoint, the center point for the 4 primary cap vertices 425 const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1); 426 Vector2 referencePoint = {point->x, point->y}; 427 if (paintInfo.cap == SkPaint::kSquare_Cap) { 428 // To account for square cap, move the primary cap vertices (that create the AA edge) by the 429 // stroke offset vector (rotated to be parallel to the stroke) 430 Vector2 rotated = {-strokeOffset.y, strokeOffset.x}; 431 referencePoint += rotated; 432 } 433 434 AlphaVertex::set(&buffer[capIndex + 0], 435 referencePoint.x + outerOffset.x + capAAOffset.x, 436 referencePoint.y + outerOffset.y + capAAOffset.y, 437 0.0f); 438 AlphaVertex::set(&buffer[capIndex + 1], 439 referencePoint.x + innerOffset.x - capAAOffset.x, 440 referencePoint.y + innerOffset.y - capAAOffset.y, 441 paintInfo.maxAlpha); 442 443 bool isRound = paintInfo.cap == SkPaint::kRound_Cap; 444 445 const int postCapIndex = (isRound && isFirst) ? (2 * extraOffset - 2) : capIndex + (2 * extra); 446 AlphaVertex::set(&buffer[postCapIndex + 2], 447 referencePoint.x - outerOffset.x + capAAOffset.x, 448 referencePoint.y - outerOffset.y + capAAOffset.y, 449 0.0f); 450 AlphaVertex::set(&buffer[postCapIndex + 3], 451 referencePoint.x - innerOffset.x - capAAOffset.x, 452 referencePoint.y - innerOffset.y - capAAOffset.y, 453 paintInfo.maxAlpha); 454 455 if (isRound) { 456 const float dTheta = PI / (extra + 1); 457 const float radialScale = 2.0f / (1 + cos(dTheta)); 458 float theta = atan2(normal.y, normal.x); 459 int capPerimIndex = capIndex + 2; 460 461 for (int i = 0; i < extra; i++) { 462 theta += dTheta; 463 464 Vector2 radialOffset = {cos(theta), sin(theta)}; 465 466 // scale to compensate for pinching at sharp angles, see totalOffsetFromNormals() 467 radialOffset *= radialScale; 468 469 AAOffset = paintInfo.deriveAAOffset(radialOffset); 470 paintInfo.scaleOffsetForStrokeWidth(radialOffset); 471 AlphaVertex::set(&buffer[capPerimIndex++], 472 referencePoint.x + radialOffset.x + AAOffset.x, 473 referencePoint.y + radialOffset.y + AAOffset.y, 474 0.0f); 475 AlphaVertex::set(&buffer[capPerimIndex++], 476 referencePoint.x + radialOffset.x - AAOffset.x, 477 referencePoint.y + radialOffset.y - AAOffset.y, 478 paintInfo.maxAlpha); 479 480 if (isFirst && i == extra - extraOffset) { 481 //copy most recent two points to first two points 482 buffer[0] = buffer[capPerimIndex - 2]; 483 buffer[1] = buffer[capPerimIndex - 1]; 484 485 capPerimIndex = 2; // start writing the rest of the round cap at index 2 486 } 487 } 488 489 if (isFirst) { 490 const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4; 491 int capFillIndex = startCapFillIndex; 492 for (int i = 0; i < extra + 2; i += 2) { 493 buffer[capFillIndex++] = buffer[1 + i]; 494 // TODO: to support odd numbers of divisions, break here on the last iteration 495 buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i]; 496 } 497 } else { 498 int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2); 499 for (int i = 0; i < extra + 2; i += 2) { 500 buffer[capFillIndex++] = buffer[capIndex + 1 + i]; 501 // TODO: to support odd numbers of divisions, break here on the last iteration 502 buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i]; 503 } 504 } 505 return; 506 } 507 if (isFirst) { 508 buffer[0] = buffer[postCapIndex + 2]; 509 buffer[1] = buffer[postCapIndex + 3]; 510 buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!) 511 buffer[postCapIndex + 5] = buffer[postCapIndex + 1]; 512 } else { 513 buffer[6 * vertices.size()] = buffer[postCapIndex + 1]; 514 buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3]; 515 } 516} 517 518/* 519the geometry for an aa, capped stroke consists of the following: 520 521 # vertices | function 522---------------------------------------------------------------------- 523a) 2 | Start AA perimeter 524b) 2, 2 * roundDivOff | First half of begin cap's perimeter 525 | 526 2 * middlePts | 'Outer' or 'Top' AA perimeter half (between caps) 527 | 528a) 4 | End cap's 529b) 2, 2 * roundDivs, 2 | AA perimeter 530 | 531 2 * middlePts | 'Inner' or 'bottom' AA perimeter half 532 | 533a) 6 | Begin cap's perimeter 534b) 2, 2*(rD - rDO + 1), | Last half of begin cap's perimeter 535 roundDivs, 2 | 536 | 537 2 * middlePts | Stroke's full opacity center strip 538 | 539a) 2 | end stroke 540b) 2, roundDivs | (and end cap fill, for round) 541 542Notes: 543* rows starting with 'a)' denote the Butt or Square cap vertex use, 'b)' denote Round 544 545* 'middlePts' is (number of points in the unclosed input vertex list, minus 2) times two 546 547* 'roundDivs' or 'rD' is the number of extra vertices (beyond the minimum of 2) that define the 548 round cap's shape, and is at least two. This will increase with cap size to sufficiently 549 define the cap's level of tessellation. 550 551* 'roundDivOffset' or 'rDO' is the point about halfway along the start cap's round perimeter, where 552 the stream of vertices for the AA perimeter starts. By starting and ending the perimeter at 553 this offset, the fill of the stroke is drawn from this point with minimal extra vertices. 554 555This means the outer perimeter starts at: 556 outerIndex = (2) OR (2 + 2 * roundDivOff) 557the inner perimeter (since it is filled in reverse) starts at: 558 innerIndex = outerIndex + (4 * middlePts) + ((4) OR (4 + 2 * roundDivs)) - 1 559the stroke starts at: 560 strokeIndex = innerIndex + 1 + ((6) OR (6 + 3 * roundDivs - 2 * roundDivOffset)) 561 562The total needed allocated space is either: 563 2 + 4 + 6 + 2 + 3 * (2 * middlePts) = 14 + 6 * middlePts = 2 + 6 * pts 564or, for rounded caps: 565 (2 + 2 * rDO) + (4 + 2 * rD) + (2 * (rD - rDO + 1) 566 + roundDivs + 4) + (2 + roundDivs) + 3 * (2 * middlePts) 567 = 14 + 6 * middlePts + 6 * roundDivs 568 = 2 + 6 * pts + 6 * roundDivs 569 */ 570void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, 571 const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) { 572 573 const int extra = paintInfo.capExtraDivisions(); 574 const int allocSize = 6 * vertices.size() + 2 + 6 * extra; 575 576 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(allocSize); 577 578 const int extraOffset = (extra + 1) / 2; 579 int offset = 2 * (vertices.size() - 2); 580 // there is no outer/inner here, using them for consistency with below approach 581 int currentAAOuterIndex = 2 + 2 * extraOffset; 582 int currentAAInnerIndex = currentAAOuterIndex + (2 * offset) + 3 + (2 * extra); 583 int currentStrokeIndex = currentAAInnerIndex + 7 + (3 * extra - 2 * extraOffset); 584 585 const Vertex* last = &(vertices[0]); 586 const Vertex* current = &(vertices[1]); 587 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 588 lastNormal.normalize(); 589 590 // TODO: use normal from bezier traversal for cap, instead of from vertices 591 storeCapAA(paintInfo, vertices, buffer, true, lastNormal, offset); 592 593 for (unsigned int i = 1; i < vertices.size() - 1; i++) { 594 const Vertex* next = &(vertices[i + 1]); 595 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 596 nextNormal.normalize(); 597 598 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 599 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset); 600 601 Vector2 innerOffset = totalOffset; 602 paintInfo.scaleOffsetForStrokeWidth(innerOffset); 603 Vector2 outerOffset = innerOffset + AAOffset; 604 innerOffset -= AAOffset; 605 606 AlphaVertex::set(&buffer[currentAAOuterIndex++], 607 current->x + outerOffset.x, 608 current->y + outerOffset.y, 609 0.0f); 610 AlphaVertex::set(&buffer[currentAAOuterIndex++], 611 current->x + innerOffset.x, 612 current->y + innerOffset.y, 613 paintInfo.maxAlpha); 614 615 AlphaVertex::set(&buffer[currentStrokeIndex++], 616 current->x + innerOffset.x, 617 current->y + innerOffset.y, 618 paintInfo.maxAlpha); 619 AlphaVertex::set(&buffer[currentStrokeIndex++], 620 current->x - innerOffset.x, 621 current->y - innerOffset.y, 622 paintInfo.maxAlpha); 623 624 AlphaVertex::set(&buffer[currentAAInnerIndex--], 625 current->x - innerOffset.x, 626 current->y - innerOffset.y, 627 paintInfo.maxAlpha); 628 AlphaVertex::set(&buffer[currentAAInnerIndex--], 629 current->x - outerOffset.x, 630 current->y - outerOffset.y, 631 0.0f); 632 633 current = next; 634 lastNormal = nextNormal; 635 } 636 637 // TODO: use normal from bezier traversal for cap, instead of from vertices 638 storeCapAA(paintInfo, vertices, buffer, false, lastNormal, offset); 639 640 DEBUG_DUMP_ALPHA_BUFFER(); 641} 642 643 644void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, 645 VertexBuffer& vertexBuffer) { 646 AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(6 * perimeter.size() + 8); 647 648 int offset = 2 * perimeter.size() + 3; 649 int currentAAOuterIndex = 0; 650 int currentStrokeIndex = offset; 651 int currentAAInnerIndex = offset * 2; 652 653 const Vertex* last = &(perimeter[perimeter.size() - 1]); 654 const Vertex* current = &(perimeter[0]); 655 Vector2 lastNormal = {current->y - last->y, last->x - current->x}; 656 lastNormal.normalize(); 657 for (unsigned int i = 0; i < perimeter.size(); i++) { 658 const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); 659 Vector2 nextNormal = {next->y - current->y, current->x - next->x}; 660 nextNormal.normalize(); 661 662 Vector2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); 663 Vector2 AAOffset = paintInfo.deriveAAOffset(totalOffset); 664 665 Vector2 innerOffset = totalOffset; 666 paintInfo.scaleOffsetForStrokeWidth(innerOffset); 667 Vector2 outerOffset = innerOffset + AAOffset; 668 innerOffset -= AAOffset; 669 670 AlphaVertex::set(&buffer[currentAAOuterIndex++], 671 current->x + outerOffset.x, 672 current->y + outerOffset.y, 673 0.0f); 674 AlphaVertex::set(&buffer[currentAAOuterIndex++], 675 current->x + innerOffset.x, 676 current->y + innerOffset.y, 677 paintInfo.maxAlpha); 678 679 AlphaVertex::set(&buffer[currentStrokeIndex++], 680 current->x + innerOffset.x, 681 current->y + innerOffset.y, 682 paintInfo.maxAlpha); 683 AlphaVertex::set(&buffer[currentStrokeIndex++], 684 current->x - innerOffset.x, 685 current->y - innerOffset.y, 686 paintInfo.maxAlpha); 687 688 AlphaVertex::set(&buffer[currentAAInnerIndex++], 689 current->x - innerOffset.x, 690 current->y - innerOffset.y, 691 paintInfo.maxAlpha); 692 AlphaVertex::set(&buffer[currentAAInnerIndex++], 693 current->x - outerOffset.x, 694 current->y - outerOffset.y, 695 0.0f); 696 697 last = current; 698 current = next; 699 lastNormal = nextNormal; 700 } 701 702 // wrap each strip around to beginning, creating degenerate tris to bridge strips 703 buffer[currentAAOuterIndex++] = buffer[0]; 704 buffer[currentAAOuterIndex++] = buffer[1]; 705 buffer[currentAAOuterIndex++] = buffer[1]; 706 707 buffer[currentStrokeIndex++] = buffer[offset]; 708 buffer[currentStrokeIndex++] = buffer[offset + 1]; 709 buffer[currentStrokeIndex++] = buffer[offset + 1]; 710 711 buffer[currentAAInnerIndex++] = buffer[2 * offset]; 712 buffer[currentAAInnerIndex++] = buffer[2 * offset + 1]; 713 // don't need to create last degenerate tri 714 715 DEBUG_DUMP_ALPHA_BUFFER(); 716} 717 718void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, 719 const mat4& transform, VertexBuffer& vertexBuffer) { 720 ATRACE_CALL(); 721 722 const PaintInfo paintInfo(paint, transform); 723 724 Vector<Vertex> tempVertices; 725 float threshInvScaleX = paintInfo.inverseScaleX; 726 float threshInvScaleY = paintInfo.inverseScaleY; 727 if (paintInfo.style == SkPaint::kStroke_Style) { 728 // alter the bezier recursion threshold values we calculate in order to compensate for 729 // expansion done after the path vertices are found 730 SkRect bounds = path.getBounds(); 731 if (!bounds.isEmpty()) { 732 threshInvScaleX *= bounds.width() / (bounds.width() + paint->getStrokeWidth()); 733 threshInvScaleY *= bounds.height() / (bounds.height() + paint->getStrokeWidth()); 734 } 735 } 736 737 // force close if we're filling the path, since fill path expects closed perimeter. 738 bool forceClose = paintInfo.style != SkPaint::kStroke_Style; 739 bool wasClosed = approximatePathOutlineVertices(path, forceClose, 740 threshInvScaleX * threshInvScaleX, threshInvScaleY * threshInvScaleY, 741 OUTLINE_REFINE_THRESHOLD_SQUARED, tempVertices); 742 743 if (!tempVertices.size()) { 744 // path was empty, return without allocating vertex buffer 745 return; 746 } 747 748#if VERTEX_DEBUG 749 for (unsigned int i = 0; i < tempVertices.size(); i++) { 750 ALOGD("orig path: point at %f %f", 751 tempVertices[i].x, tempVertices[i].y); 752 } 753#endif 754 755 if (paintInfo.style == SkPaint::kStroke_Style) { 756 if (!paintInfo.isAA) { 757 if (wasClosed) { 758 getStrokeVerticesFromPerimeter(paintInfo, tempVertices, vertexBuffer); 759 } else { 760 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer); 761 } 762 763 } else { 764 if (wasClosed) { 765 getStrokeVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer); 766 } else { 767 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer); 768 } 769 } 770 } else { 771 // For kStrokeAndFill style, the path should be adjusted externally. 772 // It will be treated as a fill here. 773 if (!paintInfo.isAA) { 774 getFillVerticesFromPerimeter(tempVertices, vertexBuffer); 775 } else { 776 getFillVerticesFromPerimeterAA(paintInfo, tempVertices, vertexBuffer); 777 } 778 } 779 780 Rect bounds(path.getBounds()); 781 paintInfo.expandBoundsForStroke(&bounds); 782 vertexBuffer.setBounds(bounds); 783} 784 785template <class TYPE> 786static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, 787 const float* points, int count, Rect& bounds) { 788 bounds.set(points[0], points[1], points[0], points[1]); 789 790 int numPoints = count / 2; 791 int verticesPerPoint = srcBuffer.getVertexCount(); 792 dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2); 793 794 for (int i = 0; i < count; i += 2) { 795 bounds.expandToCoverVertex(points[i + 0], points[i + 1]); 796 dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]); 797 } 798 dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint); 799} 800 801void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint, 802 const mat4& transform, VertexBuffer& vertexBuffer) { 803 const PaintInfo paintInfo(paint, transform); 804 805 // determine point shape 806 SkPath path; 807 float radius = paintInfo.halfStrokeWidth; 808 if (radius == 0.0f) radius = 0.25f; 809 810 if (paintInfo.cap == SkPaint::kRound_Cap) { 811 path.addCircle(0, 0, radius); 812 } else { 813 path.addRect(-radius, -radius, radius, radius); 814 } 815 816 // calculate outline 817 Vector<Vertex> outlineVertices; 818 approximatePathOutlineVertices(path, true, 819 paintInfo.inverseScaleX * paintInfo.inverseScaleX, 820 paintInfo.inverseScaleY * paintInfo.inverseScaleY, 821 OUTLINE_REFINE_THRESHOLD_SQUARED, outlineVertices); 822 823 if (!outlineVertices.size()) return; 824 825 Rect bounds; 826 // tessellate, then duplicate outline across points 827 int numPoints = count / 2; 828 VertexBuffer tempBuffer; 829 if (!paintInfo.isAA) { 830 getFillVerticesFromPerimeter(outlineVertices, tempBuffer); 831 instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds); 832 } else { 833 // note: pass maxAlpha directly, since we want fill to be alpha modulated 834 getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha); 835 instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds); 836 } 837 838 // expand bounds from vertex coords to pixel data 839 paintInfo.expandBoundsForStroke(&bounds); 840 vertexBuffer.setBounds(bounds); 841} 842 843void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, 844 const mat4& transform, VertexBuffer& vertexBuffer) { 845 ATRACE_CALL(); 846 const PaintInfo paintInfo(paint, transform); 847 848 const int extra = paintInfo.capExtraDivisions(); 849 int numLines = count / 4; 850 int lineAllocSize; 851 // pre-allocate space for lines in the buffer, and degenerate tris in between 852 if (paintInfo.isAA) { 853 lineAllocSize = 6 * (2) + 2 + 6 * extra; 854 vertexBuffer.alloc<AlphaVertex>(numLines * lineAllocSize + (numLines - 1) * 2); 855 } else { 856 lineAllocSize = 2 * ((2) + extra); 857 vertexBuffer.alloc<Vertex>(numLines * lineAllocSize + (numLines - 1) * 2); 858 } 859 860 Vector<Vertex> tempVertices; 861 tempVertices.push(); 862 tempVertices.push(); 863 Vertex* tempVerticesData = tempVertices.editArray(); 864 Rect bounds; 865 bounds.set(points[0], points[1], points[0], points[1]); 866 for (int i = 0; i < count; i += 4) { 867 Vertex::set(&(tempVerticesData[0]), points[i + 0], points[i + 1]); 868 Vertex::set(&(tempVerticesData[1]), points[i + 2], points[i + 3]); 869 870 if (paintInfo.isAA) { 871 getStrokeVerticesFromUnclosedVerticesAA(paintInfo, tempVertices, vertexBuffer); 872 } else { 873 getStrokeVerticesFromUnclosedVertices(paintInfo, tempVertices, vertexBuffer); 874 } 875 876 // calculate bounds 877 bounds.expandToCoverVertex(tempVerticesData[0].x, tempVerticesData[0].y); 878 bounds.expandToCoverVertex(tempVerticesData[1].x, tempVerticesData[1].y); 879 } 880 881 // since multiple objects tessellated into buffer, separate them with degen tris 882 if (paintInfo.isAA) { 883 vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize); 884 } else { 885 vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize); 886 } 887 888 // expand bounds from vertex coords to pixel data 889 paintInfo.expandBoundsForStroke(&bounds); 890 vertexBuffer.setBounds(bounds); 891} 892 893/////////////////////////////////////////////////////////////////////////////// 894// Simple path line approximation 895/////////////////////////////////////////////////////////////////////////////// 896 897bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, float thresholdSquared, 898 Vector<Vertex>& outputVertices) { 899 return approximatePathOutlineVertices(path, true, 1.0f, 1.0f, thresholdSquared, outputVertices); 900} 901 902void pushToVector(Vector<Vertex>& vertices, float x, float y) { 903 // TODO: make this not yuck 904 vertices.push(); 905 Vertex* newVertex = &(vertices.editArray()[vertices.size() - 1]); 906 Vertex::set(newVertex, x, y); 907} 908 909bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose, 910 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, 911 Vector<Vertex>& outputVertices) { 912 ATRACE_CALL(); 913 914 // TODO: to support joins other than sharp miter, join vertices should be labelled in the 915 // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case. 916 SkPath::Iter iter(path, forceClose); 917 SkPoint pts[4]; 918 SkPath::Verb v; 919 while (SkPath::kDone_Verb != (v = iter.next(pts))) { 920 switch (v) { 921 case SkPath::kMove_Verb: 922 pushToVector(outputVertices, pts[0].x(), pts[0].y()); 923 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y()); 924 break; 925 case SkPath::kClose_Verb: 926 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y()); 927 break; 928 case SkPath::kLine_Verb: 929 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y()); 930 pushToVector(outputVertices, pts[1].x(), pts[1].y()); 931 break; 932 case SkPath::kQuad_Verb: 933 ALOGV("kQuad_Verb"); 934 recursiveQuadraticBezierVertices( 935 pts[0].x(), pts[0].y(), 936 pts[2].x(), pts[2].y(), 937 pts[1].x(), pts[1].y(), 938 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); 939 break; 940 case SkPath::kCubic_Verb: 941 ALOGV("kCubic_Verb"); 942 recursiveCubicBezierVertices( 943 pts[0].x(), pts[0].y(), 944 pts[1].x(), pts[1].y(), 945 pts[3].x(), pts[3].y(), 946 pts[2].x(), pts[2].y(), 947 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices); 948 break; 949 default: 950 break; 951 } 952 } 953 954 int size = outputVertices.size(); 955 if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x && 956 outputVertices[0].y == outputVertices[size - 1].y) { 957 outputVertices.pop(); 958 return true; 959 } 960 return false; 961} 962 963/////////////////////////////////////////////////////////////////////////////// 964// Bezier approximation 965/////////////////////////////////////////////////////////////////////////////// 966 967void PathTessellator::recursiveCubicBezierVertices( 968 float p1x, float p1y, float c1x, float c1y, 969 float p2x, float p2y, float c2x, float c2y, 970 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, 971 Vector<Vertex>& outputVertices, int depth) { 972 float dx = p2x - p1x; 973 float dy = p2y - p1y; 974 float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); 975 float d2 = fabs((c2x - p2x) * dy - (c2y - p2y) * dx); 976 float d = d1 + d2; 977 978 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors 979 if (depth >= MAX_DEPTH 980 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { 981 // below thresh, draw line by adding endpoint 982 pushToVector(outputVertices, p2x, p2y); 983 } else { 984 float p1c1x = (p1x + c1x) * 0.5f; 985 float p1c1y = (p1y + c1y) * 0.5f; 986 float p2c2x = (p2x + c2x) * 0.5f; 987 float p2c2y = (p2y + c2y) * 0.5f; 988 989 float c1c2x = (c1x + c2x) * 0.5f; 990 float c1c2y = (c1y + c2y) * 0.5f; 991 992 float p1c1c2x = (p1c1x + c1c2x) * 0.5f; 993 float p1c1c2y = (p1c1y + c1c2y) * 0.5f; 994 995 float p2c1c2x = (p2c2x + c1c2x) * 0.5f; 996 float p2c1c2y = (p2c2y + c1c2y) * 0.5f; 997 998 float mx = (p1c1c2x + p2c1c2x) * 0.5f; 999 float my = (p1c1c2y + p2c1c2y) * 0.5f; 1000 1001 recursiveCubicBezierVertices( 1002 p1x, p1y, p1c1x, p1c1y, 1003 mx, my, p1c1c2x, p1c1c2y, 1004 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1005 recursiveCubicBezierVertices( 1006 mx, my, p2c1c2x, p2c1c2y, 1007 p2x, p2y, p2c2x, p2c2y, 1008 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1009 } 1010} 1011 1012void PathTessellator::recursiveQuadraticBezierVertices( 1013 float ax, float ay, 1014 float bx, float by, 1015 float cx, float cy, 1016 float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, 1017 Vector<Vertex>& outputVertices, int depth) { 1018 float dx = bx - ax; 1019 float dy = by - ay; 1020 float d = (cx - bx) * dy - (cy - by) * dx; 1021 1022 // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors 1023 if (depth >= MAX_DEPTH 1024 || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { 1025 // below thresh, draw line by adding endpoint 1026 pushToVector(outputVertices, bx, by); 1027 } else { 1028 float acx = (ax + cx) * 0.5f; 1029 float bcx = (bx + cx) * 0.5f; 1030 float acy = (ay + cy) * 0.5f; 1031 float bcy = (by + cy) * 0.5f; 1032 1033 // midpoint 1034 float mx = (acx + bcx) * 0.5f; 1035 float my = (acy + bcy) * 0.5f; 1036 1037 recursiveQuadraticBezierVertices(ax, ay, mx, my, acx, acy, 1038 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1039 recursiveQuadraticBezierVertices(mx, my, bx, by, bcx, bcy, 1040 sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices, depth + 1); 1041 } 1042} 1043 1044}; // namespace uirenderer 1045}; // namespace android 1046