Skip to content

Commit

Permalink
runtime: add tiling to n-slicing
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
susan101566 and susan101566 committed Aug 26, 2024
1 parent aa19296 commit 4babbdb
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 10 deletions.
2 changes: 1 addition & 1 deletion .rive_head
Original file line number Diff line number Diff line change
@@ -1 +1 @@
e1b4a53ecb34d62e9d4160e3040803173c2233b0
728ac6286912a1bf9987a91488c698668bd67354
9 changes: 7 additions & 2 deletions include/rive/layout/n_slicer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,23 @@
#define _RIVE_N_SLICER_HPP_
#include "rive/generated/layout/n_slicer_base.hpp"
#include <stdio.h>
#include <unordered_map>

namespace rive
{
class Image;
class SliceMesh;
class Axis;
class NSlicerTileMode;
enum class NSlicerTileModeType : int;

class NSlicer : public NSlicerBase
{
private:
std::unique_ptr<SliceMesh> m_sliceMesh; // the mesh that gets drawn
std::vector<Axis*> m_xs;
std::vector<Axis*> m_ys;
std::vector<NSlicerTileMode*> m_tileModes;
std::unordered_map<int, NSlicerTileModeType> m_tileModes; // patchIndex key

public:
NSlicer();
Expand All @@ -25,12 +28,14 @@ class NSlicer : public NSlicerBase

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

void addAxisX(Axis* axis);
void addAxisY(Axis* axis);
void addTileMode(NSlicerTileMode* tileMode);
void addTileMode(int patchIndex, NSlicerTileModeType style);
void axisChanged(); // only axis gets animated at runtime
};
} // namespace rive
Expand Down
7 changes: 7 additions & 0 deletions include/rive/layout/n_slicer_tile_mode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
#include <stdio.h>
namespace rive
{
enum class NSlicerTileModeType : int
{
STRETCH = 0,
REPEAT = 1,
HIDDEN = 2,
};

class NSlicerTileMode : public NSlicerTileModeBase
{
public:
Expand Down
5 changes: 5 additions & 0 deletions include/rive/shapes/slice_mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ class SliceMesh : public MeshDrawable
std::vector<float> uvStops(AxisType forAxis);
std::vector<float> vertexStops(const std::vector<float>& normalizedStops, AxisType forAxis);

uint16_t tileRepeat(std::vector<SliceMeshVertex>& vertices,
std::vector<uint16_t>& indices,
const std::vector<SliceMeshVertex>& box,
uint16_t start);

// Update the member (non-render) buffers.
void calc();

Expand Down
12 changes: 10 additions & 2 deletions src/layout/n_slicer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ Image* NSlicer::image()
return parent()->as<Image>();
}
return nullptr;
};
}

int NSlicer::patchIndex(int patchX, int patchY)
{
return patchY * (static_cast<int>(m_xs.size()) + 1) + patchX;
}

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

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

void NSlicer::addTileMode(NSlicerTileMode* tileMode) { m_tileModes.push_back(tileMode); }
void NSlicer::addTileMode(int patchIndex, NSlicerTileModeType style)
{
m_tileModes[patchIndex] = style;
}

void NSlicer::update(ComponentDirt value)
{
Expand Down
2 changes: 1 addition & 1 deletion src/layout/n_slicer_tile_mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ StatusCode NSlicerTileMode::onAddedDirty(CoreContext* context)
{
return StatusCode::MissingObject;
}
parent()->as<NSlicer>()->addTileMode(this);
parent()->as<NSlicer>()->addTileMode(patchIndex(), NSlicerTileModeType(style()));
return StatusCode::Ok;
}
118 changes: 114 additions & 4 deletions src/shapes/slice_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "rive/factory.hpp"
#include "rive/layout/axis.hpp"
#include "rive/layout/n_slicer.hpp"
#include "rive/layout/n_slicer_tile_mode.hpp"
#include "rive/math/math_types.hpp"
#include "rive/shapes/image.hpp"
#include "rive/shapes/slice_mesh.hpp"
Expand Down Expand Up @@ -180,6 +181,93 @@ std::vector<float> SliceMesh::vertexStops(const std::vector<float>& normalizedSt
return result;
}

uint16_t SliceMesh::tileRepeat(std::vector<SliceMeshVertex>& vertices,
std::vector<uint16_t>& indices,
const std::vector<SliceMeshVertex>& box,
uint16_t start)
{
assert(box.size() == 4);

const float startX = box[0].vertex.x;
const float startY = box[0].vertex.y;
const float endX = box[2].vertex.x;
const float endY = box[2].vertex.y;

const float startU = box[0].uv.x;
const float startV = box[0].uv.y;
const float endU = box[2].uv.x;
const float endV = box[2].uv.y;

// The size of each repeated tile in image space
Image* image = m_nslicer->image();
const float sizeX = image->width() * (endU - startU) / std::abs(image->scaleX());
const float sizeY = image->height() * (endV - startV) / std::abs(image->scaleY());

float curX = startX;
float curY = startY;
int curV = start;

int escape = 1000000; // a million
while (curY < endY && escape > 0)
{
escape--;
float fracY = (curY + sizeY) > endY ? (endY - curY) / sizeY : 1;
curX = startX;
while (curX < endX && escape > 0)
{
escape--;
int v0 = curV;
float fracX = (curX + sizeX) > endX ? (endX - curX) / sizeX : 1;

std::vector<SliceMeshVertex> curTile;
float endU1 = startU + (endU - startU) * fracX;
float endV1 = startV + (endV - startV) * fracY;
float endX1 = curX + sizeX * fracX;
float endY1 = curY + sizeY * fracY;

// top left
SliceMeshVertex v = SliceMeshVertex();
v.id = curV++;
v.uv = Vec2D(startU, startV);
v.vertex = Vec2D(curX, curY);
curTile.emplace_back(v);

// top right
v = SliceMeshVertex();
v.id = curV++;
v.uv = Vec2D(endU1, startV);
v.vertex = Vec2D(endX1, curY);
curTile.emplace_back(v);

// bottom right
v = SliceMeshVertex();
v.id = curV++;
v.uv = Vec2D(endU1, endV1);
v.vertex = Vec2D(endX1, endY1);
curTile.emplace_back(v);

// bottom left
v = SliceMeshVertex();
v.id = curV++;
v.uv = Vec2D(startU, endV1);
v.vertex = Vec2D(curX, endY1);
curTile.emplace_back(v);

// Commit the four vertices, and the triangulation
vertices.insert(vertices.end(), curTile.begin(), curTile.end());
for (uint16_t t : triangulation)
{
indices.emplace_back(v0 + t);
}

curX += sizeX;
}
curY += sizeY;
}
assert(escape > 0);
return curV - start;
}

void SliceMesh::calc()
{
m_vertices = {};
Expand All @@ -190,13 +278,24 @@ void SliceMesh::calc()
std::vector<float> vs = uvStops(AxisType::Y);
std::vector<float> xs = vertexStops(us, AxisType::X);
std::vector<float> ys = vertexStops(vs, AxisType::Y);
const auto& tileModes = m_nslicer->tileModes();

std::vector<SliceMeshVertex> vertices;
uint16_t vertexIndex = 0;
for (int patchY = 0; patchY < vs.size() - 1; patchY++)
{
for (int patchX = 0; patchX < us.size() - 1; patchX++)
{
auto tileModeIt = tileModes.find(m_nslicer->patchIndex(patchX, patchY));
auto tileMode =
tileModeIt == tileModes.end() ? NSlicerTileModeType::STRETCH : tileModeIt->second;

// Do nothing if hidden
if (tileMode == NSlicerTileModeType::HIDDEN)
{
continue;
}

const uint16_t v0 = vertexIndex;
std::vector<SliceMeshVertex> patchVertices;
for (const Corner& corner : patchCorners)
Expand All @@ -205,16 +304,27 @@ void SliceMesh::calc()
int yIndex = patchY + corner.y;

SliceMeshVertex v;
v.id = vertexIndex++;
if (tileMode != NSlicerTileModeType::REPEAT)
{
v.id = vertexIndex++;
}
v.uv = Vec2D(us[xIndex], vs[yIndex]);
v.vertex = Vec2D(xs[xIndex], ys[yIndex]);

patchVertices.emplace_back(v);
}
vertices.insert(vertices.end(), patchVertices.begin(), patchVertices.end());
for (uint16_t t : triangulation)

if (tileMode == NSlicerTileModeType::REPEAT)
{
vertexIndex += tileRepeat(vertices, m_indices, patchVertices, v0);
}
else
{
m_indices.emplace_back(v0 + t);
vertices.insert(vertices.end(), patchVertices.begin(), patchVertices.end());
for (uint16_t t : triangulation)
{
m_indices.emplace_back(v0 + t);
}
}
}
}
Expand Down

0 comments on commit 4babbdb

Please sign in to comment.