sw_engine: implement gradial gradient feature

also added testRadialGradient

Change-Id: If4a278cb4667c38c7842ad30edf5aa2fdd56fff7
This commit is contained in:
Hermet Park 2020-06-14 17:53:29 +09:00
parent 5c988d01a5
commit 7366e8949b
7 changed files with 242 additions and 33 deletions

1
.gitignore vendored
View file

@ -15,3 +15,4 @@ testSceneTransform
testStroke
testStrokeLine
testLinearGradient
testRadialGradient

View file

@ -158,11 +158,24 @@ struct SwDashStroke
struct SwFill
{
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;
float x1, y1, x2, y2;
float dx, dy;
float len;
float offset;
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);

View file

@ -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));

View file

@ -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);
}

View file

@ -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);

View file

@ -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
View 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();
}