common gradient: implement linear/radial gradient interfaces.

Change-Id: Ica9c54e662e73592d3fddcabed09b1605b3a5a4f
This commit is contained in:
Hermet Park 2020-06-09 15:40:13 +09:00
parent c75cca5a13
commit c36f23e80d
17 changed files with 517 additions and 40 deletions

1
.gitignore vendored
View file

@ -14,3 +14,4 @@ testTransform
testSceneTransform
testStroke
testStrokeLine
testLinearGradient

View file

@ -35,6 +35,7 @@
extern "C" {
#endif
#define _TVG_DECLARE_PRIVATE(A) \
protected: \
struct Impl; \
@ -43,16 +44,21 @@ protected: \
const A& operator=(const A&) = delete; \
A()
#define _TVG_DECLARE_ACCESSOR(A) \
friend A
#define _TVG_DISABLE_CTOR(A) \
A() = delete; \
~A() = delete
#define _TVG_DECALRE_IDENTIFIER(A) \
#define _TVG_DECLARE_ACCESSOR(A) \
friend A
#define _TVG_DECLARE_ACCESSORS(A, B) \
friend A; \
friend B
#define _TVG_DECALRE_IDENTIFIER() \
protected: \
unsigned A##_Id
unsigned id
namespace tvg
{
@ -93,9 +99,79 @@ public:
virtual Result bounds(float* x, float* y, float* w, float* h) const = 0;
_TVG_DECALRE_IDENTIFIER(Paint);
_TVG_DECLARE_ACCESSOR(Scene);
_TVG_DECLARE_ACCESSOR(Canvas);
_TVG_DECALRE_IDENTIFIER();
_TVG_DECLARE_ACCESSORS(Canvas, Scene);
};
/**
* @class Fill
*
* @ingroup TizenVG
*
* @brief description...
*
*/
class TVG_EXPORT Fill
{
public:
struct ColorStop
{
float pos;
uint8_t r, g, b, a;
};
virtual ~Fill();
Result colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept;
uint32_t colorStops(const ColorStop** colorStops) const noexcept;
_TVG_DECALRE_IDENTIFIER();
_TVG_DECLARE_PRIVATE(Fill);
};
/**
* @class LinearGradient
*
* @ingroup TizenVG
*
* @brief description...
*
*/
class TVG_EXPORT LinearGradient final : public Fill
{
public:
~LinearGradient();
Result linear(float x1, float y1, float x2, float y2) noexcept;
Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept;
static std::unique_ptr<LinearGradient> gen() noexcept;
_TVG_DECLARE_PRIVATE(LinearGradient);
};
/**
* @class RadialGradient
*
* @ingroup TizenVG
*
* @brief description...
*
*/
class TVG_EXPORT RadialGradient final : public Fill
{
public:
~RadialGradient();
Result radial(float cx, float cy, float radius) noexcept;
Result radial(float* cx, float* cy, float* radius) const noexcept;
static std::unique_ptr<RadialGradient> gen() noexcept;
_TVG_DECLARE_PRIVATE(RadialGradient);
};
@ -161,6 +237,7 @@ public:
//Fill
Result fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
Result fill(std::unique_ptr<Fill> f) noexcept;
//Transform
Result rotate(float degree) noexcept override;
@ -171,6 +248,7 @@ public:
uint32_t pathCommands(const PathCommand** cmds) const noexcept;
uint32_t pathCoords(const Point** pts) const noexcept;
Result fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
const Fill* fill() const noexcept;
Result bounds(float* x, float* y, float* w, float* h) const noexcept override;
float strokeWidth() const noexcept;
@ -181,9 +259,8 @@ public:
static std::unique_ptr<Shape> gen() noexcept;
_TVG_DECLARE_ACCESSOR(Scene);
_TVG_DECLARE_ACCESSOR(Canvas);
_TVG_DECLARE_PRIVATE(Shape);
_TVG_DECLARE_ACCESSORS(Canvas, Scene);
};
@ -211,8 +288,8 @@ public:
static std::unique_ptr<Scene> gen() noexcept;
_TVG_DECLARE_PRIVATE(Scene);
_TVG_DECLARE_ACCESSOR(Canvas);
_TVG_DECLARE_PRIVATE(Scene);
};
@ -231,6 +308,7 @@ public:
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
Result sync() noexcept override;
static std::unique_ptr<SwCanvas> gen() noexcept;
_TVG_DECLARE_PRIVATE(SwCanvas);
@ -254,6 +332,7 @@ public:
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
Result sync() noexcept override;
static std::unique_ptr<GlCanvas> gen() noexcept;
_TVG_DECLARE_PRIVATE(GlCanvas);

View file

@ -1,11 +1,11 @@
source_file = [
'tvgGlCommon.h',
'tvgGlGeometry.h',
'tvgGlGpuBuffer.h',
'tvgGlProgram.h',
'tvgGlRenderer.cpp',
'tvgGlRenderer.h',
'tvgGlShader.h',
'tvgGlGeometry.h',
'tvgGlRenderer.cpp',
]
glraster_dep = declare_dependency(

View file

@ -2,18 +2,21 @@ subdir('sw_engine')
subdir('gl_engine')
source_file = [
'tvgCanvasImpl.h',
'tvgCommon.h',
'tvgRenderCommon.h',
'tvgSceneImpl.h',
'tvgShapePath.h',
'tvgShapeImpl.h',
'tvgCanvasImpl.h',
'tvgSceneImpl.h',
'tvgEngine.cpp',
'tvgCanvas.cpp',
'tvgSwCanvas.cpp',
'tvgEngine.cpp',
'tvgFill.cpp',
'tvgGlCanvas.cpp',
'tvgLinearGradient.cpp',
'tvgRadialGradient.cpp',
'tvgScene.cpp',
'tvgShape.cpp'
'tvgShape.cpp',
'tvgSwCanvas.cpp',
]
src_dep = declare_dependency(

View file

@ -53,7 +53,7 @@ struct Canvas::Impl
assert(renderer);
for (auto paint : paints) {
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->clear(*renderer)) return Result::InsufficientCondition;
@ -73,7 +73,7 @@ struct Canvas::Impl
assert(renderer);
for(auto paint: paints) {
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->update(*renderer, nullptr)) return Result::InsufficientCondition;
@ -89,7 +89,7 @@ struct Canvas::Impl
{
assert(renderer);
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->update(*renderer)) return Result::InsufficientCondition;
@ -108,7 +108,7 @@ struct Canvas::Impl
if (!renderer->clear()) return Result::InsufficientCondition;
for(auto paint: paints) {
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if(!SCENE_IMPL->render(*renderer)) return Result::InsufficientCondition;

View file

@ -34,6 +34,9 @@ using namespace tvg;
#define PAINT_ID_SHAPE 0
#define PAINT_ID_SCENE 1
#define FILL_ID_LINEAR 0
#define FILL_ID_RADIAL 1
#include "tvgRenderCommon.h"
#include "tvgShapePath.h"
#include "tvgShapeImpl.h"

88
src/lib/tvgFill.cpp Normal file
View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _TVG_FILL_CPP_
#define _TVG_FILL_CPP_
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct Fill::Impl
{
ColorStop* colorStops = nullptr;
uint32_t cnt = 0;
~Impl()
{
if (colorStops) free(colorStops);
}
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
Fill::Fill():pImpl(make_unique<Impl>())
{
}
Fill::~Fill()
{
}
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
{
auto impl = pImpl.get();
if (!impl) return Result::MemoryCorruption;
if (cnt == 0) {
if (impl->colorStops) {
free(impl->colorStops);
impl->colorStops = nullptr;
impl->cnt = cnt;
}
return Result::Success;
}
if (impl->cnt != cnt) {
impl->colorStops = static_cast<ColorStop*>(realloc(impl->colorStops, cnt * sizeof(ColorStop)));
}
impl->cnt = cnt;
memcpy(impl->colorStops, colorStops, cnt * sizeof(ColorStop));
return Result::Success;
}
uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
{
auto impl = pImpl.get();
if (!impl) return 0;
if (colorStops) *colorStops = impl->colorStops;
return impl->cnt;
}
#endif /* _TVG_FILL_CPP_ */

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _TVG_LINEAR_GRADIENT_CPP_
#define _TVG_LINEAR_GRADIENT_CPP_
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct LinearGradient::Impl
{
float x1, y1, x2, y2;
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
LinearGradient::LinearGradient():pImpl(make_unique<Impl>())
{
id = FILL_ID_LINEAR;
}
LinearGradient::~LinearGradient()
{
}
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
{
if (fabsf(x2 - x1) < FLT_EPSILON && fabsf(y2 - y1) < FLT_EPSILON)
return Result::InvalidArguments;
auto impl = pImpl.get();
if (!impl) return Result::MemoryCorruption;
impl->x1 = x1;
impl->y1 = y1;
impl->x2 = x2;
impl->y2 = y2;
return Result::Success;
}
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
{
auto impl = pImpl.get();
if (!impl) return Result::MemoryCorruption;
if (x1) *x1 = impl->x1;
if (x2) *x2 = impl->x2;
if (y1) *y1 = impl->y1;
if (y2) *y2 = impl->y2;
return Result::Success;
}
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
{
auto fill = unique_ptr<LinearGradient>(new LinearGradient);
assert(fill);
return fill;
}
#endif /* _TVG_LINEAR_GRADIENT_CPP_ */

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#ifndef _TVG_RADIAL_GRADIENT_CPP_
#define _TVG_RADIAL_GRADIENT_CPP_
#include "tvgCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
struct RadialGradient::Impl
{
float cx, cy, radius;
};
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
RadialGradient::RadialGradient():pImpl(make_unique<Impl>())
{
id = FILL_ID_RADIAL;
}
RadialGradient::~RadialGradient()
{
}
Result RadialGradient::radial(float cx, float cy, float radius) noexcept
{
if (radius < FLT_EPSILON)
return Result::InvalidArguments;
auto impl = pImpl.get();
if (!impl) return Result::MemoryCorruption;
impl->cx = cx;
impl->cy = cy;
impl->radius = radius;
return Result::Success;
}
Result RadialGradient::radial(float* cx, float* cy, float* radius) const noexcept
{
auto impl = pImpl.get();
if (!impl) return Result::MemoryCorruption;
if (cx) *cx = impl->cx;
if (cy) *cy = impl->cy;
if (radius) *radius = impl->radius;
return Result::Success;
}
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
{
auto fill = unique_ptr<RadialGradient>(new RadialGradient);
assert(fill);
return fill;
}
#endif /* _TVG_RADIAL_GRADIENT_CPP_ */

View file

@ -28,7 +28,7 @@ struct Surface
uint32_t w, h;
};
enum RenderUpdateFlag {None = 0, Path = 1, Fill = 2, Stroke = 4, Transform = 8, All = 16};
enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, All = 32};
struct RenderTransform
{

View file

@ -25,7 +25,7 @@
Scene::Scene() : pImpl(make_unique<Impl>())
{
Paint_Id = PAINT_ID_SCENE;
id = PAINT_ID_SCENE;
}

View file

@ -39,7 +39,7 @@ struct Scene::Impl
bool clear(RenderMethod& renderer)
{
for (auto paint : paints) {
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->clear(renderer)) return false;
@ -57,7 +57,7 @@ struct Scene::Impl
bool updateInternal(RenderMethod &renderer, const RenderTransform* transform, uint32_t flag)
{
for(auto paint: paints) {
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->update(renderer, transform, flag)) return false;
@ -97,7 +97,7 @@ struct Scene::Impl
bool render(RenderMethod &renderer)
{
for(auto paint: paints) {
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if(!SCENE_IMPL->render(renderer)) return false;
@ -122,7 +122,7 @@ struct Scene::Impl
auto w2 = 0.0f;
auto h2 = 0.0f;
if (paint->Paint_Id == PAINT_ID_SCENE) {
if (paint->id == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
auto scene = static_cast<Scene*>(paint);
if (!SCENE_IMPL->bounds(&x2, &y2, &w2, &h2)) return false;

View file

@ -32,7 +32,7 @@ constexpr auto PATH_KAPPA = 0.552284f;
Shape :: Shape() : pImpl(make_unique<Impl>())
{
Paint_Id = PAINT_ID_SHAPE;
id = PAINT_ID_SHAPE;
}
@ -226,7 +226,29 @@ Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
impl->color[1] = g;
impl->color[2] = b;
impl->color[3] = a;
impl->flag |= RenderUpdateFlag::Fill;
impl->flag |= RenderUpdateFlag::Color;
if (impl->fill) {
delete(impl->fill);
impl->fill = nullptr;
impl->flag |= RenderUpdateFlag::Gradient;
}
return Result::Success;
}
Result Shape::fill(unique_ptr<Fill> f) noexcept
{
auto impl = pImpl.get();
if (!impl) return Result::MemoryCorruption;
auto p = f.release();
if (!p) return Result::MemoryCorruption;
if (impl->fill) delete(impl->fill);
impl->fill = p;
impl->flag |= RenderUpdateFlag::Gradient;
return Result::Success;
}
@ -245,6 +267,14 @@ Result Shape::fill(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcep
return Result::Success;
}
const Fill* Shape::fill() const noexcept
{
auto impl = pImpl.get();
if (!impl) return nullptr;
return impl->fill;
}
Result Shape::scale(float factor) noexcept
{

View file

@ -23,11 +23,6 @@
/* Internal Class Implementation */
/************************************************************************/
struct ShapeFill
{
};
struct ShapeStroke
{
float width = 0;
@ -46,9 +41,9 @@ struct ShapeStroke
struct Shape::Impl
{
ShapeFill *fill = nullptr;
ShapeStroke *stroke = nullptr;
ShapePath *path = nullptr;
Fill *fill = nullptr;
ShapeStroke *stroke = nullptr;
RenderTransform *transform = nullptr;
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
uint32_t flag = RenderUpdateFlag::None;
@ -62,8 +57,8 @@ struct Shape::Impl
~Impl()
{
if (path) delete(path);
if (stroke) delete(stroke);
if (fill) delete(fill);
if (stroke) delete(stroke);
if (transform) delete(transform);
}

View file

@ -1,4 +1,4 @@
compiler_flags = ['-DTIZENVG_BUILD']
compiler_flags = ['-DTVG_BUILD']
subdir('lib')
subdir('examples')

View file

@ -12,3 +12,4 @@ all:
gcc -o testSceneTransform testSceneTransform.cpp -g -lstdc++ `pkg-config --cflags --libs elementary tizenvg`
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`

107
test/testLinearGradient.cpp Normal file
View file

@ -0,0 +1,107 @@
#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)
//Linear Gradient Color Stops
tvg::Fill::ColorStop colorStops[3];
colorStops[0] = {0, 255, 0, 0, 255};
colorStops[1] = {0.5, 255, 255, 255, 255};
colorStops[2] = {1, 0, 0, 255, 255};
//Prepare Round Rectangle
auto shape1 = tvg::Shape::gen();
shape1->appendRect(0, 0, 400, 400, 50); //x, y, w, h, cornerRadius
shape1->stroke(255, 255, 255, 255);
shape1->stroke(2);
//LinearGradient
auto fill = tvg::LinearGradient::gen();
fill->linear(0, 0, 400, 400);
fill->colorStops(colorStops, 3);
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
shape2->stroke(255, 255, 255, 255);
shape2->stroke(2);
//LinearGradient
auto fill2 = tvg::LinearGradient::gen();
fill2->linear(400, 200, 400, 600);
fill2->colorStops(colorStops, 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
shape3->stroke(255, 255, 255, 255);
shape3->stroke(2);
//LinearGradient
auto fill3 = tvg::LinearGradient::gen();
fill3->linear(450, 600, 750, 600);
fill3->colorStops(colorStops, 3);
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();
}