Skip to content

Commit 4babbdb

Browse files
susan101566susan101566
andcommitted
runtime: add tiling to n-slicing
This PR translates the logic for tiling into the n-slicing runtime implementation. It additionally computes patchIndex on editor export. Demo: ![aaaaa](https://github.com/user-attachments/assets/819e437c-f91b-4d83-872c-7e6a0009809d) doc: https://www.notion.so/rive-app/9-Slice-Tech-Proposal-Image-only-50b25ea8e79c4efabb681110e288f064 Diffs= 728ac6286 runtime: add tiling to n-slicing (#7934) Co-authored-by: Susan Wang <[email protected]>
1 parent aa19296 commit 4babbdb

File tree

7 files changed

+145
-10
lines changed

7 files changed

+145
-10
lines changed

.rive_head

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e1b4a53ecb34d62e9d4160e3040803173c2233b0
1+
728ac6286912a1bf9987a91488c698668bd67354

include/rive/layout/n_slicer.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,23 @@
22
#define _RIVE_N_SLICER_HPP_
33
#include "rive/generated/layout/n_slicer_base.hpp"
44
#include <stdio.h>
5+
#include <unordered_map>
6+
57
namespace rive
68
{
79
class Image;
810
class SliceMesh;
911
class Axis;
1012
class NSlicerTileMode;
13+
enum class NSlicerTileModeType : int;
1114

1215
class NSlicer : public NSlicerBase
1316
{
1417
private:
1518
std::unique_ptr<SliceMesh> m_sliceMesh; // the mesh that gets drawn
1619
std::vector<Axis*> m_xs;
1720
std::vector<Axis*> m_ys;
18-
std::vector<NSlicerTileMode*> m_tileModes;
21+
std::unordered_map<int, NSlicerTileModeType> m_tileModes; // patchIndex key
1922

2023
public:
2124
NSlicer();
@@ -25,12 +28,14 @@ class NSlicer : public NSlicerBase
2528

2629
Image* image();
2730
SliceMesh* sliceMesh() { return m_sliceMesh.get(); };
31+
int patchIndex(int patchX, int patchY);
2832
const std::vector<Axis*>& xs() { return m_xs; }
2933
const std::vector<Axis*>& ys() { return m_ys; }
34+
const std::unordered_map<int, NSlicerTileModeType>& tileModes() { return m_tileModes; }
3035

3136
void addAxisX(Axis* axis);
3237
void addAxisY(Axis* axis);
33-
void addTileMode(NSlicerTileMode* tileMode);
38+
void addTileMode(int patchIndex, NSlicerTileModeType style);
3439
void axisChanged(); // only axis gets animated at runtime
3540
};
3641
} // namespace rive

include/rive/layout/n_slicer_tile_mode.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44
#include <stdio.h>
55
namespace rive
66
{
7+
enum class NSlicerTileModeType : int
8+
{
9+
STRETCH = 0,
10+
REPEAT = 1,
11+
HIDDEN = 2,
12+
};
13+
714
class NSlicerTileMode : public NSlicerTileModeBase
815
{
916
public:

include/rive/shapes/slice_mesh.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ class SliceMesh : public MeshDrawable
3535
std::vector<float> uvStops(AxisType forAxis);
3636
std::vector<float> vertexStops(const std::vector<float>& normalizedStops, AxisType forAxis);
3737

38+
uint16_t tileRepeat(std::vector<SliceMeshVertex>& vertices,
39+
std::vector<uint16_t>& indices,
40+
const std::vector<SliceMeshVertex>& box,
41+
uint16_t start);
42+
3843
// Update the member (non-render) buffers.
3944
void calc();
4045

src/layout/n_slicer.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ Image* NSlicer::image()
1515
return parent()->as<Image>();
1616
}
1717
return nullptr;
18-
};
18+
}
19+
20+
int NSlicer::patchIndex(int patchX, int patchY)
21+
{
22+
return patchY * (static_cast<int>(m_xs.size()) + 1) + patchX;
23+
}
1924

2025
StatusCode NSlicer::onAddedDirty(CoreContext* context)
2126
{
@@ -46,7 +51,10 @@ void NSlicer::addAxisY(Axis* axis) { m_ys.push_back(axis); }
4651

4752
void NSlicer::axisChanged() { addDirt(ComponentDirt::Path); }
4853

49-
void NSlicer::addTileMode(NSlicerTileMode* tileMode) { m_tileModes.push_back(tileMode); }
54+
void NSlicer::addTileMode(int patchIndex, NSlicerTileModeType style)
55+
{
56+
m_tileModes[patchIndex] = style;
57+
}
5058

5159
void NSlicer::update(ComponentDirt value)
5260
{

src/layout/n_slicer_tile_mode.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ StatusCode NSlicerTileMode::onAddedDirty(CoreContext* context)
1515
{
1616
return StatusCode::MissingObject;
1717
}
18-
parent()->as<NSlicer>()->addTileMode(this);
18+
parent()->as<NSlicer>()->addTileMode(patchIndex(), NSlicerTileModeType(style()));
1919
return StatusCode::Ok;
2020
}

src/shapes/slice_mesh.cpp

Lines changed: 114 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "rive/factory.hpp"
44
#include "rive/layout/axis.hpp"
55
#include "rive/layout/n_slicer.hpp"
6+
#include "rive/layout/n_slicer_tile_mode.hpp"
67
#include "rive/math/math_types.hpp"
78
#include "rive/shapes/image.hpp"
89
#include "rive/shapes/slice_mesh.hpp"
@@ -180,6 +181,93 @@ std::vector<float> SliceMesh::vertexStops(const std::vector<float>& normalizedSt
180181
return result;
181182
}
182183

184+
uint16_t SliceMesh::tileRepeat(std::vector<SliceMeshVertex>& vertices,
185+
std::vector<uint16_t>& indices,
186+
const std::vector<SliceMeshVertex>& box,
187+
uint16_t start)
188+
{
189+
assert(box.size() == 4);
190+
191+
const float startX = box[0].vertex.x;
192+
const float startY = box[0].vertex.y;
193+
const float endX = box[2].vertex.x;
194+
const float endY = box[2].vertex.y;
195+
196+
const float startU = box[0].uv.x;
197+
const float startV = box[0].uv.y;
198+
const float endU = box[2].uv.x;
199+
const float endV = box[2].uv.y;
200+
201+
// The size of each repeated tile in image space
202+
Image* image = m_nslicer->image();
203+
const float sizeX = image->width() * (endU - startU) / std::abs(image->scaleX());
204+
const float sizeY = image->height() * (endV - startV) / std::abs(image->scaleY());
205+
206+
float curX = startX;
207+
float curY = startY;
208+
int curV = start;
209+
210+
int escape = 1000000; // a million
211+
while (curY < endY && escape > 0)
212+
{
213+
escape--;
214+
float fracY = (curY + sizeY) > endY ? (endY - curY) / sizeY : 1;
215+
curX = startX;
216+
while (curX < endX && escape > 0)
217+
{
218+
escape--;
219+
int v0 = curV;
220+
float fracX = (curX + sizeX) > endX ? (endX - curX) / sizeX : 1;
221+
222+
std::vector<SliceMeshVertex> curTile;
223+
float endU1 = startU + (endU - startU) * fracX;
224+
float endV1 = startV + (endV - startV) * fracY;
225+
float endX1 = curX + sizeX * fracX;
226+
float endY1 = curY + sizeY * fracY;
227+
228+
// top left
229+
SliceMeshVertex v = SliceMeshVertex();
230+
v.id = curV++;
231+
v.uv = Vec2D(startU, startV);
232+
v.vertex = Vec2D(curX, curY);
233+
curTile.emplace_back(v);
234+
235+
// top right
236+
v = SliceMeshVertex();
237+
v.id = curV++;
238+
v.uv = Vec2D(endU1, startV);
239+
v.vertex = Vec2D(endX1, curY);
240+
curTile.emplace_back(v);
241+
242+
// bottom right
243+
v = SliceMeshVertex();
244+
v.id = curV++;
245+
v.uv = Vec2D(endU1, endV1);
246+
v.vertex = Vec2D(endX1, endY1);
247+
curTile.emplace_back(v);
248+
249+
// bottom left
250+
v = SliceMeshVertex();
251+
v.id = curV++;
252+
v.uv = Vec2D(startU, endV1);
253+
v.vertex = Vec2D(curX, endY1);
254+
curTile.emplace_back(v);
255+
256+
// Commit the four vertices, and the triangulation
257+
vertices.insert(vertices.end(), curTile.begin(), curTile.end());
258+
for (uint16_t t : triangulation)
259+
{
260+
indices.emplace_back(v0 + t);
261+
}
262+
263+
curX += sizeX;
264+
}
265+
curY += sizeY;
266+
}
267+
assert(escape > 0);
268+
return curV - start;
269+
}
270+
183271
void SliceMesh::calc()
184272
{
185273
m_vertices = {};
@@ -190,13 +278,24 @@ void SliceMesh::calc()
190278
std::vector<float> vs = uvStops(AxisType::Y);
191279
std::vector<float> xs = vertexStops(us, AxisType::X);
192280
std::vector<float> ys = vertexStops(vs, AxisType::Y);
281+
const auto& tileModes = m_nslicer->tileModes();
193282

194283
std::vector<SliceMeshVertex> vertices;
195284
uint16_t vertexIndex = 0;
196285
for (int patchY = 0; patchY < vs.size() - 1; patchY++)
197286
{
198287
for (int patchX = 0; patchX < us.size() - 1; patchX++)
199288
{
289+
auto tileModeIt = tileModes.find(m_nslicer->patchIndex(patchX, patchY));
290+
auto tileMode =
291+
tileModeIt == tileModes.end() ? NSlicerTileModeType::STRETCH : tileModeIt->second;
292+
293+
// Do nothing if hidden
294+
if (tileMode == NSlicerTileModeType::HIDDEN)
295+
{
296+
continue;
297+
}
298+
200299
const uint16_t v0 = vertexIndex;
201300
std::vector<SliceMeshVertex> patchVertices;
202301
for (const Corner& corner : patchCorners)
@@ -205,16 +304,27 @@ void SliceMesh::calc()
205304
int yIndex = patchY + corner.y;
206305

207306
SliceMeshVertex v;
208-
v.id = vertexIndex++;
307+
if (tileMode != NSlicerTileModeType::REPEAT)
308+
{
309+
v.id = vertexIndex++;
310+
}
209311
v.uv = Vec2D(us[xIndex], vs[yIndex]);
210312
v.vertex = Vec2D(xs[xIndex], ys[yIndex]);
211313

212314
patchVertices.emplace_back(v);
213315
}
214-
vertices.insert(vertices.end(), patchVertices.begin(), patchVertices.end());
215-
for (uint16_t t : triangulation)
316+
317+
if (tileMode == NSlicerTileModeType::REPEAT)
318+
{
319+
vertexIndex += tileRepeat(vertices, m_indices, patchVertices, v0);
320+
}
321+
else
216322
{
217-
m_indices.emplace_back(v0 + t);
323+
vertices.insert(vertices.end(), patchVertices.begin(), patchVertices.end());
324+
for (uint16_t t : triangulation)
325+
{
326+
m_indices.emplace_back(v0 + t);
327+
}
218328
}
219329
}
220330
}

0 commit comments

Comments
 (0)