diff --git a/inc/thorvg.h b/inc/thorvg.h index fed62b7a..ec1a6e0f 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -231,7 +231,8 @@ enum class SceneEffect : uint8_t ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state. GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]} DropShadow, ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(float)[0 - 360], distance(float), blur_sigma(float)[> 0], quality(int)[0 - 100]} - Fill ///< Override the scene content color with a given fill information (Experimental API). Param(5) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255]} + Fill, ///< Override the scene content color with a given fill information (Experimental API). Param(5) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255]} + Tint ///< Tinting the current scene color with a given black, white color paramters (Experimental API). Param(7) = {black_R(int)[0 - 255], black_G(int)[0 - 255], black_B(int)[0 - 255], white_R(int)[0 - 255], white_G(int)[0 - 255], white_B(int)[0 - 255], intensity(float)[0 - 100]} }; diff --git a/src/renderer/sw_engine/tvgSwCommon.h b/src/renderer/sw_engine/tvgSwCommon.h index bad67c54..b1cfcddd 100644 --- a/src/renderer/sw_engine/tvgSwCommon.h +++ b/src/renderer/sw_engine/tvgSwCommon.h @@ -571,6 +571,7 @@ void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32 void rasterUnpremultiply(RenderSurface* surface); void rasterPremultiply(RenderSurface* surface); bool rasterConvertCS(RenderSurface* surface, ColorSpace to); +uint32_t rasterUnpremultiply(uint32_t data); bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params); bool effectGaussianBlurPrepare(RenderEffectGaussianBlur* effect); @@ -578,5 +579,8 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEff bool effectDropShadowPrepare(RenderEffectDropShadow* effect); bool effectFillPrepare(RenderEffectFill* effect); bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct); +bool effectTintPrepare(RenderEffectTint* effect); +bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct); + #endif /* _TVG_SW_COMMON_H_ */ diff --git a/src/renderer/sw_engine/tvgSwPostEffect.cpp b/src/renderer/sw_engine/tvgSwPostEffect.cpp index ad106a91..eebf051f 100644 --- a/src/renderer/sw_engine/tvgSwPostEffect.cpp +++ b/src/renderer/sw_engine/tvgSwPostEffect.cpp @@ -448,5 +448,60 @@ bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct) dbuffer += cmp->image.stride; } } + return true; +} + + +/************************************************************************/ +/* Tint Implementation */ +/************************************************************************/ + +bool effectTintPrepare(RenderEffectTint* params) +{ + params->valid = true; + return true; +} + + +bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct) +{ + auto& bbox = cmp->bbox; + auto w = size_t(bbox.max.x - bbox.min.x); + auto h = size_t(bbox.max.y - bbox.min.y); + auto black = cmp->recoverSfc->join(params->black[0], params->black[1], params->black[2], 255); + auto white = cmp->recoverSfc->join(params->white[0], params->white[1], params->white[2], 255); + 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, %f)", 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); + + if (direct) { + auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x); + auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); + for (size_t y = 0; y < h; ++y) { + auto dst = dbuffer; + auto src = sbuffer; + for (size_t x = 0; x < w; ++x, ++dst, ++src) { + auto tmp = rasterUnpremultiply(*src); + auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity); + *dst = INTERPOLATE(val, *dst, MULTIPLY(opacity, A(tmp))); + } + dbuffer += cmp->image.stride; + sbuffer += cmp->recoverSfc->stride; + } + cmp->valid = true; //no need the subsequent composition + } else { + auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x); + for (size_t y = 0; y < h; ++y) { + auto dst = dbuffer; + for (size_t x = 0; x < w; ++x, ++dst) { + auto tmp = rasterUnpremultiply(*dst); + auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity); + *dst = ALPHA_BLEND(val, A(tmp)); + } + dbuffer += cmp->image.stride; + } + } + return true; } \ No newline at end of file diff --git a/src/renderer/sw_engine/tvgSwRaster.cpp b/src/renderer/sw_engine/tvgSwRaster.cpp index 2b678387..10c2825a 100644 --- a/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/src/renderer/sw_engine/tvgSwRaster.cpp @@ -1668,6 +1668,20 @@ bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_ } +uint32_t rasterUnpremultiply(uint32_t data) +{ + uint8_t a = data >> 24; + if (a == 255 || a == 0) return data; + uint16_t r = ((data >> 8) & 0xff00) / a; + uint16_t g = ((data) & 0xff00) / a; + uint16_t b = ((data << 8) & 0xff00) / a; + if (r > 0xff) r = 0xff; + if (g > 0xff) g = 0xff; + if (b > 0xff) b = 0xff; + return (a << 24) | (r << 16) | (g << 8) | (b); +} + + void rasterUnpremultiply(RenderSurface* surface) { if (surface->channelSize != sizeof(uint32_t)) return; @@ -1678,20 +1692,7 @@ void rasterUnpremultiply(RenderSurface* surface) for (uint32_t y = 0; y < surface->h; y++) { auto buffer = surface->buf32 + surface->stride * y; for (uint32_t x = 0; x < surface->w; ++x) { - uint8_t a = buffer[x] >> 24; - if (a == 255) { - continue; - } else if (a == 0) { - buffer[x] = 0x00ffffff; - } else { - uint16_t r = ((buffer[x] >> 8) & 0xff00) / a; - uint16_t g = ((buffer[x]) & 0xff00) / a; - uint16_t b = ((buffer[x] << 8) & 0xff00) / a; - if (r > 0xff) r = 0xff; - if (g > 0xff) g = 0xff; - if (b > 0xff) b = 0xff; - buffer[x] = (a << 24) | (r << 16) | (g << 8) | (b); - } + buffer[x] = rasterUnpremultiply(buffer[x]); } } surface->premultiplied = false; diff --git a/src/renderer/sw_engine/tvgSwRenderer.cpp b/src/renderer/sw_engine/tvgSwRenderer.cpp index 15043f6c..c89be2c8 100644 --- a/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -655,6 +655,7 @@ bool SwRenderer::prepare(RenderEffect* effect) case SceneEffect::GaussianBlur: return effectGaussianBlurPrepare(static_cast(effect)); case SceneEffect::DropShadow: return effectDropShadowPrepare(static_cast(effect)); case SceneEffect::Fill: return effectFillPrepare(static_cast(effect)); + case SceneEffect::Tint: return effectTintPrepare(static_cast(effect)); default: return false; } } @@ -687,6 +688,9 @@ bool SwRenderer::effect(RenderCompositor* cmp, const RenderEffect* effect, bool case SceneEffect::Fill: { return effectFill(p, static_cast(effect), direct); } + case SceneEffect::Tint: { + return effectTint(p, static_cast(effect), direct); + } default: return false; } } diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index b8b454b4..0ce3b9bc 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -301,7 +301,7 @@ struct RenderEffectDropShadow : RenderEffect inst->color[0] = va_arg(args, int); inst->color[1] = va_arg(args, int); inst->color[2] = va_arg(args, int); - inst->color[3] = std::min(va_arg(args, int), 255); + inst->color[3] = va_arg(args, int); inst->angle = (float) va_arg(args, double); inst->distance = (float) va_arg(args, double); inst->sigma = std::max((float) va_arg(args, double), 0.0f); @@ -321,12 +321,33 @@ struct RenderEffectFill : RenderEffect inst->color[0] = va_arg(args, int); inst->color[1] = va_arg(args, int); inst->color[2] = va_arg(args, int); - inst->color[3] = std::min(va_arg(args, int), 255); + inst->color[3] = va_arg(args, int); inst->type = SceneEffect::Fill; return inst; } }; +struct RenderEffectTint : RenderEffect +{ + uint8_t black[3]; //rgb + uint8_t white[3]; //rgb + float intensity; //0 - 100 + + static RenderEffectTint* gen(va_list& args) + { + auto inst = new RenderEffectTint; + inst->black[0] = va_arg(args, int); + inst->black[1] = va_arg(args, int); + inst->black[2] = va_arg(args, int); + inst->white[0] = va_arg(args, int); + inst->white[1] = va_arg(args, int); + inst->white[2] = va_arg(args, int); + inst->intensity = std::min((float)va_arg(args, double), 100.0f) * 2.55f; + inst->type = SceneEffect::Tint; + return inst; + } +}; + class RenderMethod { private: diff --git a/src/renderer/tvgScene.h b/src/renderer/tvgScene.h index 626bd755..3ee7b193 100644 --- a/src/renderer/tvgScene.h +++ b/src/renderer/tvgScene.h @@ -326,6 +326,10 @@ struct Scene::Impl : Paint::Impl re = RenderEffectFill::gen(args); break; } + case SceneEffect::Tint: { + re = RenderEffectTint::gen(args); + break; + } default: break; }