common: spec out TexMap

Spec out this incomplete experimental feature,
this is a still promising one, we will reintroduce
this officially after 1.0 release

size: -2kb

issue: https://github.com/thorvg/thorvg/issues/1372
This commit is contained in:
Hermet Park 2024-08-14 17:23:48 +09:00
parent affe6718b5
commit 0f17f6c69a
26 changed files with 78 additions and 617 deletions

View file

@ -1,102 +0,0 @@
/*
* Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "Example.h"
/************************************************************************/
/* ThorVG Drawing Contents */
/************************************************************************/
struct UserExample : tvgexam::Example
{
bool content(tvg::Canvas* canvas, uint32_t w, uint32_t h) override
{
if (!canvas) return false;
//Background
auto shape = tvg::Shape::gen();
shape->appendRect(0, 0, w, h);
shape->fill(255, 255, 255);
canvas->push(std::move(shape));
//Raw Image
string path(EXAMPLE_DIR"/image/rawimage_200x300.raw");
ifstream file(path, ios::binary);
if (!file.is_open()) return false;
auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300));
file.read(reinterpret_cast<char *>(data), sizeof (uint32_t) * 200 * 300);
file.close();
//Picture
auto picture = tvg::Picture::gen();
if (!tvgexam::verify(picture->load(data, 200, 300, true))) return false;
//Composing Meshes
tvg::Polygon triangles[4];
triangles[0].vertex[0] = {{100, 125}, {0, 0}};
triangles[0].vertex[1] = {{300, 100}, {0.5, 0}};
triangles[0].vertex[2] = {{200, 550}, {0, 1}};
triangles[1].vertex[0] = {{300, 100}, {0.5, 0}};
triangles[1].vertex[1] = {{350, 450}, {0.5, 1}};
triangles[1].vertex[2] = {{200, 550}, {0, 1}};
triangles[2].vertex[0] = {{300, 100}, {0.5, 0}};
triangles[2].vertex[1] = {{500, 200}, {1, 0}};
triangles[2].vertex[2] = {{350, 450}, {0.5, 1}};
triangles[3].vertex[0] = {{500, 200}, {1, 0}};
triangles[3].vertex[1] = {{450, 450}, {1, 1}};
triangles[3].vertex[2] = {{350, 450}, {0.5, 1}};
if (!tvgexam::verify(picture->mesh(triangles, 4))) return false;
//Masking + Opacity
auto picture2 = tvg::cast<tvg::Picture>(picture->duplicate());
picture2->translate(400, 400);
picture2->opacity(200);
auto mask = tvg::Shape::gen();
mask->appendCircle(700, 700, 200, 200);
mask->fill(255, 255, 255);
picture2->composite(std::move(mask), tvg::CompositeMethod::AlphaMask);
canvas->push(std::move(picture));
canvas->push(std::move(picture2));
free(data);
return true;
}
};
/************************************************************************/
/* Entry Point */
/************************************************************************/
int main(int argc, char **argv)
{
return tvgexam::main(new UserExample, argc, argv, 1024, 1024);
}

View file

@ -28,35 +28,6 @@ using namespace std;
/* ThorVG Saving Contents */
/************************************************************************/
unique_ptr<tvg::Paint> tvgTexmap(uint32_t * data, int width, int height)
{
auto texmap = tvg::Picture::gen();
if (!tvgexam::verify(texmap->load(data, width, height, true))) return nullptr;
texmap->translate(100, 100);
//Composing Meshes
tvg::Polygon triangles[4];
triangles[0].vertex[0] = {{100, 125}, {0, 0}};
triangles[0].vertex[1] = {{300, 100}, {0.5, 0}};
triangles[0].vertex[2] = {{200, 550}, {0, 1}};
triangles[1].vertex[0] = {{300, 100}, {0.5, 0}};
triangles[1].vertex[1] = {{350, 450}, {0.5, 1}};
triangles[1].vertex[2] = {{200, 550}, {0, 1}};
triangles[2].vertex[0] = {{300, 100}, {0.5, 0}};
triangles[2].vertex[1] = {{500, 200}, {1, 0}};
triangles[2].vertex[2] = {{350, 450}, {0.5, 1}};
triangles[3].vertex[0] = {{500, 200}, {1, 0}};
triangles[3].vertex[1] = {{450, 450}, {1, 1}};
triangles[3].vertex[2] = {{350, 450}, {0.5, 1}};
texmap->mesh(triangles, 4);
return texmap;
}
unique_ptr<tvg::Paint> tvgClippedImage(uint32_t * data, int width, int height)
{
auto image = tvg::Picture::gen();
@ -193,10 +164,6 @@ void exportTvg()
file.read(reinterpret_cast<char*>(data), sizeof(uint32_t) * width * height);
file.close();
//texmap image
auto texmap = tvgTexmap(data, width, height);
scene->push(std::move(texmap));
//clipped image
auto image = tvgClippedImage(data, width, height);
scene->push(std::move(image));

View file

@ -65,7 +65,6 @@ source_file = [
'StrokeMiterlimit.cpp',
'StrokeTrim.cpp',
'Svg.cpp',
'Texmap.cpp',
'Text.cpp',
'Transform.cpp',
'Tvg.cpp',

View file

@ -256,34 +256,6 @@ struct Matrix
};
/**
* @brief A data structure representing a texture mesh vertex
*
* @param pt The vertex coordinate
* @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0)
*
* @note Experimental API
*/
struct Vertex
{
Point pt;
Point uv;
};
/**
* @brief A data structure representing a triangle in a texture mesh
*
* @param vertex The three vertices that make up the polygon
*
* @note Experimental API
*/
struct Polygon
{
Vertex vertex[3];
};
/**
* @class Paint
*
@ -1359,41 +1331,6 @@ public:
*/
const Paint* paint(uint32_t id) noexcept;
/**
* @brief Sets or removes the triangle mesh to deform the image.
*
* If a mesh is provided, the transform property of the Picture will apply to the triangle mesh, and the
* image data will be used as the texture.
*
* If @p triangles is @c nullptr, or @p triangleCnt is 0, the mesh will be removed.
*
* Only raster image types are supported at this time (png, jpg). Vector types like svg and tvg do not support.
* mesh deformation. However, if required you should be able to render a vector image to a raster image and then apply a mesh.
*
* @param[in] triangles An array of Polygons(triangles) that make up the mesh, or null to remove the mesh.
* @param[in] triangleCnt The number of Polygons(triangles) provided, or 0 to remove the mesh.
*
* @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect.
* @warning Please do not use it, this API is not official one. It could be modified in the next version.
*
* @note Experimental API
*/
Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept;
/**
* @brief Return the number of triangles in the mesh, and optionally get a pointer to the array of triangles in the mesh.
*
* @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh.
*
* @return The number of polygons in the array.
*
* @note Modifying the triangles returned by this method will modify them directly within the mesh.
* @warning Please do not use it, this API is not official one. It could be modified in the next version.
*
* @note Experimental API
*/
uint32_t mesh(const Polygon** triangles) const noexcept;
/**
* @brief Creates a new Picture object.
*

View file

@ -418,21 +418,6 @@ static bool _parsePicture(TvgBinBlock block, Paint* paint)
return true;
}
case TVG_TAG_PICTURE_MESH: {
if (block.length < 1 * SIZE(uint32_t)) return false;
auto ptr = block.data;
uint32_t meshCnt;
READ_UI32(&meshCnt, ptr);
ptr += SIZE(uint32_t);
auto size = meshCnt * SIZE(Polygon);
if (block.length != SIZE(uint32_t) + size) return false;
picture->mesh((Polygon*) ptr, meshCnt);
return true;
}
//Base Paint Properties
default: {
if (_parsePaintProperty(block, picture)) return true;

View file

@ -58,115 +58,57 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
return true;
}
bool GlGeometry::tesselate(const Surface* image, const RenderMesh* mesh, RenderUpdateFlag flag)
bool GlGeometry::tesselate(const Surface* image, RenderUpdateFlag flag)
{
if (flag & RenderUpdateFlag::Image) {
fillVertex.clear();
fillIndex.clear();
if (mesh && mesh->triangleCnt) {
fillVertex.reserve(mesh->triangleCnt * 3 * 5);
fillIndex.reserve(mesh->triangleCnt * 3);
fillVertex.reserve(5 * 4);
fillIndex.reserve(6);
uint32_t index = 0;
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
fillVertex.push(mesh->triangles[i].vertex[0].pt.x);
fillVertex.push(mesh->triangles[i].vertex[0].pt.y);
float left = 0.f;
float top = 0.f;
float right = image->w;
float bottom = image->h;
fillVertex.push(mesh->triangles[i].vertex[0].uv.x);
fillVertex.push(mesh->triangles[i].vertex[0].uv.y);
// left top point
fillVertex.push(left);
fillVertex.push(top);
fillVertex.push(mesh->triangles[i].vertex[1].pt.x);
fillVertex.push(mesh->triangles[i].vertex[1].pt.y);
fillVertex.push(0.f);
fillVertex.push(1.f);
// left bottom point
fillVertex.push(left);
fillVertex.push(bottom);
fillVertex.push(mesh->triangles[i].vertex[1].uv.x);
fillVertex.push(mesh->triangles[i].vertex[1].uv.y);
fillVertex.push(0.f);
fillVertex.push(0.f);
// right top point
fillVertex.push(right);
fillVertex.push(top);
fillVertex.push(mesh->triangles[i].vertex[2].pt.x);
fillVertex.push(mesh->triangles[i].vertex[2].pt.y);
fillVertex.push(1.f);
fillVertex.push(1.f);
// right bottom point
fillVertex.push(right);
fillVertex.push(bottom);
fillVertex.push(mesh->triangles[i].vertex[2].uv.x);
fillVertex.push(mesh->triangles[i].vertex[2].uv.y);
fillVertex.push(1.f);
fillVertex.push(0.f);
fillIndex.push(index);
fillIndex.push(index + 1);
fillIndex.push(index + 2);
index += 3;
}
fillIndex.push(0);
fillIndex.push(1);
fillIndex.push(2);
float left = mesh->triangles[0].vertex[0].pt.x;
float top = mesh->triangles[0].vertex[0].pt.y;
float right = mesh->triangles[0].vertex[0].pt.x;
float bottom = mesh->triangles[0].vertex[0].pt.y;
fillIndex.push(2);
fillIndex.push(1);
fillIndex.push(3);
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
left = min(left, mesh->triangles[i].vertex[0].pt.x);
left = min(left, mesh->triangles[i].vertex[1].pt.x);
left = min(left, mesh->triangles[i].vertex[2].pt.x);
top = min(top, mesh->triangles[i].vertex[0].pt.y);
top = min(top, mesh->triangles[i].vertex[1].pt.y);
top = min(top, mesh->triangles[i].vertex[2].pt.y);
right = max(right, mesh->triangles[i].vertex[0].pt.x);
right = max(right, mesh->triangles[i].vertex[1].pt.x);
right = max(right, mesh->triangles[i].vertex[2].pt.x);
bottom = max(bottom, mesh->triangles[i].vertex[0].pt.y);
bottom = max(bottom, mesh->triangles[i].vertex[1].pt.y);
bottom = max(bottom, mesh->triangles[i].vertex[2].pt.y);
}
mBounds.x = static_cast<int32_t>(left);
mBounds.y = static_cast<int32_t>(top);
mBounds.w = static_cast<int32_t>(right - left);
mBounds.h = static_cast<int32_t>(bottom - top);
} else {
fillVertex.reserve(5 * 4);
fillIndex.reserve(6);
float left = 0.f;
float top = 0.f;
float right = image->w;
float bottom = image->h;
// left top point
fillVertex.push(left);
fillVertex.push(top);
fillVertex.push(0.f);
fillVertex.push(1.f);
// left bottom point
fillVertex.push(left);
fillVertex.push(bottom);
fillVertex.push(0.f);
fillVertex.push(0.f);
// right top point
fillVertex.push(right);
fillVertex.push(top);
fillVertex.push(1.f);
fillVertex.push(1.f);
// right bottom point
fillVertex.push(right);
fillVertex.push(bottom);
fillVertex.push(1.f);
fillVertex.push(0.f);
fillIndex.push(0);
fillIndex.push(1);
fillIndex.push(2);
fillIndex.push(2);
fillIndex.push(1);
fillIndex.push(3);
mBounds.x = 0;
mBounds.y = 0;
mBounds.w = image->w;
mBounds.h = image->h;
}
mBounds.x = 0;
mBounds.y = 0;
mBounds.w = image->w;
mBounds.h = image->h;
}
return true;

View file

@ -190,7 +190,7 @@ public:
~GlGeometry();
bool tesselate(const RenderShape& rshape, RenderUpdateFlag flag);
bool tesselate(const Surface* image, const RenderMesh* mesh, RenderUpdateFlag flag);
bool tesselate(const Surface* image, RenderUpdateFlag flag);
void disableVertex(uint32_t location);
bool draw(GlRenderTask* task, GlStageBuffer* gpuBuffer, RenderUpdateFlag flag);
void updateTransform(const Matrix& m);

View file

@ -1099,7 +1099,7 @@ static GLuint _genTexture(Surface* image)
}
RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
RenderData GlRenderer::prepare(Surface* image, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{
if (flags == RenderUpdateFlag::None) return data;
@ -1115,14 +1115,14 @@ RenderData GlRenderer::prepare(Surface* image, const RenderMesh* mesh, RenderDat
sdata->texId = _genTexture(image);
sdata->opacity = opacity;
sdata->texColorSpace = image->cs;
sdata->texFlipY = (mesh && mesh->triangleCnt) ? 0 : 1;
sdata->texFlipY = 1;
sdata->geometry = make_unique<GlGeometry>();
}
sdata->geometry->updateTransform(transform);
sdata->geometry->setViewport(mViewport);
sdata->geometry->tesselate(image, mesh, flags);
sdata->geometry->tesselate(image, flags);
if (!clips.empty()) sdata->clips.push(clips);

View file

@ -54,7 +54,7 @@ public:
};
RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override;

View file

@ -519,7 +519,7 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke);
bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image);
@ -564,7 +564,7 @@ void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);

View file

@ -34,7 +34,7 @@ static inline bool _onlyShifted(const Matrix& m)
}
static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix& transform, SwMpool* mpool, unsigned tid)
static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool, unsigned tid)
{
image->outline = mpoolReqOutline(mpool, tid);
auto outline = image->outline;
@ -45,48 +45,12 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix& tr
outline->closed.reserve(1);
Point to[4];
if (mesh->triangleCnt > 0) {
// TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple
// places. We should be able to re-use one we have already done? Also see:
// tvgPicture.h --> bounds
// tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh
//
// TODO: Should we calculate the exact path(s) of the triangle mesh instead?
// i.e. copy tvgSwShape.capp -> _genOutline?
//
// TODO: Cntrs?
auto triangles = mesh->triangles;
auto min = triangles[0].vertex[0].pt;
auto max = triangles[0].vertex[0].pt;
for (uint32_t i = 0; i < mesh->triangleCnt; ++i) {
if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
}
to[0] = {min.x, min.y};
to[1] = {max.x, min.y};
to[2] = {max.x, max.y};
to[3] = {min.x, max.y};
} else {
auto w = static_cast<float>(image->w);
auto h = static_cast<float>(image->h);
to[0] = {0, 0};
to[1] = {w, 0};
to[2] = {w, h};
to[3] = {0, h};
}
auto w = static_cast<float>(image->w);
auto h = static_cast<float>(image->h);
to[0] = {0, 0};
to[1] = {w, 0};
to[2] = {w, h};
to[3] = {0, h};
for (int i = 0; i < 4; i++) {
outline->pts.push(mathTransform(&to[i], transform));
@ -108,7 +72,7 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix& tr
/* External Class Implementation */
/************************************************************************/
bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
{
image->direct = _onlyShifted(transform);
@ -126,7 +90,7 @@ bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix& transfor
else image->scaled = false;
}
if (!_genOutline(image, mesh, transform, mpool, tid)) return false;
if (!_genOutline(image, transform, mpool, tid)) return false;
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
}

View file

@ -1987,13 +1987,12 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
}
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
{
//Outside of the viewport, skip the rendering
if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return true;
if (mesh && mesh->triangleCnt > 0) return _rasterTexmapPolygonMesh(surface, image, mesh, transform, &bbox, opacity);
else return _rasterImage(surface, image, transform, bbox, opacity);
return _rasterImage(surface, image, transform, bbox, opacity);
}

View file

@ -1135,68 +1135,3 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
#endif
return _apply(surface, aaSpans);
}
/*
Provide any number of triangles to draw a mesh using the supplied image.
Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one.
Example:
0 -- 1 0 -- 1 0
| / | --> | / / |
| / | | / / |
2 -- 3 2 1 -- 2
Should provide two Polygons, one for each triangle.
// TODO: region?
*/
static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix& transform, const SwBBox* region, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
return false;
}
//Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
// Step polygons once to transform
auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt);
float ys = FLT_MAX, ye = -1.0f;
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
transformedTris[i] = mesh->triangles[i];
transformedTris[i].vertex[0].pt *= transform;
transformedTris[i].vertex[1].pt *= transform;
transformedTris[i].vertex[2].pt *= transform;
if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y;
else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y;
if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y;
else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y;
// Convert normalized UV coordinates to image coordinates
transformedTris[i].vertex[0].uv.x *= (float)image->w;
transformedTris[i].vertex[0].uv.y *= (float)image->h;
transformedTris[i].vertex[1].uv.x *= (float)image->w;
transformedTris[i].vertex[1].uv.y *= (float)image->h;
transformedTris[i].vertex[2].uv.x *= (float)image->w;
transformedTris[i].vertex[2].uv.y *= (float)image->h;
}
// Get AA spans and step polygons again to draw
if (auto aaSpans = _AASpans(ys, ye, image, region)) {
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
_rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
}
#if 0
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
#endif
_apply(surface, aaSpans);
}
free(transformedTris);
return true;
}

View file

@ -203,7 +203,6 @@ struct SwImageTask : SwTask
{
SwImage image;
Surface* source; //Image source
const RenderMesh* mesh = nullptr; //Should be valid ptr in action
bool clip(SwRle* target) override
{
@ -236,10 +235,9 @@ struct SwImageTask : SwTask
imageReset(&image);
if (!image.data || image.w == 0 || image.h == 0) goto end;
if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end;
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
// TODO: How do we clip the triangle mesh? Only clip non-meshed images for now
if (mesh->triangleCnt == 0 && clips.count > 0) {
if (clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end;
if (image.rle) {
//Clear current task memorypool here if the clippers would use the same memory pool
@ -423,7 +421,7 @@ bool SwRenderer::renderImage(RenderData data)
if (task->opacity == 0) return true;
return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity);
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
}
@ -632,7 +630,7 @@ bool SwRenderer::endComposite(Compositor* cmp)
//Default is alpha blending
if (p->method == CompositeMethod::None) {
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
return rasterImage(surface, &p->image, nullptr, m, p->bbox, p->opacity);
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
}
return true;
@ -697,7 +695,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
}
RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
RenderData SwRenderer::prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{
//prepare task
auto task = static_cast<SwImageTask*>(data);
@ -705,7 +703,6 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
else task->done();
task->source = surface;
task->mesh = mesh;
return prepareCommon(task, transform, clips, opacity, flags);
}

View file

@ -37,7 +37,7 @@ class SwRenderer : public RenderMethod
{
public:
RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override;

View file

@ -205,23 +205,6 @@ Result Picture::size(float* w, float* h) const noexcept
}
Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept
{
if (!triangles && triangleCnt > 0) return Result::InvalidArguments;
if (triangles && triangleCnt == 0) return Result::InvalidArguments;
pImpl->mesh(triangles, triangleCnt);
return Result::Success;
}
uint32_t Picture::mesh(const Polygon** triangles) const noexcept
{
if (triangles) *triangles = pImpl->rm.triangles;
return pImpl->rm.triangleCnt;
}
const Paint* Picture::paint(uint32_t id) noexcept
{
struct Value

View file

@ -63,7 +63,6 @@ struct Picture::Impl
Surface* surface = nullptr; //bitmap picture uses
RenderData rd = nullptr; //engine data
float w = 0, h = 0;
RenderMesh rm; //mesh data
Picture* picture = nullptr;
bool resizing = false;
bool needComp = false; //need composition
@ -102,7 +101,7 @@ struct Picture::Impl
auto scale = sx < sy ? sx : sy;
auto m = transform * Matrix{scale, 0, 0, 0, scale, 0, 0, 0, 1};
rd = renderer->prepare(surface, &rm, rd, m, clips, opacity, flag);
rd = renderer->prepare(surface, rd, m, clips, opacity, flag);
} else if (paint) {
if (resizing) {
loader->resize(paint, w, h);
@ -116,37 +115,10 @@ struct Picture::Impl
bool bounds(float* x, float* y, float* w, float* h, bool stroking)
{
if (rm.triangleCnt > 0) {
auto triangles = rm.triangles;
auto min = triangles[0].vertex[0].pt;
auto max = triangles[0].vertex[0].pt;
for (uint32_t i = 0; i < rm.triangleCnt; ++i) {
if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
}
if (x) *x = min.x;
if (y) *y = min.y;
if (w) *w = max.x - min.x;
if (h) *h = max.y - min.y;
} else {
if (x) *x = 0;
if (y) *y = 0;
if (w) *w = this->w;
if (h) *h = this->h;
}
if (x) *x = 0;
if (y) *y = 0;
if (w) *w = this->w;
if (h) *h = this->h;
return true;
}
@ -181,19 +153,6 @@ struct Picture::Impl
return load(loader);
}
void mesh(const Polygon* triangles, const uint32_t triangleCnt)
{
if (triangles && triangleCnt > 0) {
this->rm.triangleCnt = triangleCnt;
this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt);
memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt);
} else {
free(this->rm.triangles);
this->rm.triangles = nullptr;
this->rm.triangleCnt = 0;
}
}
Paint* duplicate(Paint* ret)
{
if (ret) TVGERR("RENDERER", "TODO: duplicate()");
@ -216,12 +175,6 @@ struct Picture::Impl
dup->h = h;
dup->resizing = resizing;
if (rm.triangleCnt > 0) {
dup->rm.triangleCnt = rm.triangleCnt;
dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt);
memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
}
return picture;
}

View file

@ -86,15 +86,15 @@ struct Compositor
uint8_t opacity;
};
struct RenderMesh
struct Vertex
{
Polygon* triangles = nullptr;
uint32_t triangleCnt = 0;
Point pt;
Point uv;
};
~RenderMesh()
{
free(triangles);
}
struct Polygon
{
Vertex vertex[3];
};
struct RenderRegion
@ -287,7 +287,7 @@ public:
virtual ~RenderMethod() {}
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
virtual RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
virtual bool preRender() = 0;
virtual bool renderShape(RenderData data) = 0;
virtual bool renderImage(RenderData data) = 0;

View file

@ -279,26 +279,6 @@ void WgGeometryData::appendBlitBox()
}
void WgGeometryData::appendMesh(const RenderMesh* rmesh)
{
assert(rmesh);
positions.pts.reserve(rmesh->triangleCnt * 3);
texCoords.reserve(rmesh->triangleCnt * 3);
indexes.reserve(rmesh->triangleCnt * 3);
for (uint32_t i = 0; i < rmesh->triangleCnt; i++) {
positions.appendPoint(rmesh->triangles[i].vertex[0].pt);
positions.appendPoint(rmesh->triangles[i].vertex[1].pt);
positions.appendPoint(rmesh->triangles[i].vertex[2].pt);
texCoords.push(rmesh->triangles[i].vertex[0].uv);
texCoords.push(rmesh->triangles[i].vertex[1].uv);
texCoords.push(rmesh->triangles[i].vertex[2].uv);
indexes.push(i*3 + 0);
indexes.push(i*3 + 1);
indexes.push(i*3 + 2);
}
}
void WgGeometryData::appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke)
{
assert(stroke);

View file

@ -117,7 +117,6 @@ struct WgGeometryData
void appendCircle(WgPoint center, float radius);
void appendImageBox(float w, float h);
void appendBlitBox();
void appendMesh(const RenderMesh* rmesh);
void appendStrokeDashed(const WgPolyline* polyline, const RenderStroke *stroke);
void appendStrokeJoin(const WgPoint& v0, const WgPoint& v1, const WgPoint& v2,
StrokeJoin join, float halfWidth, float miterLimit);

View file

@ -514,7 +514,6 @@ void WgRenderDataShapePool::release(WgContext& context)
void WgRenderDataPicture::release(WgContext& context)
{
meshData.release(context);
imageData.release(context);
context.pipelines->layouts.releaseBindGroup(bindGroupPicture);
WgRenderDataPaint::release(context);

View file

@ -110,7 +110,7 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const
}
RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
RenderData WgRenderer::prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{
// get or create render data shape
auto renderDataPicture = (WgRenderDataPicture*)data;
@ -127,8 +127,7 @@ RenderData WgRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
// update image data
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Image)) {
WgGeometryData geometryData;
if (mesh->triangleCnt == 0) geometryData.appendImageBox(surface->w, surface->h);
else geometryData.appendMesh(mesh);
geometryData.appendImageBox(surface->w, surface->h);
mContext.pipelines->layouts.releaseBindGroup(renderDataPicture->bindGroupPicture);
renderDataPicture->meshData.release(mContext);
renderDataPicture->meshData.update(mContext, &geometryData);

View file

@ -29,7 +29,7 @@ class WgRenderer : public RenderMethod
{
public:
RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
RenderData prepare(Surface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;
bool renderImage(RenderData data) override;

View file

@ -635,20 +635,6 @@ TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* p
cnt += writeData(pixels, imgSize);
cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
//mesh: currently only available in bitmap image.
const Polygon* triangles = nullptr;
auto triangleCnt = picture->mesh(&triangles);
if (triangles && triangleCnt > 0) {
TvgBinCounter triangleCntSize = SIZE(triangleCnt);
TvgBinCounter trianglesSize = triangleCnt * SIZE(triangles[0]);
writeTag(TVG_TAG_PICTURE_MESH);
writeCount(triangleCntSize + trianglesSize);
cnt += writeData(&triangleCnt, triangleCntSize);
cnt += writeData(triangles, trianglesSize);
cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
}
//Bitmap picture needs the transform info.
cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);

Binary file not shown.

View file

@ -98,67 +98,6 @@ TEST_CASE("Load RAW file and render", "[tvgPicture]")
free(data);
}
TEST_CASE("Texture mesh", "[tvgPicture]")
{
auto picture = Picture::gen();
REQUIRE(picture);
ifstream file(TEST_DIR"/rawimage_200x300.raw");
if (!file.is_open()) return;
auto data = (uint32_t*)malloc(sizeof(uint32_t) * (200*300));
file.read(reinterpret_cast<char *>(data), sizeof (uint32_t) * 200 * 300);
file.close();
REQUIRE(picture->load(data, 200, 300, false) == Result::Success);
//Composing Meshes
tvg::Polygon triangles[4];
triangles[0].vertex[0] = {{100, 125}, {0, 0}};
triangles[0].vertex[1] = {{300, 100}, {0.5, 0}};
triangles[0].vertex[2] = {{200, 550}, {0, 1}};
triangles[1].vertex[0] = {{300, 100}, {0.5, 0}};
triangles[1].vertex[1] = {{350, 450}, {0.5, 1}};
triangles[1].vertex[2] = {{200, 550}, {0, 1}};
triangles[2].vertex[0] = {{300, 100}, {0.5, 0}};
triangles[2].vertex[1] = {{500, 200}, {1, 0}};
triangles[2].vertex[2] = {{350, 450}, {0.5, 1}};
triangles[3].vertex[0] = {{500, 200}, {1, 0}};
triangles[3].vertex[1] = {{450, 450}, {1, 1}};
triangles[3].vertex[2] = {{350, 450}, {0.5, 1}};
//Negative cases
const tvg::Polygon* triangles2 = nullptr;
REQUIRE(picture->mesh(nullptr, 4) == tvg::Result::InvalidArguments);
REQUIRE(picture->mesh(nullptr) == 0);
REQUIRE(picture->mesh(&triangles2) == 0);
REQUIRE(picture->mesh(triangles, 0) == tvg::Result::InvalidArguments);
REQUIRE(picture->mesh(nullptr) == 0);
REQUIRE(picture->mesh(&triangles2) == 0);
//Positive cases
REQUIRE(picture->mesh(triangles, 4) == tvg::Result::Success);
REQUIRE(picture->mesh(nullptr) == 4);
REQUIRE(picture->mesh(&triangles2) == 4);
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 3; j++) {
REQUIRE(triangles[i].vertex[j].pt.x == triangles2[i].vertex[j].pt.x);
REQUIRE(triangles[i].vertex[j].pt.y == triangles2[i].vertex[j].pt.y);
REQUIRE(triangles[i].vertex[j].uv.x == triangles2[i].vertex[j].uv.x);
REQUIRE(triangles[i].vertex[j].uv.y == triangles2[i].vertex[j].uv.y);
}
}
REQUIRE(picture->mesh(nullptr, 0) == tvg::Result::Success);
REQUIRE(picture->mesh(nullptr) == 0);
REQUIRE(picture->mesh(&triangles2) == 0);
free(data);
}
TEST_CASE("Picture Size", "[tvgPicture]")
{
auto picture = Picture::gen();