api: revise engine initialization and termination

Simplified parameters and ensured proper backend engine
initialization by using reference checking through canvas
instances.

C++ API Modification:
- Result Initializer::init(uint32_t threads, CanvasEngine engine)
 -> Result Initializer::init(uint32_t threads)
- Result Initializer::term(CanvasEngine engine)
 -> Result Initializer::term()

C API Modification:
- Tvg_Result tvg_engine_init(Tvg_Engine engine_method, unsigned threads)
 -> Tvg_Result tvg_engine_init(unsigned threads);
- Tvg_Result tvg_engine_term(Tvg_Engine engine_method)
 ->  Tvg_Result tvg_engine_term()

issue: https://github.com/thorvg/thorvg/issues/3116
This commit is contained in:
Hermet Park 2025-04-23 14:01:31 +09:00 committed by Hermet Park
parent 4cf68b75e5
commit 53e5e783b7
25 changed files with 313 additions and 459 deletions

View file

@ -268,7 +268,7 @@ float progress(uint32_t elapsed, float durationInSec)
int main(int argc, char **argv)
{
tvg_engine_init(Tvg_Engine(TVG_ENGINE_SW), 0);
tvg_engine_init(4);
SDL_Init(SDL_INIT_VIDEO);
@ -333,7 +333,7 @@ int main(int argc, char **argv)
SDL_Quit();
tvg_engine_term(Tvg_Engine(TVG_ENGINE_SW));
tvg_engine_term();
return 0;
}

View file

@ -148,10 +148,9 @@ struct Window
bool clearBuffer = false;
bool print = false;
Window(tvg::CanvasEngine engine, Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt)
Window(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt)
{
//Initialize ThorVG Engine (engine: raster method)
if (!verify(tvg::Initializer::init(threadsCnt, engine), "Failed to init ThorVG engine!")) return;
if (!verify(tvg::Initializer::init(threadsCnt), "Failed to init ThorVG engine!")) return;
//Initialize the SDL
SDL_Init(SDL_INIT_VIDEO);
@ -284,7 +283,7 @@ struct Window
struct SwWindow : Window
{
SwWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(tvg::CanvasEngine::Sw, example, width, height, threadsCnt)
SwWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(example, width, height, threadsCnt)
{
if (!initialized) return;
@ -323,7 +322,7 @@ struct GlWindow : Window
{
SDL_GLContext context;
GlWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(tvg::CanvasEngine::Gl, example, width, height, threadsCnt)
GlWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(example, width, height, threadsCnt)
{
if (!initialized) return;
@ -383,7 +382,7 @@ struct WgWindow : Window
WGPUAdapter adapter;
WGPUDevice device;
WgWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(tvg::CanvasEngine::Wg, example, width, height, threadsCnt)
WgWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(example, width, height, threadsCnt)
{
if (!initialized) return;
@ -484,7 +483,7 @@ struct WgWindow : Window
#else
struct WgWindow : Window
{
WgWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(tvg::CanvasEngine::Wg, example, width, height, threadsCnt)
WgWindow(Example* example, uint32_t width, uint32_t height, uint32_t threadsCnt) : Window(example, width, height, threadsCnt)
{
cout << "webgpu driver is not detected!" << endl;
}
@ -540,20 +539,20 @@ bool verify(tvg::Result result, string failMsg)
int main(Example* example, int argc, char **argv, bool clearBuffer = false, uint32_t width = 800, uint32_t height = 800, uint32_t threadsCnt = 4, bool print = false)
{
auto engine = tvg::CanvasEngine::Sw;
auto engine = 0; //0: sw, 1: gl, 2: wg
if (argc > 1) {
if (!strcmp(argv[1], "gl")) engine = tvg::CanvasEngine::Gl;
if (!strcmp(argv[1], "wg")) engine = tvg::CanvasEngine::Wg;
if (!strcmp(argv[1], "gl")) engine = 1;
if (!strcmp(argv[1], "wg")) engine = 2;
}
unique_ptr<Window> window;
if (engine == tvg::CanvasEngine::Sw) {
if (engine == 0) {
window = unique_ptr<Window>(new SwWindow(example, width, height, threadsCnt));
} else if (engine == tvg::CanvasEngine::Gl) {
} else if (engine == 1) {
window = unique_ptr<Window>(new GlWindow(example, width, height, threadsCnt));
} else if (engine == tvg::CanvasEngine::Wg) {
} else if (engine == 2) {
window = unique_ptr<Window>(new WgWindow(example, width, height, threadsCnt));
}

View file

@ -401,20 +401,20 @@ void runWg()
int main(int argc, char **argv)
{
auto engine = tvg::CanvasEngine::Sw;
auto engine = 0; //0: sw, 1: gl, 2: wg
if (argc > 1) {
if (!strcmp(argv[1], "gl")) engine = tvg::CanvasEngine::Gl;
if (!strcmp(argv[1], "wg")) engine = tvg::CanvasEngine::Wg;
if (!strcmp(argv[1], "gl")) engine = 1;
if (!strcmp(argv[1], "wg")) engine = 2;
}
if (tvgexam::verify(tvg::Initializer::init(4, engine))) {
if (tvgexam::verify(tvg::Initializer::init(4))) {
SDL_Init(SDL_INIT_VIDEO);
if (engine == tvg::CanvasEngine::Sw) runSw();
else if (engine == tvg::CanvasEngine::Gl) runGl();
else if (engine == tvg::CanvasEngine::Wg) runWg();
if (engine == 0) runSw();
else if (engine == 1) runGl();
else if (engine == 2) runWg();
SDL_Quit();

View file

@ -237,18 +237,6 @@ enum class SceneEffect : uint8_t
};
/**
* @brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed.
*/
enum class CanvasEngine : uint8_t
{
All = 0, ///< All feasible rasterizers. @since 1.0
Sw = (1 << 1), ///< CPU rasterizer.
Gl = (1 << 2), ///< OpenGL rasterizer.
Wg = (1 << 3), ///< WebGPU rasterizer. @since 0.15
};
/**
* @brief Enumeration specifying the ThorVG class type value.
*
@ -1850,35 +1838,33 @@ class TVG_API Initializer final
{
public:
/**
* @brief Initializes TVG engines.
* @brief Initializes the ThorVG engine.
*
* TVG requires the running-engine environment.
* TVG runs its own task-scheduler for parallelizing rendering tasks efficiently.
* You can indicate the number of threads, the count of which is designated @p threads.
* In the initialization step, TVG will generate/spawn the threads as set by @p threads count.
* ThorVG requires an active runtime environment to operate.
* Internally, it utilizes a task scheduler to efficiently parallelize rendering operations.
* You can specify the number of worker threads using the @p threads parameter.
* During initialization, ThorVG will spawn the specified number of threads.
*
* @param[in] threads The number of additional threads. Zero indicates only the main thread is to be used.
* @param[in] engine The engine types to initialize. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed.
* @param[in] threads The number of worker threads to create. A value of zero indicates that only the main thread will be used.
*
* @retval Result::NonSupport In case the engine type is not supported on the system.
*
* @note The Initializer keeps track of the number of times it was called. Threads count is fixed at the first init() call.
* @note The initializer uses internal reference counting to track multiple calls.
* The number of threads is fixed on the first call to init() and cannot be changed in subsequent calls.
* @see Initializer::term()
*/
static Result init(uint32_t threads, CanvasEngine engine = tvg::CanvasEngine::All) noexcept;
static Result init(uint32_t threads) noexcept;
/**
* @brief Terminates TVG engines.
* @brief Terminates the ThorVG engine.
*
* @param[in] engine The engine types to terminate. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed
* Cleans up resources and stops any internal threads initialized by init().
*
* @retval Result::InsufficientCondition In case there is nothing to be terminated.
* @retval Result::NonSupport In case the engine type is not supported on the system.
* @retval Result::InsufficientCondition Returned if there is nothing to terminate (e.g., init() was not called).
*
* @note Initializer does own reference counting for multiple calls.
* @note The initializer maintains a reference count for safe repeated use.
* Only the final call to term() will fully shut down the engine.
* @see Initializer::init()
*/
static Result term(CanvasEngine engine = tvg::CanvasEngine::All) noexcept;
static Result term() noexcept;
/**
* @brief Retrieves the version of the TVG engine.

View file

@ -107,18 +107,6 @@ typedef struct _Tvg_Animation Tvg_Animation;
typedef struct _Tvg_Accessor Tvg_Accessor;
/**
* @brief Enumeration specifying the engine type used for the graphics backend. For multiple backends bitwise operation is allowed.
*
* @ingroup ThorVGCapi_Initializer
*/
typedef enum {
TVG_ENGINE_SW = (1 << 1), ///< CPU rasterizer
TVG_ENGINE_GL = (1 << 2), ///< OpenGL rasterizer
TVG_ENGINE_WG = (1 << 3) ///< WebGPU rasterizer
} Tvg_Engine;
/**
* @brief Enumeration specifying the result from the APIs.
*
@ -342,43 +330,36 @@ typedef struct
/* Engine API */
/************************************************************************/
/*!
* @brief Initializes TVG engines.
* @brief Initializes the ThorVG engine.
*
* TVG requires the running-engine environment.
* TVG runs its own task-scheduler for parallelizing rendering tasks efficiently.
* You can indicate the number of threads, the count of which is designated @p threads.
* In the initialization step, TVG will generate/spawn the threads as set by @p threads count.
* ThorVG requires an active runtime environment to operate.
* Internally, it utilizes a task scheduler to efficiently parallelize rendering operations.
* You can specify the number of worker threads using the @p threads parameter.
* During initialization, ThorVG will spawn the specified number of threads.
*
* @param[in] engine_method The engine types to initialize. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed.
* @param[in] threads The number of additional threads used to perform rendering. Zero indicates only the main thread is to be used.
* @param[in] threads The number of worker threads to create. A value of zero indicates that only the main thread will be used.
*
* @return Tvg_Result enumeration.
* @retval TVG_RESULT_INVALID_ARGUMENT Unknown engine type.
* @retval TVG_RESULT_NOT_SUPPORTED Unsupported engine type.
*
* @note The Initializer keeps track of the number of times it was called. Threads count is fixed at the first init() call.
* @note The initializer uses internal reference counting to track multiple calls.
* The number of threads is fixed on the first call to tvg_engine_init() and cannot be changed in subsequent calls.
* @see tvg_engine_term()
* @see Tvg_Engine
*/
TVG_API Tvg_Result tvg_engine_init(Tvg_Engine engine_method, unsigned threads);
TVG_API Tvg_Result tvg_engine_init(unsigned threads);
/*!
* @brief Terminates TVG engines.
* @brief Terminates the ThorVG engine.
*
* It should be called in case of termination of the TVG client with the same engine types as were passed when tvg_engine_init() was called.
*
* @param engine_method The engine types to terminate. This is relative to the Canvas types, in which it will be used. For multiple backends bitwise operation is allowed
* Cleans up resources and stops any internal threads initialized by tvg_engine_init().
*
* @return Tvg_Result enumeration.
* @retval TVG_RESULT_INSUFFICIENT_CONDITION Nothing to be terminated.
* @retval TVG_RESULT_INVALID_ARGUMENT Unknown engine type.
* @retval TVG_RESULT_NOT_SUPPORTED Unsupported engine type.
* @retval TVG_RESULT_INSUFFICIENT_CONDITION Returned if there is nothing to terminate (e.g., tvg_engine_init() was not called).
*
* @note The initializer maintains a reference count for safe repeated use. Only the final call to tvg_engine_term() will fully shut down the engine.
* @see tvg_engine_init()
* @see Tvg_Engine
*/
TVG_API Tvg_Result tvg_engine_term(Tvg_Engine engine_method);
TVG_API Tvg_Result tvg_engine_term();
/**

View file

@ -40,15 +40,15 @@ extern "C" {
/* Engine API */
/************************************************************************/
TVG_API Tvg_Result tvg_engine_init(Tvg_Engine engine_method, unsigned threads)
TVG_API Tvg_Result tvg_engine_init(unsigned threads)
{
return (Tvg_Result) Initializer::init(threads, CanvasEngine(engine_method));
return (Tvg_Result) Initializer::init(threads);
}
TVG_API Tvg_Result tvg_engine_term(Tvg_Engine engine_method)
TVG_API Tvg_Result tvg_engine_term()
{
return (Tvg_Result) Initializer::term(CanvasEngine(engine_method));
return (Tvg_Result) Initializer::term();
}

View file

@ -34,17 +34,7 @@
#define NOISE_LEVEL 0.5f
static atomic<int32_t> initEngineCnt{};
static atomic<int32_t> rendererCnt{};
static void _termEngine()
{
if (rendererCnt > 0) return;
//TODO: Clean up global resources
}
static atomic<int32_t> rendererCnt{-1};
void GlRenderer::clearDisposes()
{
@ -88,6 +78,7 @@ void GlRenderer::currentContext()
GlRenderer::GlRenderer()
{
++rendererCnt;
}
@ -99,7 +90,7 @@ GlRenderer::~GlRenderer()
ARRAY_FOREACH(p, mPrograms) delete(*p);
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
}
@ -1520,45 +1511,33 @@ bool GlRenderer::postUpdate()
}
bool GlRenderer::init(uint32_t threads)
{
if ((initEngineCnt++) > 0) return true;
//TODO: runtime linking?
return true;
}
int32_t GlRenderer::init()
{
return initEngineCnt;
}
bool GlRenderer::term()
{
if ((--initEngineCnt) > 0) return true;
if (rendererCnt > 0) return false;
initEngineCnt = 0;
//TODO: clean up global resources
_termEngine();
rendererCnt = -1;
return true;
}
GlRenderer* GlRenderer::gen()
GlRenderer* GlRenderer::gen(TVG_UNUSED uint32_t threads)
{
//TODO: GL minimum version check, should be replaced with the runtime linking in GlRenderer::init()
GLint vMajor, vMinor;
glGetIntegerv(GL_MAJOR_VERSION, &vMajor);
glGetIntegerv(GL_MINOR_VERSION, &vMinor);
if (vMajor < TVG_REQUIRE_GL_MAJOR_VER || (vMajor == TVG_REQUIRE_GL_MAJOR_VER && vMinor < TVG_REQUIRE_GL_MINOR_VER)) {
TVGERR("GL_ENGINE", "OpenGL/ES version is not satisfied. Current: v%d.%d, Required: v%d.%d", vMajor, vMinor, TVG_REQUIRE_GL_MAJOR_VER, TVG_REQUIRE_GL_MINOR_VER);
return nullptr;
//initialize engine
if (rendererCnt == -1) {
//TODO: GL minimum version check, should be replaced with the runtime linking in GlRenderer::init()
GLint vMajor, vMinor;
glGetIntegerv(GL_MAJOR_VERSION, &vMajor);
glGetIntegerv(GL_MINOR_VERSION, &vMinor);
if (vMajor < TVG_REQUIRE_GL_MAJOR_VER || (vMajor == TVG_REQUIRE_GL_MAJOR_VER && vMinor < TVG_REQUIRE_GL_MINOR_VER)) {
TVGERR("GL_ENGINE", "OpenGL/ES version is not satisfied. Current: v%d.%d, Required: v%d.%d", vMajor, vMinor, TVG_REQUIRE_GL_MAJOR_VER, TVG_REQUIRE_GL_MINOR_VER);
return nullptr;
}
TVGLOG("GL_ENGINE", "OpenGL/ES version = v%d.%d", vMajor, vMinor);
rendererCnt = 0;
}
TVGLOG("GL_ENGINE", "OpenGL/ES version = v%d.%d", vMajor, vMinor);
return new GlRenderer();
return new GlRenderer;
}

View file

@ -99,9 +99,7 @@ public:
bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
void dispose(RenderEffect* effect) override;
static GlRenderer* gen();
static bool init(TVG_UNUSED uint32_t threads);
static int32_t init();
static GlRenderer* gen(uint32_t threads);
static bool term();
private:

View file

@ -184,7 +184,7 @@ bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffec
auto back = buffer.buf32;
auto swapped = false;
TVGLOG("SW_ENGINE", "GaussianFilter region(%ld, %ld, %ld, %ld) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level);
TVGLOG("SW_ENGINE", "GaussianFilter region(%d, %d, %d, %d) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level);
/* It is best to take advantage of the Gaussian blurs separable property
by dividing the process into two passes. horizontal and vertical.
@ -363,7 +363,7 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe
auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3];
TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level);
TVGLOG("SW_ENGINE", "DropShadow region(%d, %d, %d, %d) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level);
//saving the original image in order to overlay it into the filtered image.
_dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false);
@ -433,7 +433,7 @@ bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct)
auto h = size_t(bbox.max.y - bbox.min.y);
auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255);
TVGLOG("SW_ENGINE", "Fill region(%ld, %ld, %ld, %ld), param(%d %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->color[0], params->color[1], params->color[2], params->color[3]);
TVGLOG("SW_ENGINE", "Fill region(%d, %d, %d, %d), param(%d %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->color[0], params->color[1], params->color[2], params->color[3]);
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
@ -484,7 +484,7 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct)
auto opacity = cmp->opacity;
auto luma = cmp->recoverSfc->alphas[2]; //luma function
TVGLOG("SW_ENGINE", "Tint region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity);
TVGLOG("SW_ENGINE", "Tint region(%d, %d, %d, %d), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity);
/* Tint Formula: (1 - L) * Black + L * White, where the L is Luminance. */
@ -558,7 +558,7 @@ bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool di
auto opacity = cmp->opacity;
auto luma = cmp->recoverSfc->alphas[2]; //luma function
TVGLOG("SW_ENGINE", "Tritone region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2]);
TVGLOG("SW_ENGINE", "Tritone region(%d, %d, %d, %d), param(%d %d %d, %d %d %d, %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2]);
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);

View file

@ -362,7 +362,7 @@ static bool _rasterMaskedRect(SwSurface* surface, const SwBBox& region, const Re
//8bit masking channels composition
if (surface->channelSize != sizeof(uint8_t)) return false;
TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
TVGLOG("SW_ENGINE", "Masked(%d) Rect [Region: %d %d %d %d]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
auto maskOp = _getMaskOp(surface->compositor->method);
if (_direct(surface->compositor->method)) return _rasterDirectMaskedRect(surface, region, maskOp, c.a);
@ -379,7 +379,7 @@ static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, const Re
auto cbuffer = surface->compositor->image.buf8 + ((region.min.y * surface->compositor->image.stride + region.min.x) * csize); //compositor buffer
auto alpha = surface->alpha(surface->compositor->method);
TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %d %d %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
//32bits channels
if (surface->channelSize == sizeof(uint32_t)) {
@ -917,7 +917,7 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize;
auto alpha = surface->alpha(surface->compositor->method);
TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %d %d %d %d]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
@ -1039,7 +1039,7 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %d %d %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
//32 bits
if (surface->channelSize == sizeof(uint32_t)) {
@ -1269,7 +1269,7 @@ static bool _rasterGradientMaskedRect(SwSurface* surface, const SwBBox& region,
{
auto method = surface->compositor->method;
TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %lu %lu %lu %lu]", (int)method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
TVGLOG("SW_ENGINE", "Masked(%d) Gradient [Region: %d %d %d %d]", (int)method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
auto maskOp = _getMaskOp(method);
@ -1290,7 +1290,7 @@ static bool _rasterGradientMattedRect(SwSurface* surface, const SwBBox& region,
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize;
auto alpha = surface->alpha(surface->compositor->method);
TVGLOG("SW_ENGINE", "Matted(%d) Gradient [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
TVGLOG("SW_ENGINE", "Matted(%d) Gradient [Region: %d %d %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
for (uint32_t y = 0; y < h; ++y) {
fillMethod()(fill, buffer, region.min.y + y, region.min.x, w, cbuffer, alpha, csize, 255);

View file

@ -32,8 +32,7 @@
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static atomic<int32_t> initEngineCnt{};
static atomic<int32_t> rendererCnt{};
static atomic<int32_t> rendererCnt{-1};
static SwMpool* globalMpool = nullptr;
static uint32_t threadsCnt = 0;
@ -242,15 +241,6 @@ struct SwImageTask : SwTask
};
static void _termEngine()
{
if (rendererCnt > 0) return;
mpoolTerm(globalMpool);
globalMpool = nullptr;
}
static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
{
if (auto fill = task->rshape->fill) {
@ -280,6 +270,21 @@ static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity
/* External Class Implementation */
/************************************************************************/
SwRenderer::SwRenderer()
{
if (TaskScheduler::onthread()) {
TVGLOG("SW_RENDERER", "Running on a non-dominant thread!, Renderer(%p)", this);
mpool = mpoolInit(threadsCnt);
sharedMpool = false;
} else {
mpool = globalMpool;
sharedMpool = true;
}
++rendererCnt;
}
SwRenderer::~SwRenderer()
{
clearCompositors();
@ -289,8 +294,6 @@ SwRenderer::~SwRenderer()
if (!sharedMpool) mpoolTerm(mpool);
--rendererCnt;
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
}
@ -777,59 +780,30 @@ RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const
}
SwRenderer::SwRenderer()
{
if (TaskScheduler::onthread()) {
TVGLOG("SW_RENDERER", "Running on a non-dominant thread!, Renderer(%p)", this);
mpool = mpoolInit(threadsCnt);
sharedMpool = false;
} else {
mpool = globalMpool;
sharedMpool = true;
}
}
bool SwRenderer::init(uint32_t threads)
{
if ((initEngineCnt++) > 0) return true;
threadsCnt = threads;
//Share the memory pool among the renderer
globalMpool = mpoolInit(threads);
if (!globalMpool) {
--initEngineCnt;
return false;
}
return true;
}
int32_t SwRenderer::init()
{
#ifdef THORVG_SW_OPENMP_SUPPORT
omp_set_num_threads(TaskScheduler::threads());
#endif
return initEngineCnt;
}
bool SwRenderer::term()
{
if ((--initEngineCnt) > 0) return true;
if (rendererCnt > 0) return false;
initEngineCnt = 0;
_termEngine();
mpoolTerm(globalMpool);
globalMpool = nullptr;
rendererCnt = -1;
return true;
}
SwRenderer* SwRenderer::gen()
SwRenderer* SwRenderer::gen(uint32_t threads)
{
++rendererCnt;
return new SwRenderer();
//initialize engine
if (rendererCnt == -1) {
#ifdef THORVG_SW_OPENMP_SUPPORT
omp_set_num_threads(threads);
#endif
//Share the memory pool among the renderer
globalMpool = mpoolInit(threads);
threadsCnt = threads;
rendererCnt = 0;
}
return new SwRenderer;
}

View file

@ -66,9 +66,7 @@ public:
bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
void dispose(RenderEffect* effect) override;
static SwRenderer* gen();
static bool init(uint32_t threads);
static int32_t init();
static SwRenderer* gen(uint32_t threads);
static bool term();
private:

View file

@ -116,6 +116,8 @@ namespace tvg {
{
std::free(ptr);
}
extern int engineInit;
}
#endif //_TVG_COMMON_H_

View file

@ -21,23 +21,19 @@
*/
#include "tvgCanvas.h"
#include "tvgTaskScheduler.h"
#ifdef THORVG_GL_RASTER_SUPPORT
#include "tvgGlRenderer.h"
#endif
GlCanvas::GlCanvas()
{
#ifdef THORVG_GL_RASTER_SUPPORT
pImpl->renderer = GlRenderer::gen();
pImpl->renderer->ref();
#endif
}
GlCanvas::GlCanvas() = default;
GlCanvas::~GlCanvas()
{
//TODO:
#ifdef THORVG_GL_RASTER_SUPPORT
GlRenderer::term();
#endif
}
@ -70,8 +66,14 @@ Result GlCanvas::target(void* context, int32_t id, uint32_t w, uint32_t h, Color
GlCanvas* GlCanvas::gen() noexcept
{
#ifdef THORVG_GL_RASTER_SUPPORT
if (GlRenderer::init() <= 0) return nullptr;
return new GlCanvas;
if (engineInit > 0) {
auto renderer = GlRenderer::gen(TaskScheduler::threads());
if (!renderer) return nullptr;
renderer->ref();
auto ret = new GlCanvas;
ret->pImpl->renderer = renderer;
return ret;
}
#endif
return nullptr;
}

View file

@ -41,14 +41,12 @@
/* Internal Class Implementation */
/************************************************************************/
static int _initCnt = 0;
namespace tvg {
int engineInit = 0;
}
static uint16_t _version = 0;
//enum class operation helper
static constexpr bool operator &(CanvasEngine a, CanvasEngine b)
{
return int(a) & int(b);
}
static bool _buildVersionInfo(uint32_t* major, uint32_t* minor, uint32_t* micro)
{
@ -82,34 +80,9 @@ static bool _buildVersionInfo(uint32_t* major, uint32_t* minor, uint32_t* micro)
/* External Class Implementation */
/************************************************************************/
Result Initializer::init(uint32_t threads, CanvasEngine engine) noexcept
Result Initializer::init(uint32_t threads) noexcept
{
auto nonSupport = true;
if (engine == CanvasEngine::All || engine & CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
#endif
}
if (engine == CanvasEngine::All || engine & CanvasEngine::Gl) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
#endif
}
if (engine == CanvasEngine::All || engine & CanvasEngine::Wg) {
#ifdef THORVG_WG_RASTER_SUPPORT
if (!WgRenderer::init(threads)) return Result::FailedAllocation;
nonSupport = false;
#endif
}
if (nonSupport) return Result::NonSupport;
if (_initCnt++ > 0) return Result::Success;
if (engineInit++ > 0) return Result::Success;
if (!_buildVersionInfo(nullptr, nullptr, nullptr)) return Result::Unknown;
@ -121,36 +94,23 @@ Result Initializer::init(uint32_t threads, CanvasEngine engine) noexcept
}
Result Initializer::term(CanvasEngine engine) noexcept
Result Initializer::term() noexcept
{
if (_initCnt == 0) return Result::InsufficientCondition;
if (engineInit == 0) return Result::InsufficientCondition;
auto nonSupport = true;
if (--engineInit > 0) return Result::Success;
if (engine == CanvasEngine::All || engine & CanvasEngine::Sw) {
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
}
#ifdef THORVG_SW_RASTER_SUPPORT
if (!SwRenderer::term()) return Result::InsufficientCondition;
#endif
if (engine == CanvasEngine::All || engine & CanvasEngine::Gl) {
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
}
#ifdef THORVG_GL_RASTER_SUPPORT
if (!GlRenderer::term()) return Result::InsufficientCondition;
#endif
if (engine == CanvasEngine::All || engine & CanvasEngine::Wg) {
#ifdef THORVG_WG_RASTER_SUPPORT
if (!WgRenderer::term()) return Result::InsufficientCondition;
nonSupport = false;
#endif
}
if (nonSupport) return Result::NonSupport;
if (--_initCnt > 0) return Result::Success;
#ifdef THORVG_WG_RASTER_SUPPORT
if (!WgRenderer::term()) return Result::InsufficientCondition;
#endif
TaskScheduler::term();

View file

@ -21,6 +21,7 @@
*/
#include "tvgCanvas.h"
#include "tvgTaskScheduler.h"
#include "tvgLoadModule.h"
#ifdef THORVG_SW_RASTER_SUPPORT
@ -28,18 +29,13 @@
#endif
SwCanvas::SwCanvas()
{
#ifdef THORVG_SW_RASTER_SUPPORT
pImpl->renderer = SwRenderer::gen();
pImpl->renderer->ref();
#endif
}
SwCanvas::SwCanvas() = default;
SwCanvas::~SwCanvas()
{
//TODO:
#ifdef THORVG_SW_RASTER_SUPPORT
SwRenderer::term();
#endif
}
@ -76,8 +72,13 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
SwCanvas* SwCanvas::gen() noexcept
{
#ifdef THORVG_SW_RASTER_SUPPORT
if (SwRenderer::init() <= 0) return nullptr;
return new SwCanvas;
if (engineInit > 0) {
auto renderer = SwRenderer::gen(TaskScheduler::threads());
renderer->ref();
auto ret = new SwCanvas;
ret->pImpl->renderer = renderer;
return ret;
}
#endif
return nullptr;
}

View file

@ -21,26 +21,22 @@
*/
#include "tvgCanvas.h"
#include "tvgTaskScheduler.h"
#ifdef THORVG_WG_RASTER_SUPPORT
#include "tvgWgRenderer.h"
#endif
WgCanvas::WgCanvas()
{
#ifdef THORVG_WG_RASTER_SUPPORT
pImpl->renderer = WgRenderer::gen();
pImpl->renderer->ref();
#endif
}
WgCanvas::WgCanvas() = default;
WgCanvas::~WgCanvas()
{
#ifdef THORVG_WG_RASTER_SUPPORT
auto renderer = static_cast<WgRenderer*>(pImpl->renderer);
renderer->target(nullptr, nullptr, nullptr, 0, 0);
WgRenderer::term();
#endif
}
@ -74,8 +70,13 @@ Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w,
WgCanvas* WgCanvas::gen() noexcept
{
#ifdef THORVG_WG_RASTER_SUPPORT
if (WgRenderer::init() <= 0) return nullptr;
return new WgCanvas;
if (engineInit > 0) {
auto renderer = WgRenderer::gen(TaskScheduler::threads());
renderer->ref();
auto ret = new WgCanvas;
ret->pImpl->renderer = renderer;
return ret;
}
#endif
return nullptr;
}

View file

@ -29,16 +29,7 @@
/* Internal Class Implementation */
/************************************************************************/
static atomic<int32_t> initEngineCnt{};
static atomic<int32_t> rendererCnt{};
static void _termEngine()
{
if (rendererCnt > 0) return;
//TODO:
}
static atomic<int32_t> rendererCnt{-1};
void WgRenderer::release()
@ -426,6 +417,8 @@ WgRenderer::WgRenderer()
} else {
mBufferPool.pool = WgGeometryBufferPool::instance();
}
++rendererCnt;
}
@ -436,8 +429,6 @@ WgRenderer::~WgRenderer()
if (mBufferPool.individual) delete(mBufferPool.pool);
--rendererCnt;
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
}
@ -648,36 +639,24 @@ bool WgRenderer::postUpdate()
}
bool WgRenderer::init(TVG_UNUSED uint32_t threads)
{
if ((initEngineCnt++) > 0) return true;
//TODO: global engine init
return true;
}
int32_t WgRenderer::init()
{
return initEngineCnt;
}
bool WgRenderer::term()
{
if ((--initEngineCnt) > 0) return true;
if (rendererCnt > 0) return false;
initEngineCnt = 0;
//TODO: clean up global resources
_termEngine();
rendererCnt = -1;
return true;
}
WgRenderer* WgRenderer::gen()
WgRenderer* WgRenderer::gen(TVG_UNUSED uint32_t threads)
{
++rendererCnt;
return new WgRenderer();
//initialize engine
if (rendererCnt == -1) {
//TODO:
}
return new WgRenderer;
}

View file

@ -58,9 +58,7 @@ public:
bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
void dispose(RenderEffect* effect) override;
static WgRenderer* gen();
static bool init(uint32_t threads);
static int32_t init();
static WgRenderer* gen(uint32_t threads);
static bool term();
private:

View file

@ -41,46 +41,46 @@ TEST_CASE("Accessor Creation", "[tvgAccessor]")
TEST_CASE("Set", "[tvgAccessor]")
{
REQUIRE(Initializer::init(0) == Result::Success);
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
auto picture = unique_ptr<Picture>(Picture::gen());
REQUIRE(picture);
REQUIRE(picture->load(TEST_DIR"/logo.svg") == Result::Success);
auto accessor = unique_ptr<Accessor>(Accessor::gen());
REQUIRE(accessor);
//Case 1
REQUIRE(accessor->set(picture.get(), nullptr, nullptr) == Result::InvalidArguments);
//Case 2
Shape* ret = nullptr;
auto f = [](const tvg::Paint* paint, void* data) -> bool
{
if (paint->type() == Type::Shape) {
auto shape = (tvg::Shape*) paint;
uint8_t r, g, b;
shape->fill(&r, &g, &b);
if (r == 37 && g == 47 && b == 53) {
shape->fill(0, 0, 255);
shape->id = Accessor::id("TestAccessor");
*static_cast<Shape**>(data) = shape;
return false;
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
auto picture = unique_ptr<Picture>(Picture::gen());
REQUIRE(picture);
REQUIRE(picture->load(TEST_DIR"/logo.svg") == Result::Success);
auto accessor = unique_ptr<Accessor>(Accessor::gen());
REQUIRE(accessor);
//Case 1
REQUIRE(accessor->set(picture.get(), nullptr, nullptr) == Result::InvalidArguments);
//Case 2
Shape* ret = nullptr;
auto f = [](const tvg::Paint* paint, void* data) -> bool
{
if (paint->type() == Type::Shape) {
auto shape = (tvg::Shape*) paint;
uint8_t r, g, b;
shape->fill(&r, &g, &b);
if (r == 37 && g == 47 && b == 53) {
shape->fill(0, 0, 255);
shape->id = Accessor::id("TestAccessor");
*static_cast<Shape**>(data) = shape;
return false;
}
}
}
return true;
};
return true;
};
REQUIRE(accessor->set(picture.get(), f, &ret) == Result::Success);
REQUIRE((ret && ret->id == Accessor::id("TestAccessor")));
REQUIRE(accessor->set(picture.get(), f, &ret) == Result::Success);
REQUIRE((ret && ret->id == Accessor::id("TestAccessor")));
}
REQUIRE(Initializer::term() == Result::Success);
}

View file

@ -58,9 +58,4 @@ TEST_CASE("Version", "[tvgInitializer]")
TEST_CASE("Negative termination", "[tvgInitializer]")
{
REQUIRE(Initializer::term() == Result::InsufficientCondition);
}
TEST_CASE("Invalid engine", "[tvgInitializer]")
{
REQUIRE(Initializer::init(0, CanvasEngine(64)) == Result::NonSupport);
}
}

View file

@ -71,30 +71,30 @@ TEST_CASE("Load RAW Data", "[tvgPicture]")
TEST_CASE("Load RAW file and render", "[tvgPicture]")
{
REQUIRE(Initializer::init(0) == Result::Success);
{
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
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();
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();
auto picture = Picture::gen();
REQUIRE(picture);
auto picture = Picture::gen();
REQUIRE(picture);
REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success);
REQUIRE(picture->size(100, 150) == Result::Success);
REQUIRE(picture->load(data, 200, 300, ColorSpace::ARGB8888, false) == Result::Success);
REQUIRE(picture->size(100, 150) == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
free(data);
}
REQUIRE(Initializer::term() == Result::Success);
free(data);
}
TEST_CASE("Picture Size", "[tvgPicture]")
@ -203,28 +203,28 @@ TEST_CASE("Load SVG Data", "[tvgPicture]")
TEST_CASE("Load SVG file and render", "[tvgPicture]")
{
REQUIRE(Initializer::init(0) == Result::Success);
{
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto buffer = new uint32_t[1000*1000];
if (!buffer) return;
auto buffer = new uint32_t[1000*1000];
if (!buffer) return;
REQUIRE(canvas->target(buffer, 1000, 1000, 1000, ColorSpace::ABGR8888) == Result::Success);
REQUIRE(canvas->target(buffer, 1000, 1000, 1000, ColorSpace::ABGR8888) == Result::Success);
auto picture = Picture::gen();
REQUIRE(picture);
auto picture = Picture::gen();
REQUIRE(picture);
REQUIRE(picture->load(TEST_DIR"/tag.svg") == Result::Success);
REQUIRE(picture->size(100, 100) == Result::Success);
REQUIRE(picture->load(TEST_DIR"/tag.svg") == Result::Success);
REQUIRE(picture->size(100, 100) == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
REQUIRE(canvas->draw() == Result::Success);
REQUIRE(canvas->sync() == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
REQUIRE(canvas->draw() == Result::Success);
REQUIRE(canvas->sync() == Result::Success);
delete[] buffer;
}
REQUIRE(Initializer::term() == Result::Success);
delete[] buffer;
}
#endif
@ -275,22 +275,22 @@ TEST_CASE("Load PNG file from data", "[tvgPicture]")
TEST_CASE("Load PNG file and render", "[tvgPicture]")
{
REQUIRE(Initializer::init(0) == Result::Success);
{
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
auto picture = Picture::gen();
REQUIRE(picture);
auto picture = Picture::gen();
REQUIRE(picture);
REQUIRE(picture->load(TEST_DIR"/test.png") == Result::Success);
REQUIRE(picture->opacity(192) == Result::Success);
REQUIRE(picture->scale(5.0) == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
REQUIRE(picture->load(TEST_DIR"/test.png") == Result::Success);
REQUIRE(picture->opacity(192) == Result::Success);
REQUIRE(picture->scale(5.0) == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
}
REQUIRE(Initializer::term() == Result::Success);
}
@ -345,20 +345,21 @@ TEST_CASE("Load JPG file from data", "[tvgPicture]")
TEST_CASE("Load JPG file and render", "[tvgPicture]")
{
REQUIRE(Initializer::init(0) == Result::Success);
{
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
auto picture = Picture::gen();
REQUIRE(picture);
auto picture = Picture::gen();
REQUIRE(picture);
REQUIRE(picture->load(TEST_DIR"/test.jpg") == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
REQUIRE(picture->load(TEST_DIR"/test.jpg") == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
}
REQUIRE(Initializer::term() == Result::Success);
}
@ -410,22 +411,22 @@ TEST_CASE("Load WEBP file from data", "[tvgPicture]")
TEST_CASE("Load WEBP file and render", "[tvgPicture]")
{
REQUIRE(Initializer::init(0) == Result::Success);
{
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
uint32_t buffer[100*100];
REQUIRE(canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888) == Result::Success);
auto picture = Picture::gen();
REQUIRE(picture);
auto picture = Picture::gen();
REQUIRE(picture);
REQUIRE(picture->load(TEST_DIR"/test.webp") == Result::Success);
REQUIRE(picture->opacity(192) == Result::Success);
REQUIRE(picture->scale(5.0) == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
REQUIRE(picture->load(TEST_DIR"/test.webp") == Result::Success);
REQUIRE(picture->opacity(192) == Result::Success);
REQUIRE(picture->scale(5.0) == Result::Success);
REQUIRE(canvas->push(picture) == Result::Success);
}
REQUIRE(Initializer::term() == Result::Success);
}

View file

@ -78,27 +78,27 @@ TEST_CASE("Scene Clear", "[tvgScene]")
TEST_CASE("Scene Clear And Reuse Shape", "[tvgScene]")
{
REQUIRE(Initializer::init(0) == Result::Success);
{
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
REQUIRE(canvas);
auto scene = Scene::gen();
REQUIRE(scene);
auto scene = Scene::gen();
REQUIRE(scene);
auto shape = Shape::gen();
REQUIRE(shape);
REQUIRE(shape->ref() == 1);
auto shape = Shape::gen();
REQUIRE(shape);
REQUIRE(shape->ref() == 1);
REQUIRE(scene->push(shape) == Result::Success);
REQUIRE(canvas->push(scene) == Result::Success);
REQUIRE(canvas->update() == Result::Success);
REQUIRE(scene->push(shape) == Result::Success);
REQUIRE(canvas->push(scene) == Result::Success);
REQUIRE(canvas->update() == Result::Success);
//No deallocate shape.
REQUIRE(scene->remove() == Result::Success);
//Reuse shape.
REQUIRE(scene->push(shape) == Result::Success);
REQUIRE(shape->unref() == 1); //The scene still holds 1.
//No deallocate shape.
REQUIRE(scene->remove() == Result::Success);
//Reuse shape.
REQUIRE(scene->push(shape) == Result::Success);
REQUIRE(shape->unref() == 1); //The scene still holds 1.
}
REQUIRE(Initializer::term() == Result::Success);
}

View file

@ -70,7 +70,7 @@ private:
bool convert(string& in, string& out)
{
if (Initializer::init(0, CanvasEngine::Sw) != Result::Success) return false;
if (Initializer::init(0) != Result::Success) return false;
auto animation = Animation::gen();
auto picture = animation->picture();

View file

@ -172,7 +172,7 @@ private:
if (threads > 0) --threads;
//Initialize ThorVG Engine
if (tvg::Initializer::init(threads, tvg::CanvasEngine::Sw) != tvg::Result::Success) {
if (tvg::Initializer::init(threads) != tvg::Result::Success) {
cout << "Error: Engine is not supported" << endl;
}