| /* |
| * Copyright 2022 Rive |
| */ |
| |
| #include "rive/pls/pls.hpp" |
| |
| namespace rive::pls |
| { |
| constexpr static float pack_params(int32_t patchSegmentSpan, int32_t vertexType) |
| { |
| return static_cast<float>((patchSegmentSpan << 2) | vertexType); |
| } |
| |
| static void generate_buffer_data_for_patch_type(PatchType patchType, |
| PatchVertex vertices[], |
| uint16_t indices[], |
| uint16_t baseVertex) |
| { |
| // AA border vertices. "Inner tessellation curves" have one more segment without a fan triangle |
| // whose purpose is to be a bowtie join. |
| size_t vertexCount = 0; |
| size_t patchSegmentSpan = patchType == PatchType::midpointFan ? kMidpointFanPatchSegmentSpan |
| : kOuterCurvePatchSegmentSpan; |
| for (int i = 0; i < patchSegmentSpan; ++i) |
| { |
| float params = pack_params(patchSegmentSpan, flags::kStrokeVertex); |
| float l = static_cast<float>(i); |
| float r = l + 1; |
| if (patchType == PatchType::outerCurves) |
| { |
| vertices[vertexCount + 0].set(l, 0.f, .5f, params); |
| vertices[vertexCount + 1].set(l, 1.f, .0f, params); |
| vertices[vertexCount + 2].set(r, 0.f, .5f, params); |
| vertices[vertexCount + 3].set(r, 1.f, .0f, params); |
| |
| // Give the vertex an alternate position when mirrored so the border has the same |
| // diagonals whether morrored or not. |
| vertices[vertexCount + 0].setMirroredPosition(r, 0.f, .5f); |
| vertices[vertexCount + 1].setMirroredPosition(l, 0.f, .5f); |
| vertices[vertexCount + 2].setMirroredPosition(r, 1.f, .0f); |
| vertices[vertexCount + 3].setMirroredPosition(l, 1.f, .0f); |
| } |
| else |
| { |
| assert(patchType == PatchType::midpointFan); |
| vertices[vertexCount + 0].set(l, -1.f, 1.f, params); |
| vertices[vertexCount + 1].set(l, +1.f, 0.f, params); |
| vertices[vertexCount + 2].set(r, -1.f, 1.f, params); |
| vertices[vertexCount + 3].set(r, +1.f, 0.f, params); |
| |
| // Give the vertex an alternate position when mirrored so the border has the same |
| // diagonals whether morrored or not. |
| vertices[vertexCount + 0].setMirroredPosition(r - 1.f, -1.f, 1.f); |
| vertices[vertexCount + 1].setMirroredPosition(l - 1.f, -1.f, 1.f); |
| vertices[vertexCount + 2].setMirroredPosition(r - 1.f, +1.f, 0.f); |
| vertices[vertexCount + 3].setMirroredPosition(l - 1.f, +1.f, 0.f); |
| } |
| vertexCount += 4; |
| } |
| |
| // Bottom (negative coverage) side of the AA border. |
| if (patchType == PatchType::outerCurves) |
| { |
| float params = pack_params(patchSegmentSpan, flags::kStrokeVertex); |
| for (int i = 0; i < patchSegmentSpan; ++i) |
| { |
| float l = static_cast<float>(i); |
| float r = l + 1; |
| |
| vertices[vertexCount + 0].set(l, -.0f, .5f, params); |
| vertices[vertexCount + 1].set(r, -.0f, .5f, params); |
| vertices[vertexCount + 2].set(l, -1.f, .0f, params); |
| vertices[vertexCount + 3].set(r, -1.f, .0f, params); |
| |
| // Give the vertex an alternate position when mirrored so the border has the same |
| // diagonals whether morrored or not. |
| vertices[vertexCount + 0].setMirroredPosition(r, -0.f, .5f); |
| vertices[vertexCount + 1].setMirroredPosition(r, -1.f, .0f); |
| vertices[vertexCount + 2].setMirroredPosition(l, -0.f, .5f); |
| vertices[vertexCount + 3].setMirroredPosition(l, -1.f, .0f); |
| |
| vertexCount += 4; |
| } |
| } |
| |
| // Triangle fan vertices. (These only touch the first "fanSegmentSpan" segments on inner |
| // tessellation curves. |
| size_t fanVerticesIdx = vertexCount; |
| size_t fanSegmentSpan = |
| patchType == PatchType::midpointFan ? patchSegmentSpan : patchSegmentSpan - 1; |
| assert((fanSegmentSpan & (fanSegmentSpan - 1)) == 0); // The fan must be a power of two. |
| for (int i = 0; i <= fanSegmentSpan; ++i) |
| { |
| float params = pack_params(patchSegmentSpan, flags::kFanVertex); |
| if (patchType == PatchType::outerCurves) |
| { |
| vertices[vertexCount].set(static_cast<float>(i), 0.f, 1, params); |
| } |
| else |
| { |
| vertices[vertexCount].set(static_cast<float>(i), -1.f, 1, params); |
| vertices[vertexCount].setMirroredPosition(static_cast<float>(i) - 1, -1.f, 1); |
| } |
| ++vertexCount; |
| } |
| |
| // The midpoint vertex is only included on midpoint fan patches. |
| size_t midpointIdx = vertexCount; |
| if (patchType == PatchType::midpointFan) |
| { |
| vertices[vertexCount++].set(0, |
| 0, |
| 1, |
| pack_params(patchSegmentSpan, flags::kFanMidpointVertex)); |
| } |
| assert(vertexCount == (patchType == PatchType::outerCurves ? kOuterCurvePatchVertexCount |
| : kMidpointFanPatchVertexCount)); |
| |
| // AA border indices. |
| constexpr static size_t kBorderPatternVertexCount = 4; |
| constexpr static size_t kBorderPatternIndexCount = 6; |
| constexpr static uint16_t kBorderPattern[kBorderPatternIndexCount] = {0, 1, 2, 2, 1, 3}; |
| constexpr static uint16_t kNegativeBorderPattern[kBorderPatternIndexCount] = {0, 2, 1, 1, 2, 3}; |
| |
| size_t indexCount = 0; |
| size_t borderEdgeVerticesIdx = 0; |
| for (size_t borderSegmentIdx = 0; borderSegmentIdx < patchSegmentSpan; ++borderSegmentIdx) |
| { |
| for (size_t i = 0; i < kBorderPatternIndexCount; ++i) |
| { |
| indices[indexCount++] = baseVertex + borderEdgeVerticesIdx + kBorderPattern[i]; |
| } |
| borderEdgeVerticesIdx += kBorderPatternVertexCount; |
| } |
| |
| // Bottom (negative coverage) side of the AA border. |
| if (patchType == PatchType::outerCurves) |
| { |
| for (size_t borderSegmentIdx = 0; borderSegmentIdx < patchSegmentSpan; ++borderSegmentIdx) |
| { |
| for (size_t i = 0; i < kBorderPatternIndexCount; ++i) |
| { |
| indices[indexCount++] = |
| baseVertex + borderEdgeVerticesIdx + kNegativeBorderPattern[i]; |
| } |
| borderEdgeVerticesIdx += kBorderPatternVertexCount; |
| } |
| } |
| |
| assert(borderEdgeVerticesIdx == fanVerticesIdx); |
| |
| // Triangle fan indices, in a middle-out topology. |
| // Don't include the final bowtie join if this is an "outerStroke" patch. (i.e., use |
| // fanSegmentSpan and not "patchSegmentSpan".) |
| for (int step = 1; step < fanSegmentSpan; step <<= 1) |
| { |
| for (int i = 0; i < fanSegmentSpan; i += step * 2) |
| { |
| indices[indexCount++] = fanVerticesIdx + i + baseVertex; |
| indices[indexCount++] = fanVerticesIdx + i + step + baseVertex; |
| indices[indexCount++] = fanVerticesIdx + i + step * 2 + baseVertex; |
| } |
| } |
| if (patchType == PatchType::midpointFan) |
| { |
| // Triangle to the contour midpoint. |
| indices[indexCount++] = fanVerticesIdx + baseVertex; |
| indices[indexCount++] = fanVerticesIdx + fanSegmentSpan + baseVertex; |
| indices[indexCount++] = midpointIdx + baseVertex; |
| assert(indexCount == kMidpointFanPatchIndexCount); |
| } |
| else |
| { |
| assert(patchType == PatchType::outerCurves); |
| assert(indexCount == kOuterCurvePatchIndexCount); |
| } |
| } |
| |
| void GeneratePatchBufferData(PatchVertex vertices[kPatchVertexBufferCount], |
| uint16_t indices[kPatchIndexBufferCount]) |
| { |
| generate_buffer_data_for_patch_type(PatchType::midpointFan, vertices, indices, 0); |
| generate_buffer_data_for_patch_type(PatchType::outerCurves, |
| vertices + kMidpointFanPatchVertexCount, |
| indices + kMidpointFanPatchIndexCount, |
| kMidpointFanPatchVertexCount); |
| } |
| |
| float FindTransformedArea(const AABB& bounds, const Mat2D& matrix) |
| { |
| Vec2D pts[4] = {{bounds.left(), bounds.top()}, |
| {bounds.right(), bounds.top()}, |
| {bounds.right(), bounds.bottom()}, |
| {bounds.left(), bounds.bottom()}}; |
| Vec2D screenSpacePts[4]; |
| matrix.mapPoints(screenSpacePts, pts, 4); |
| Vec2D v[3] = {screenSpacePts[1] - screenSpacePts[0], |
| screenSpacePts[2] - screenSpacePts[0], |
| screenSpacePts[3] - screenSpacePts[0]}; |
| return (fabsf(Vec2D::cross(v[0], v[1])) + fabsf(Vec2D::cross(v[1], v[2]))) * .5f; |
| } |
| } // namespace rive::pls |