mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-07 21:23:32 +00:00
sw_engine: implement gradial gradient feature
also added testRadialGradient Change-Id: If4a278cb4667c38c7842ad30edf5aa2fdd56fff7
This commit is contained in:
parent
5c988d01a5
commit
7366e8949b
7 changed files with 242 additions and 33 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -15,3 +15,4 @@ testSceneTransform
|
|||
testStroke
|
||||
testStrokeLine
|
||||
testLinearGradient
|
||||
testRadialGradient
|
||||
|
|
|
@ -158,11 +158,24 @@ struct SwDashStroke
|
|||
|
||||
struct SwFill
|
||||
{
|
||||
uint32_t* ctable;
|
||||
float x1, y1, x2, y2;
|
||||
struct SwLinear {
|
||||
float dx, dy;
|
||||
float len;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SwRadial {
|
||||
float cx, cy;
|
||||
float a;
|
||||
float inv2a;
|
||||
};
|
||||
|
||||
union {
|
||||
SwLinear linear;
|
||||
SwRadial radial;
|
||||
};
|
||||
|
||||
uint32_t* ctable;
|
||||
FillSpread spread;
|
||||
bool translucent;
|
||||
};
|
||||
|
@ -251,12 +264,13 @@ void strokeFree(SwStroke* stroke);
|
|||
bool fillGenColorTable(SwFill* fill, const Fill* fdata);
|
||||
void fillReset(SwFill* fill, const Fill* fdata);
|
||||
void fillFree(SwFill* fill);
|
||||
void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
|
||||
void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
|
||||
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
|
||||
|
||||
SwRleData* rleRender(const SwOutline* outline, const SwBBox& bbox, const SwSize& clip);
|
||||
void rleFree(SwRleData* rle);
|
||||
|
||||
bool rasterGradientShape(Surface& surface, SwShape& shape);
|
||||
bool rasterGradientShape(Surface& surface, SwShape& shape, unsigned id);
|
||||
bool rasterSolidShape(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterStroke(Surface& surface, SwShape& shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
#define FIXPT_SIZE (1<<FIXPT_BITS)
|
||||
|
||||
|
||||
static bool _updateColorTable(SwFill* fill, const LinearGradient* linear)
|
||||
static bool _updateColorTable(SwFill* fill, const Fill* fdata)
|
||||
{
|
||||
assert(fill && linear);
|
||||
assert(fill && fdata);
|
||||
|
||||
if (!fill->ctable) {
|
||||
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
|
||||
|
@ -39,7 +39,7 @@ static bool _updateColorTable(SwFill* fill, const LinearGradient* linear)
|
|||
}
|
||||
|
||||
const Fill::ColorStop* colors;
|
||||
auto cnt = linear->colorStops(&colors);
|
||||
auto cnt = fdata->colorStops(&colors);
|
||||
if (cnt == 0 || !colors) return false;
|
||||
|
||||
auto pColors = colors;
|
||||
|
@ -92,18 +92,18 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear)
|
|||
{
|
||||
assert(fill && linear);
|
||||
|
||||
if (linear->linear(&fill->x1, &fill->y1, &fill->x2, &fill->y2) != Result::Success) return false;
|
||||
float x1, x2, y1, y2;
|
||||
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
|
||||
|
||||
fill->dx = fill->x2 - fill->x1;
|
||||
fill->dy = fill->y2 - fill->y1;
|
||||
fill->len = fill->dx * fill->dx + fill->dy * fill->dy;
|
||||
fill->offset = 0;
|
||||
fill->linear.dx = x2 - x1;
|
||||
fill->linear.dy = y2 - y1;
|
||||
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
|
||||
|
||||
if (fill->len < FLT_EPSILON) return true;
|
||||
if (fill->linear.len < FLT_EPSILON) return true;
|
||||
|
||||
fill->dx /= fill->len;
|
||||
fill->dy /= fill->len;
|
||||
fill->offset = -fill->dx * fill->x1 - fill->dy * fill->y1;
|
||||
fill->linear.dx /= fill->linear.len;
|
||||
fill->linear.dy /= fill->linear.len;
|
||||
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
|
||||
|
||||
return _updateColorTable(fill, linear);
|
||||
}
|
||||
|
@ -113,7 +113,14 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial)
|
|||
{
|
||||
assert(fill && radial);
|
||||
|
||||
return true;
|
||||
float radius;
|
||||
if (radial->radial(&fill->radial.cx, &fill->radial.cy, &radius) != Result::Success) return false;
|
||||
if (radius < FLT_EPSILON) return true;
|
||||
|
||||
fill->radial.a = radius * radius;
|
||||
fill->radial.inv2a = pow(1 / (2 * fill->radial.a), 2);
|
||||
|
||||
return _updateColorTable(fill, radial);
|
||||
}
|
||||
|
||||
|
||||
|
@ -180,15 +187,39 @@ static inline void _write(uint32_t *dst, uint32_t val, uint32_t len)
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void fillFetch(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
|
||||
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
|
||||
{
|
||||
assert(fill->len > 0);
|
||||
if (fill->radial.a < FLT_EPSILON) return;
|
||||
|
||||
//TODO: Rotation???
|
||||
auto rx = x + 0.5f - fill->radial.cx;
|
||||
auto ry = y + 0.5f - fill->radial.cy;
|
||||
auto inv2a = fill->radial.inv2a;
|
||||
auto rxy = rx * rx + ry * ry;
|
||||
auto rxryPlus = 2 * rx;
|
||||
auto det = (-4 * fill->radial.a * -rxy) * inv2a;
|
||||
auto detDelta = (4 * fill->radial.a * (rxryPlus + 1.0f)) * inv2a;
|
||||
auto detDelta2 = (4 * fill->radial.a * 2.0f) * inv2a;
|
||||
|
||||
for (uint32_t i = 0 ; i < len ; ++i)
|
||||
{
|
||||
*dst = _pixel(fill, sqrt(det));
|
||||
++dst;
|
||||
det += detDelta;
|
||||
detDelta += detDelta2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
|
||||
{
|
||||
if (fill->linear.len < FLT_EPSILON) return;
|
||||
|
||||
//TODO: Rotation???
|
||||
auto rx = x + 0.5f;
|
||||
auto ry = y + 0.5f;
|
||||
auto t = (fill->dx * rx + fill->dy * ry + fill->offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
auto inc = (fill->dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
auto t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
auto inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (fabsf(inc) < FLT_EPSILON) {
|
||||
auto color = _fixedPixel(fill, static_cast<uint32_t>(t * FIXPT_SIZE));
|
||||
|
|
|
@ -69,7 +69,7 @@ static bool _rasterSolidRle(Surface& surface, SwRleData* rle, uint32_t color)
|
|||
}
|
||||
|
||||
|
||||
static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
|
||||
static bool _rasterLinearGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
|
||||
{
|
||||
if (!rle || !fill) return false;
|
||||
|
||||
|
@ -81,17 +81,16 @@ static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* f
|
|||
|
||||
//Translucent Gradient
|
||||
if (fill->translucent) {
|
||||
uint32_t tmp;
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||
fillFetch(fill, buf, span->y, span->x, span->len);
|
||||
fillFetchLinear(fill, buf, span->y, span->x, span->len);
|
||||
if (span->coverage == 255) {
|
||||
for (uint32_t i = 0; i < span->len; ++i) {
|
||||
dst[i] = buf[i] + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(buf[i]));
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < span->len; ++i) {
|
||||
tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
|
||||
auto tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
|
||||
dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
|
||||
}
|
||||
}
|
||||
|
@ -102,9 +101,56 @@ static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* f
|
|||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||
if (span->coverage == 255) {
|
||||
fillFetch(fill, dst, span->y, span->x, span->len);
|
||||
fillFetchLinear(fill, dst, span->y, span->x, span->len);
|
||||
} else {
|
||||
fillFetch(fill, buf, span->y, span->x, span->len);
|
||||
fillFetchLinear(fill, buf, span->y, span->x, span->len);
|
||||
auto ialpha = 255 - span->coverage;
|
||||
for (uint32_t i = 0; i < span->len; ++i) {
|
||||
dst[i] = COLOR_ALPHA_BLEND(buf[i], span->coverage) + COLOR_ALPHA_BLEND(dst[i], ialpha);
|
||||
}
|
||||
}
|
||||
++span;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _rasterRadialGradientRle(Surface& surface, SwRleData* rle, const SwFill* fill)
|
||||
{
|
||||
if (!rle || !fill) return false;
|
||||
|
||||
auto buf = static_cast<uint32_t*>(alloca(surface.w * sizeof(uint32_t)));
|
||||
if (!buf) return false;
|
||||
|
||||
auto span = rle->spans;
|
||||
auto stride = surface.stride;
|
||||
|
||||
//Translucent Gradient
|
||||
if (fill->translucent) {
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||
fillFetchRadial(fill, buf, span->y, span->x, span->len);
|
||||
if (span->coverage == 255) {
|
||||
for (uint32_t i = 0; i < span->len; ++i) {
|
||||
dst[i] = buf[i] + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(buf[i]));
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < span->len; ++i) {
|
||||
auto tmp = COLOR_ALPHA_BLEND(buf[i], span->coverage);
|
||||
dst[i] = tmp + COLOR_ALPHA_BLEND(dst[i], 255 - COLOR_ALPHA(tmp));
|
||||
}
|
||||
}
|
||||
++span;
|
||||
}
|
||||
//Opaque Gradient
|
||||
} else {
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
auto dst = &surface.buffer[span->y * stride + span->x];
|
||||
if (span->coverage == 255) {
|
||||
fillFetchRadial(fill, dst, span->y, span->x, span->len);
|
||||
} else {
|
||||
fillFetchRadial(fill, buf, span->y, span->x, span->len);
|
||||
auto ialpha = 255 - span->coverage;
|
||||
for (uint32_t i = 0; i < span->len; ++i) {
|
||||
dst[i] = COLOR_ALPHA_BLEND(buf[i], span->coverage) + COLOR_ALPHA_BLEND(dst[i], ialpha);
|
||||
|
@ -121,9 +167,10 @@ static bool _rasterGradientRle(Surface& surface, SwRleData* rle, const SwFill* f
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool rasterGradientShape(Surface& surface, SwShape& shape)
|
||||
bool rasterGradientShape(Surface& surface, SwShape& shape, unsigned id)
|
||||
{
|
||||
return _rasterGradientRle(surface, shape.rle, shape.fill);
|
||||
if (id == FILL_ID_LINEAR) return _rasterLinearGradientRle(surface, shape.rle, shape.fill);
|
||||
return _rasterRadialGradientRle(surface, shape.rle, shape.fill);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -64,8 +64,8 @@ bool SwRenderer::render(const Shape& sdata, void *data)
|
|||
|
||||
uint8_t r, g, b, a;
|
||||
|
||||
if (sdata.fill()) {
|
||||
rasterGradientShape(surface, *shape);
|
||||
if (auto fill = sdata.fill()) {
|
||||
rasterGradientShape(surface, *shape, fill->id());
|
||||
} else {
|
||||
sdata.fill(&r, &g, &b, &a);
|
||||
if (a > 0) rasterSolidShape(surface, *shape, r, g, b, a);
|
||||
|
|
|
@ -13,3 +13,4 @@ all:
|
|||
gcc -o testStroke testStroke.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
|
||||
gcc -o testStrokeLine testStrokeLine.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
|
||||
gcc -o testLinearGradient testLinearGradient.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
|
||||
gcc -o testRadialGradient testRadialGradient.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
|
||||
|
|
115
test/testRadialGradient.cpp
Normal file
115
test/testRadialGradient.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
#include <tizenvg.h>
|
||||
#include <Elementary.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define WIDTH 800
|
||||
#define HEIGHT 800
|
||||
|
||||
static uint32_t buffer[WIDTH * HEIGHT];
|
||||
|
||||
void tvgtest()
|
||||
{
|
||||
//Initialize TizenVG Engine
|
||||
tvg::Engine::init();
|
||||
|
||||
//Create a Canvas
|
||||
auto canvas = tvg::SwCanvas::gen();
|
||||
canvas->target(buffer, WIDTH, WIDTH, HEIGHT);
|
||||
canvas->reserve(3); //reserve 3 shape nodes (optional)
|
||||
|
||||
//Prepare Round Rectangle
|
||||
auto shape1 = tvg::Shape::gen();
|
||||
shape1->appendRect(0, 0, 400, 400, 0); //x, y, w, h, cornerRadius
|
||||
|
||||
//RadialGradient
|
||||
auto fill = tvg::RadialGradient::gen();
|
||||
fill->radial(200, 200, 200);
|
||||
|
||||
//Linear Gradient Color Stops
|
||||
tvg::Fill::ColorStop colorStops[2];
|
||||
colorStops[0] = {0, 255, 255, 255, 255};
|
||||
colorStops[1] = {1, 0, 0, 0, 255};
|
||||
|
||||
fill->colorStops(colorStops, 2);
|
||||
|
||||
shape1->fill(move(fill));
|
||||
canvas->push(move(shape1));
|
||||
|
||||
//Prepare Circle
|
||||
auto shape2 = tvg::Shape::gen();
|
||||
shape2->appendCircle(400, 400, 200, 200); //cx, cy, radiusW, radiusH
|
||||
|
||||
//RadialGradient
|
||||
auto fill2 = tvg::RadialGradient::gen();
|
||||
fill2->radial(400, 400, 200);
|
||||
|
||||
//Linear Gradient Color Stops
|
||||
tvg::Fill::ColorStop colorStops2[3];
|
||||
colorStops2[0] = {0, 255, 0, 0, 255};
|
||||
colorStops2[1] = {0.5, 255, 255, 0, 255};
|
||||
colorStops2[2] = {1, 255, 255, 255, 255};
|
||||
|
||||
fill2->colorStops(colorStops2, 3);
|
||||
|
||||
shape2->fill(move(fill2));
|
||||
canvas->push(move(shape2));
|
||||
|
||||
|
||||
//Prepare Ellipse
|
||||
auto shape3 = tvg::Shape::gen();
|
||||
shape3->appendCircle(600, 600, 150, 100); //cx, cy, radiusW, radiusH
|
||||
|
||||
//RadialGradient
|
||||
auto fill3 = tvg::RadialGradient::gen();
|
||||
fill3->radial(600, 600, 150);
|
||||
|
||||
//Linear Gradient Color Stops
|
||||
tvg::Fill::ColorStop colorStops3[4];
|
||||
colorStops3[0] = {0, 0, 127, 0, 127};
|
||||
colorStops3[1] = {0.25, 0, 170, 170, 170};
|
||||
colorStops3[2] = {0.5, 200, 0, 200, 200};
|
||||
colorStops3[3] = {1, 255, 255, 255, 255};
|
||||
|
||||
fill3->colorStops(colorStops3, 4);
|
||||
|
||||
shape3->fill(move(fill3));
|
||||
canvas->push(move(shape3));
|
||||
|
||||
//Draw the Shapes onto the Canvas
|
||||
canvas->draw();
|
||||
canvas->sync();
|
||||
|
||||
//Terminate TizenVG Engine
|
||||
tvg::Engine::term();
|
||||
}
|
||||
|
||||
void
|
||||
win_del(void *data, Evas_Object *o, void *ev)
|
||||
{
|
||||
elm_exit();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
tvgtest();
|
||||
|
||||
//Show the result using EFL...
|
||||
elm_init(argc, argv);
|
||||
|
||||
Eo* win = elm_win_util_standard_add(NULL, "TizenVG Test");
|
||||
evas_object_smart_callback_add(win, "delete,request", win_del, 0);
|
||||
|
||||
Eo* img = evas_object_image_filled_add(evas_object_evas_get(win));
|
||||
evas_object_image_size_set(img, WIDTH, HEIGHT);
|
||||
evas_object_image_data_set(img, buffer);
|
||||
evas_object_size_hint_weight_set(img, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
|
||||
evas_object_show(img);
|
||||
|
||||
elm_win_resize_object_add(win, img);
|
||||
evas_object_geometry_set(win, 0, 0, WIDTH, HEIGHT);
|
||||
evas_object_show(win);
|
||||
|
||||
elm_run();
|
||||
elm_shutdown();
|
||||
}
|
Loading…
Add table
Reference in a new issue