Compare commits
60 commits
v1.0-pre20
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
3be197c53a | ||
![]() |
85c1ec4281 | ||
![]() |
fd1ef5f7ec | ||
![]() |
482dd0e6f8 | ||
![]() |
830db9ecb7 | ||
![]() |
ed93570756 | ||
![]() |
27e78095ff | ||
![]() |
60f0f4cbe4 | ||
![]() |
d5a5e3215c | ||
![]() |
92e3c243ec | ||
![]() |
4c3c5d9d06 | ||
![]() |
8f10f45756 | ||
![]() |
3eaf110e0a | ||
![]() |
174fae9089 | ||
![]() |
596f7f767f | ||
![]() |
2e5af58592 | ||
![]() |
bfef89858f | ||
![]() |
b3d73e1568 | ||
![]() |
a2665cbab7 | ||
![]() |
6c1b388d77 | ||
![]() |
676a465c55 | ||
![]() |
8eb046c318 | ||
![]() |
ae97f5f20f | ||
![]() |
dfc7d268a1 | ||
![]() |
eb76769dcf | ||
![]() |
0fa5d41c8d | ||
![]() |
92070fe0de | ||
![]() |
6fd7b87754 | ||
![]() |
eeebfbb654 | ||
![]() |
dc59440744 | ||
![]() |
24509b0e41 | ||
![]() |
1f53f2d72f | ||
![]() |
2eb2b83bb0 | ||
![]() |
e2909dd6a4 | ||
![]() |
d0be8cd2bd | ||
![]() |
dc8c5bce50 | ||
![]() |
cc72eda465 | ||
![]() |
8a35f02105 | ||
![]() |
04b7bb4f25 | ||
![]() |
b221eed7fa | ||
![]() |
211fee73e2 | ||
![]() |
55847bdcb3 | ||
![]() |
32c38041db | ||
![]() |
1b72113fcc | ||
![]() |
1d0973cdf0 | ||
![]() |
e01ccb5db9 | ||
![]() |
c41b2c2a1c | ||
![]() |
633bcd3176 | ||
![]() |
e98b87892b | ||
![]() |
97d96a7f8b | ||
![]() |
bace6b9e52 | ||
![]() |
1e692f223c | ||
![]() |
6e41b44ea1 | ||
![]() |
cd12618529 | ||
![]() |
7e8743e8fe | ||
![]() |
5154e491cc | ||
![]() |
5ed719dcec | ||
![]() |
97817bedfe | ||
![]() |
3fbb55440a | ||
![]() |
2524cdfee5 |
10
CODEOWNERS
|
@ -4,14 +4,8 @@
|
|||
|
||||
* @hermet
|
||||
/src/renderer/sw_engine @mgrudzinska
|
||||
/src/renderer/gl_engine @RuiwenTang @SergeyLebedkin
|
||||
/src/renderer/gl_engine @SergeyLebedkin @RuiwenTang
|
||||
/src/renderer/wg_engine @SergeyLebedkin
|
||||
/src/loaders/external_webp @JSUYA
|
||||
/src/loaders/raw @JSUYA
|
||||
/src/loaders/svg @JSUYA @mgrudzinska
|
||||
/src/loaders/webp @JSUYA
|
||||
/src/loaders/svg @mgrudzinska @JSUYA
|
||||
/src/loaders/lottie @mgrudzinska
|
||||
/src/bindings/capi @mgrudzinska
|
||||
/src/bindings/wasm @tinyjin
|
||||
/src/savers/gif @JSUYA
|
||||
/src/tools/svg2png @JSUYA
|
||||
|
|
|
@ -44,3 +44,4 @@ Thaddeus Crews <repiteo@outlook.com>
|
|||
Benjamin <benjaminhalko@hotmail.com>
|
||||
Benson Muite <benson_muite@emailplus.com>
|
||||
kkocdko <kkocdko@gmail.com>
|
||||
SoonGeon Noh <nors.nsg@gmail.com>
|
||||
|
|
27
README.md
|
@ -1,5 +1,6 @@
|
|||
[](https://discord.gg/n25xj6J6HM)
|
||||
[](https://chat.openai.com/g/g-Ht3dYIwLO-thorvgpt)
|
||||
[](https://deepwiki.com/thorvg/thorvg)
|
||||
[](https://opencollective.com/thorvg)
|
||||
[](LICENSE)
|
||||

|
||||
|
@ -98,7 +99,7 @@ ThorVG is actively under development, continuously expanding its support for ess
|
|||
- [LVGL](#lvgl)
|
||||
- [Tizen](#tizen)
|
||||
- [References](#references)
|
||||
- [APIs](#apis)
|
||||
- [Documentation](#documentation)
|
||||
- [Examples](#examples)
|
||||
- [Tools](#tools)
|
||||
- [ThorVG Viewer](#thorvg-viewer)
|
||||
|
@ -294,7 +295,7 @@ ThorVG facilitates [SVG Tiny Specification](https://www.w3.org/TR/SVGTiny12/) re
|
|||
The figure below highlights ThorVG's SVG rendering capabilities:
|
||||
|
||||
<p align="center">
|
||||
<img width="780" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_svg.png">
|
||||
<img width="780" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_svg.jpg">
|
||||
</p>
|
||||
|
||||
The following code snippet shows how to draw SVG image using ThorVG:
|
||||
|
@ -420,8 +421,10 @@ ThorVG has been integrated into the [Tizen](https://www.tizen.org) platform as t
|
|||
[Back to contents](#contents)
|
||||
<br />
|
||||
<br />
|
||||
## APIs
|
||||
The ThorVG API documentation can be accessed at [thorvg.org/apis](https://www.thorvg.org/apis), and is also available in the [C++ API](https://github.com/thorvg/thorvg/blob/main/inc/thorvg.h), [C API](https://github.com/thorvg/thorvg/blob/main/src/bindings/capi/thorvg_capi.h) within this repository.
|
||||
## Documentation
|
||||
The ThorVG API documentation is available at [thorvg.org/apis](https://www.thorvg.org/apis), and can also be found directly in this repository via the [C++ API](https://github.com/thorvg/thorvg/blob/main/inc/thorvg.h) and [C API](https://github.com/thorvg/thorvg/blob/main/src/bindings/capi/thorvg_capi.h).
|
||||
|
||||
For comprehensive and well-structured technical information, please visit the [DeepWiki](https://deepwiki.com/thorvg/thorvg), which offers in-depth guidance on ThorVG's architecture, features, and usage.
|
||||
|
||||
[Back to contents](#contents)
|
||||
<br />
|
||||
|
@ -529,14 +532,16 @@ meson setup builddir -Dbindings="capi"
|
|||
<br />
|
||||
<br />
|
||||
## Dependencies
|
||||
ThorVG offers versatile support for image loading, accommodating both static and external loaders. This flexibility ensures that, even in environments without external libraries, users can still leverage static loaders as a reliable alternative. At its foundation, the ThorVG core library is engineered to function autonomously, free from external dependencies. However, it is important to note that ThorVG also encompasses a range of optional feature extensions, each with its specific set of dependencies. The dependencies associated with these selective features are outlined as follows:
|
||||
ThorVG provides flexible image loading capabilities, supporting both static and external loaders. This design ensures that even in environments lacking external libraries, users can rely on built-in static loaders for core functionality. At its core, the ThorVG library is fully self-contained and operates without mandatory external dependencies. However, several optional feature extensions are available, each with its own set of dependencies.
|
||||
|
||||
* GL engine: [OpenGL v3.3](https://www.khronos.org/opengl/) or [GLES v3.0](https://www.khronos.org/opengles/)
|
||||
* WG engine: [webgpu-native](https://github.com/gfx-rs/wgpu-native)
|
||||
* External PNG support: [libpng](https://github.com/glennrp/libpng)
|
||||
* External JPG support: [turbojpeg](https://github.com/libjpeg-turbo/libjpeg-turbo)
|
||||
* External WebP support: [libwebp](https://developers.google.com/speed/webp/download)
|
||||
* Examples: [SDL2](https://www.libsdl.org/)
|
||||
The following outlines the dependencies for these optional features:
|
||||
|
||||
* **GL Engine**: [OpenGL 3.3](https://www.khronos.org/opengl/), [OpenGL ES 3.0](https://www.khronos.org/opengles/), or a browser with [WebGL2](https://www.khronos.org/webgl/) support.
|
||||
* **WG Engine**: [webgpu-native v0.22](https://github.com/gfx-rs/wgpu-native) or a browser with [WebGPU](https://www.w3.org/TR/webgpu/) support.
|
||||
* **PNG Loader** (external): [libpng](https://github.com/pnggroup/libpng)
|
||||
* **JPEG Loader** (external): [libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo)
|
||||
* **WebP Loader** (external): [libwebp](https://developers.google.com/speed/webp/download)
|
||||
* **Examples**: [SDL2](https://www.libsdl.org/)
|
||||
|
||||
[Back to contents](#contents)
|
||||
<br />
|
||||
|
|
|
@ -17,6 +17,6 @@ cpp_link_args = ['-miphoneos-version-min=11.0']
|
|||
system = 'darwin'
|
||||
subsystem = 'ios'
|
||||
kernel = 'xnu'
|
||||
cpu_family = 'arm64'
|
||||
cpu = 'arm64'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'aarch64'
|
||||
endian = 'little'
|
||||
|
|
|
@ -65,7 +65,7 @@ struct UserExample : tvgexam::Example
|
|||
auto picture = tvg::Picture::gen();
|
||||
if (!tvgexam::verify(picture->load(data, size, "png", "", true))) return false;
|
||||
free(data);
|
||||
picture->translate(400, 0);
|
||||
picture->translate(380, 0);
|
||||
picture->scale(0.8);
|
||||
canvas->push(picture);
|
||||
|
||||
|
|
Before Width: | Height: | Size: 399 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 444 KiB After Width: | Height: | Size: 390 KiB |
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 533 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 617 KiB After Width: | Height: | Size: 192 KiB |
26
inc/thorvg.h
|
@ -65,6 +65,7 @@ namespace tvg
|
|||
|
||||
class RenderMethod;
|
||||
class Animation;
|
||||
class Shape;
|
||||
|
||||
/**
|
||||
* @defgroup ThorVG ThorVG
|
||||
|
@ -229,9 +230,9 @@ enum class BlendMethod : uint8_t
|
|||
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]}
|
||||
GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(4) = {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(double)[0 - 360], distance(double), blur_sigma(double)[> 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(4) = {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]}
|
||||
Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights (Experimental API). Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255]}
|
||||
};
|
||||
|
@ -396,13 +397,13 @@ public:
|
|||
*
|
||||
* @param[in] clipper The shape object as the clipper.
|
||||
*
|
||||
* @retval Result::NonSupport If the @p clipper type is not Shape.
|
||||
* @retval Result::InsufficientCondition if the target has already belonged to another paint.
|
||||
* @retval Result::InsufficientCondition if the @p clipper has already belonged to another paint.
|
||||
*
|
||||
* @see Paint::clip()
|
||||
*
|
||||
* @note @p clipper only supports the Shape type.
|
||||
* @since 1.0
|
||||
*/
|
||||
Result clip(Paint* clipper) noexcept;
|
||||
Result clip(Shape* clipper) noexcept;
|
||||
|
||||
/**
|
||||
* @brief Sets the blending method for the paint object.
|
||||
|
@ -478,6 +479,19 @@ public:
|
|||
*/
|
||||
MaskMethod mask(const Paint** target) const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Get the clipper shape of the paint object.
|
||||
*
|
||||
* This function returns the clipper that has been previously set to this paint object.
|
||||
*
|
||||
* @return The shape object used as the clipper, or @c nullptr if no clipper is set.
|
||||
*
|
||||
* @see Paint::clip(Shape* clipper)
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
Shape* clip() const noexcept;
|
||||
|
||||
/**
|
||||
* @brief Increment the reference count for the Paint instance.
|
||||
*
|
||||
|
|
BIN
res/example_svg.jpg
Normal file
After Width: | Height: | Size: 453 KiB |
Before Width: | Height: | Size: 818 KiB |
|
@ -972,7 +972,7 @@ TVG_API Tvg_Result tvg_paint_set_mask_method(Tvg_Paint* paint, Tvg_Paint* target
|
|||
TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint* paint, const Tvg_Paint** target, Tvg_Mask_Method* method);
|
||||
|
||||
|
||||
/*!
|
||||
/**
|
||||
* @brief Clip the drawing region of the paint object.
|
||||
*
|
||||
* This function restricts the drawing area of the paint object to the specified shape's paths.
|
||||
|
@ -985,10 +985,24 @@ TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint* paint, const Tvg_P
|
|||
* @retval TVG_RESULT_INSUFFICIENT_CONDITION if the target has already belonged to another paint.
|
||||
* @retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape.
|
||||
*
|
||||
* @see tvg_paint_get_clip()
|
||||
|
||||
* @since 1.0
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_paint_clip(Tvg_Paint* paint, Tvg_Paint* clipper);
|
||||
TVG_API Tvg_Result tvg_paint_set_clip(Tvg_Paint* paint, Tvg_Paint* clipper);
|
||||
|
||||
/**
|
||||
* @brief Get the clipper shape of the paint object.
|
||||
*
|
||||
* This function returns the clipper that has been previously set to this paint object.
|
||||
*
|
||||
* @return The shape object used as the clipper, or @c nullptr if no clipper is set.
|
||||
*
|
||||
* @see tvg_paint_set_clip()
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
TVG_API Tvg_Paint* tvg_paint_get_clip(const Tvg_Paint* paint);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the parent paint object.
|
||||
|
|
|
@ -47,6 +47,8 @@ struct TvgEngineMethod
|
|||
}
|
||||
};
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
|
||||
struct TvgSwEngine : TvgEngineMethod
|
||||
{
|
||||
uint8_t* buffer = nullptr;
|
||||
|
@ -78,6 +80,8 @@ struct TvgSwEngine : TvgEngineMethod
|
|||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
|
||||
|
@ -243,12 +247,14 @@ public:
|
|||
{
|
||||
errorMsg = NoError;
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (engine == "sw") this->engine = new TvgSwEngine;
|
||||
#endif
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
else if (engine == "gl") this->engine = new TvgGLEngine;
|
||||
if (engine == "gl") this->engine = new TvgGLEngine;
|
||||
#endif
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
else if (engine == "wg") this->engine = new TvgWgEngine;
|
||||
if (engine == "wg") this->engine = new TvgWgEngine;
|
||||
#endif
|
||||
|
||||
if (!this->engine) {
|
||||
|
|
|
@ -95,6 +95,24 @@ struct Array
|
|||
return data[idx];
|
||||
}
|
||||
|
||||
void operator=(const Array& rhs)
|
||||
{
|
||||
reserve(rhs.count);
|
||||
if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * rhs.count);
|
||||
count = rhs.count;
|
||||
}
|
||||
|
||||
void move(Array& to)
|
||||
{
|
||||
to.reset();
|
||||
to.data = data;
|
||||
to.count = count;
|
||||
to.reserved = reserved;
|
||||
|
||||
data = nullptr;
|
||||
count = reserved = 0;
|
||||
}
|
||||
|
||||
const T* begin() const
|
||||
{
|
||||
return data;
|
||||
|
@ -130,6 +148,12 @@ struct Array
|
|||
return data[count - 1];
|
||||
}
|
||||
|
||||
T& next()
|
||||
{
|
||||
if (full()) grow(count + 1);
|
||||
return data[count++];
|
||||
}
|
||||
|
||||
T& first()
|
||||
{
|
||||
return data[0];
|
||||
|
@ -157,17 +181,14 @@ struct Array
|
|||
return count == 0;
|
||||
}
|
||||
|
||||
template<class COMPARE>
|
||||
void sort()
|
||||
bool full()
|
||||
{
|
||||
qsort<COMPARE>(data, 0, static_cast<int32_t>(count) - 1);
|
||||
return count == reserved;
|
||||
}
|
||||
|
||||
void operator=(const Array& rhs)
|
||||
template<class COMPARE> void sort()
|
||||
{
|
||||
reserve(rhs.count);
|
||||
if (rhs.count > 0) memcpy(data, rhs.data, sizeof(T) * rhs.count);
|
||||
count = rhs.count;
|
||||
qsort<COMPARE>(data, 0, (int32_t)(count - 1));
|
||||
}
|
||||
|
||||
~Array()
|
||||
|
@ -180,20 +201,14 @@ private:
|
|||
void qsort(T* arr, int32_t low, int32_t high)
|
||||
{
|
||||
if (low < high) {
|
||||
int32_t i = low;
|
||||
int32_t j = high;
|
||||
T tmp = arr[low];
|
||||
auto i = low;
|
||||
auto j = high;
|
||||
auto tmp = arr[low];
|
||||
while (i < j) {
|
||||
while (i < j && !COMPARE{}(arr[j], tmp)) --j;
|
||||
if (i < j) {
|
||||
arr[i] = arr[j];
|
||||
++i;
|
||||
}
|
||||
if (i < j) arr[i++] = arr[j];
|
||||
while (i < j && COMPARE{}(arr[i], tmp)) ++i;
|
||||
if (i < j) {
|
||||
arr[j] = arr[i];
|
||||
--j;
|
||||
}
|
||||
if (i < j) arr[j--] = arr[i];
|
||||
}
|
||||
arr[i] = tmp;
|
||||
qsort<COMPARE>(arr, low, i - 1);
|
||||
|
|
|
@ -90,7 +90,7 @@ bool operator==(const Matrix& lhs, const Matrix& rhs);
|
|||
static inline bool rightAngle(const Matrix& m)
|
||||
{
|
||||
auto radian = fabsf(tvg::atan2(m.e21, m.e11));
|
||||
if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true;
|
||||
if (tvg::zero(radian) || tvg::zero(radian - MATH_PI2) || tvg::zero(radian - MATH_PI)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,21 +33,11 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <setjmp.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgJpgd.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
|
||||
#define JPGD_NORETURN __declspec(noreturn)
|
||||
#elif defined(__GNUC__)
|
||||
#define JPGD_NORETURN __attribute__ ((noreturn))
|
||||
#else
|
||||
#define JPGD_NORETURN
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
@ -171,7 +161,7 @@ private:
|
|||
jpeg_decoder(const jpeg_decoder &);
|
||||
jpeg_decoder &operator =(const jpeg_decoder &);
|
||||
|
||||
typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
|
||||
typedef bool (*pDecode_block_func)(jpeg_decoder*, int, int, int);
|
||||
|
||||
struct huff_tables
|
||||
{
|
||||
|
@ -198,7 +188,6 @@ private:
|
|||
char m_data[1];
|
||||
};
|
||||
|
||||
jmp_buf m_jmp_state;
|
||||
mem_block *m_pMem_blocks;
|
||||
int m_image_x_size;
|
||||
int m_image_y_size;
|
||||
|
@ -274,21 +263,21 @@ private:
|
|||
int m_total_bytes_read;
|
||||
|
||||
void free_all_blocks();
|
||||
JPGD_NORETURN void stop_decoding(jpgd_status status);
|
||||
bool stop_decoding(jpgd_status status);
|
||||
void *alloc(size_t n, bool zero = false);
|
||||
void word_clear(void *p, uint16_t c, uint32_t n);
|
||||
void prep_in_buffer();
|
||||
void read_dht_marker();
|
||||
void read_dqt_marker();
|
||||
void read_sof_marker();
|
||||
void skip_variable_marker();
|
||||
void read_dri_marker();
|
||||
void read_sos_marker();
|
||||
bool prep_in_buffer();
|
||||
bool read_dht_marker();
|
||||
bool read_dqt_marker();
|
||||
bool read_sof_marker();
|
||||
bool skip_variable_marker();
|
||||
bool read_dri_marker();
|
||||
bool read_sos_marker();
|
||||
int next_marker();
|
||||
int process_markers();
|
||||
void locate_soi_marker();
|
||||
void locate_sof_marker();
|
||||
int locate_sos_marker();
|
||||
bool locate_soi_marker();
|
||||
bool locate_sof_marker();
|
||||
bool locate_sos_marker();
|
||||
void init(jpeg_decoder_stream * pStream);
|
||||
void create_look_ups();
|
||||
void fix_in_buffer();
|
||||
|
@ -297,18 +286,18 @@ private:
|
|||
coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
|
||||
inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
|
||||
void load_next_row();
|
||||
void decode_next_row();
|
||||
bool decode_next_row();
|
||||
void make_huff_table(int index, huff_tables *pH);
|
||||
void check_quant_tables();
|
||||
void check_huff_tables();
|
||||
bool check_quant_tables();
|
||||
bool check_huff_tables();
|
||||
void calc_mcu_block_order();
|
||||
int init_scan();
|
||||
void init_frame();
|
||||
void process_restart();
|
||||
void decode_scan(pDecode_block_func decode_block_func);
|
||||
void init_progressive();
|
||||
void init_sequential();
|
||||
void decode_start();
|
||||
bool init_frame();
|
||||
bool process_restart();
|
||||
bool decode_scan(pDecode_block_func decode_block_func);
|
||||
bool init_progressive();
|
||||
bool init_sequential();
|
||||
bool decode_start();
|
||||
void decode_init(jpeg_decoder_stream * pStream);
|
||||
void H2V2Convert();
|
||||
void H2V1Convert();
|
||||
|
@ -326,10 +315,10 @@ private:
|
|||
inline int huff_decode(huff_tables *pH);
|
||||
inline int huff_decode(huff_tables *pH, int& extrabits);
|
||||
static inline uint8_t clamp(int i);
|
||||
static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
static bool decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
static bool decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
static bool decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
static bool decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
|
||||
};
|
||||
|
||||
|
||||
|
@ -630,7 +619,7 @@ inline uint32_t jpeg_decoder::get_char()
|
|||
// Any bytes remaining in buffer?
|
||||
if (!m_in_buf_left) {
|
||||
// Try to get more bytes.
|
||||
prep_in_buffer();
|
||||
if (!prep_in_buffer()) return 0xFF;
|
||||
// Still nothing to get?
|
||||
if (!m_in_buf_left) {
|
||||
// Pad the end of the stream with 0xFF 0xD9 (EOI marker)
|
||||
|
@ -650,7 +639,7 @@ inline uint32_t jpeg_decoder::get_char()
|
|||
inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag)
|
||||
{
|
||||
if (!m_in_buf_left) {
|
||||
prep_in_buffer();
|
||||
if (!prep_in_buffer()) return 0xFF;
|
||||
if (!m_in_buf_left) {
|
||||
*pPadding_flag = true;
|
||||
int t = m_tem_flag;
|
||||
|
@ -1093,13 +1082,15 @@ void jpeg_decoder::free_all_blocks()
|
|||
}
|
||||
|
||||
|
||||
// This method handles all errors. It will never return.
|
||||
// It could easily be changed to use C++ exceptions.
|
||||
JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status)
|
||||
bool jpeg_decoder::stop_decoding(jpgd_status status)
|
||||
{
|
||||
#if 0 //for debugging
|
||||
m_error_code = status;
|
||||
#else
|
||||
m_error_code = JPGD_FAILED;
|
||||
#endif
|
||||
free_all_blocks();
|
||||
longjmp(m_jmp_state, status);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1116,8 +1107,7 @@ void *jpeg_decoder::alloc(size_t nSize, bool zero)
|
|||
}
|
||||
if (!rv) {
|
||||
int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
|
||||
mem_block *b = tvg::malloc<mem_block*>(sizeof(mem_block) + capacity);
|
||||
if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
|
||||
auto b = tvg::malloc<mem_block*>(sizeof(mem_block) + capacity);
|
||||
b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
|
||||
b->m_used_count = nSize;
|
||||
b->m_size = capacity;
|
||||
|
@ -1142,16 +1132,15 @@ void jpeg_decoder::word_clear(void *p, uint16_t c, uint32_t n)
|
|||
// Refill the input buffer.
|
||||
// This method will sit in a loop until (A) the buffer is full or (B)
|
||||
// the stream's read() method reports and end of file condition.
|
||||
void jpeg_decoder::prep_in_buffer()
|
||||
bool jpeg_decoder::prep_in_buffer()
|
||||
{
|
||||
m_in_buf_left = 0;
|
||||
m_pIn_buf_ofs = m_in_buf;
|
||||
|
||||
if (m_eof_flag) return;
|
||||
if (m_eof_flag) return true;
|
||||
|
||||
do {
|
||||
int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
|
||||
if (bytes_read == -1) stop_decoding(JPGD_STREAM_READ);
|
||||
auto bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
|
||||
if (bytes_read == -1) return stop_decoding(JPGD_STREAM_READ);
|
||||
m_in_buf_left += bytes_read;
|
||||
} while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
|
||||
|
||||
|
@ -1160,18 +1149,19 @@ void jpeg_decoder::prep_in_buffer()
|
|||
// Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
|
||||
// (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
|
||||
word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Read a Huffman code table.
|
||||
void jpeg_decoder::read_dht_marker()
|
||||
bool jpeg_decoder::read_dht_marker()
|
||||
{
|
||||
int i, index, count;
|
||||
uint8_t huff_num[17];
|
||||
uint8_t huff_val[256];
|
||||
uint32_t num_left = get_bits(16);
|
||||
|
||||
if (num_left < 2) stop_decoding(JPGD_BAD_DHT_MARKER);
|
||||
if (num_left < 2) return stop_decoding(JPGD_BAD_DHT_MARKER);
|
||||
num_left -= 2;
|
||||
|
||||
while (num_left) {
|
||||
|
@ -1184,19 +1174,19 @@ void jpeg_decoder::read_dht_marker()
|
|||
count += huff_num[i];
|
||||
}
|
||||
|
||||
if (count > 255) stop_decoding(JPGD_BAD_DHT_COUNTS);
|
||||
if (count > 255) return stop_decoding(JPGD_BAD_DHT_COUNTS);
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
huff_val[i] = static_cast<uint8_t>(get_bits(8));
|
||||
|
||||
i = 1 + 16 + count;
|
||||
|
||||
if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DHT_MARKER);
|
||||
if (num_left < (uint32_t)i) return stop_decoding(JPGD_BAD_DHT_MARKER);
|
||||
num_left -= i;
|
||||
|
||||
if ((index & 0x10) > 0x10) stop_decoding(JPGD_BAD_DHT_INDEX);
|
||||
if ((index & 0x10) > 0x10) return stop_decoding(JPGD_BAD_DHT_INDEX);
|
||||
index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
|
||||
if (index >= JPGD_MAX_HUFF_TABLES) stop_decoding(JPGD_BAD_DHT_INDEX);
|
||||
if (index >= JPGD_MAX_HUFF_TABLES) return stop_decoding(JPGD_BAD_DHT_INDEX);
|
||||
|
||||
if (!m_huff_num[index]) m_huff_num[index] = (uint8_t *)alloc(17);
|
||||
if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256);
|
||||
|
@ -1205,16 +1195,17 @@ void jpeg_decoder::read_dht_marker()
|
|||
memcpy(m_huff_num[index], huff_num, 17);
|
||||
memcpy(m_huff_val[index], huff_val, 256);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Read a quantization table.
|
||||
void jpeg_decoder::read_dqt_marker()
|
||||
bool jpeg_decoder::read_dqt_marker()
|
||||
{
|
||||
int n, i, prec;
|
||||
uint32_t temp;
|
||||
uint32_t num_left = get_bits(16);
|
||||
if (num_left < 2) stop_decoding(JPGD_BAD_DQT_MARKER);
|
||||
if (num_left < 2) return stop_decoding(JPGD_BAD_DQT_MARKER);
|
||||
num_left -= 2;
|
||||
|
||||
while (num_left) {
|
||||
|
@ -1222,7 +1213,7 @@ void jpeg_decoder::read_dqt_marker()
|
|||
prec = n >> 4;
|
||||
n &= 0x0F;
|
||||
|
||||
if (n >= JPGD_MAX_QUANT_TABLES) stop_decoding(JPGD_BAD_DQT_TABLE);
|
||||
if (n >= JPGD_MAX_QUANT_TABLES) return stop_decoding(JPGD_BAD_DQT_TABLE);
|
||||
|
||||
if (!m_quant[n]) m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t));
|
||||
|
||||
|
@ -1234,30 +1225,31 @@ void jpeg_decoder::read_dqt_marker()
|
|||
}
|
||||
i = 64 + 1;
|
||||
if (prec) i += 64;
|
||||
if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DQT_LENGTH);
|
||||
if (num_left < (uint32_t)i) return stop_decoding(JPGD_BAD_DQT_LENGTH);
|
||||
num_left -= i;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Read the start of frame (SOF) marker.
|
||||
void jpeg_decoder::read_sof_marker()
|
||||
bool jpeg_decoder::read_sof_marker()
|
||||
{
|
||||
int i;
|
||||
uint32_t num_left = get_bits(16);
|
||||
|
||||
if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */
|
||||
if (get_bits(8) != 8) return stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */
|
||||
|
||||
m_image_y_size = get_bits(16);
|
||||
if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT);
|
||||
if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) return stop_decoding(JPGD_BAD_HEIGHT);
|
||||
|
||||
m_image_x_size = get_bits(16);
|
||||
if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) stop_decoding(JPGD_BAD_WIDTH);
|
||||
if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) return stop_decoding(JPGD_BAD_WIDTH);
|
||||
|
||||
m_comps_in_frame = get_bits(8);
|
||||
if (m_comps_in_frame > JPGD_MAX_COMPONENTS) stop_decoding(JPGD_TOO_MANY_COMPONENTS);
|
||||
if (m_comps_in_frame > JPGD_MAX_COMPONENTS) return stop_decoding(JPGD_TOO_MANY_COMPONENTS);
|
||||
|
||||
if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) stop_decoding(JPGD_BAD_SOF_LENGTH);
|
||||
if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) return stop_decoding(JPGD_BAD_SOF_LENGTH);
|
||||
|
||||
for (i = 0; i < m_comps_in_frame; i++) {
|
||||
m_comp_ident[i] = get_bits(8);
|
||||
|
@ -1265,33 +1257,36 @@ void jpeg_decoder::read_sof_marker()
|
|||
m_comp_v_samp[i] = get_bits(4);
|
||||
m_comp_quant[i] = get_bits(8);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Used to skip unrecognized markers.
|
||||
void jpeg_decoder::skip_variable_marker()
|
||||
bool jpeg_decoder::skip_variable_marker()
|
||||
{
|
||||
uint32_t num_left = get_bits(16);
|
||||
if (num_left < 2) stop_decoding(JPGD_BAD_VARIABLE_MARKER);
|
||||
if (num_left < 2) return stop_decoding(JPGD_BAD_VARIABLE_MARKER);
|
||||
num_left -= 2;
|
||||
|
||||
while (num_left) {
|
||||
get_bits(8);
|
||||
num_left--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Read a define restart interval (DRI) marker.
|
||||
void jpeg_decoder::read_dri_marker()
|
||||
bool jpeg_decoder::read_dri_marker()
|
||||
{
|
||||
if (get_bits(16) != 4) stop_decoding(JPGD_BAD_DRI_LENGTH);
|
||||
if (get_bits(16) != 4) return stop_decoding(JPGD_BAD_DRI_LENGTH);
|
||||
m_restart_interval = get_bits(16);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Read a start of scan (SOS) marker.
|
||||
void jpeg_decoder::read_sos_marker()
|
||||
bool jpeg_decoder::read_sos_marker()
|
||||
{
|
||||
int i, ci, c, cc;
|
||||
uint32_t num_left = get_bits(16);
|
||||
|
@ -1300,7 +1295,7 @@ void jpeg_decoder::read_sos_marker()
|
|||
m_comps_in_scan = n;
|
||||
num_left -= 3;
|
||||
|
||||
if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) stop_decoding(JPGD_BAD_SOS_LENGTH);
|
||||
if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) return stop_decoding(JPGD_BAD_SOS_LENGTH);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
cc = get_bits(8);
|
||||
|
@ -1310,7 +1305,7 @@ void jpeg_decoder::read_sos_marker()
|
|||
for (ci = 0; ci < m_comps_in_frame; ci++)
|
||||
if (cc == m_comp_ident[ci]) break;
|
||||
|
||||
if (ci >= m_comps_in_frame) stop_decoding(JPGD_BAD_SOS_COMP_ID);
|
||||
if (ci >= m_comps_in_frame) return stop_decoding(JPGD_BAD_SOS_COMP_ID);
|
||||
|
||||
m_comp_list[i] = ci;
|
||||
m_comp_dc_tab[ci] = (c >> 4) & 15;
|
||||
|
@ -1331,6 +1326,7 @@ void jpeg_decoder::read_sos_marker()
|
|||
get_bits(8);
|
||||
num_left--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1380,21 +1376,21 @@ int jpeg_decoder::process_markers()
|
|||
case M_EOI:
|
||||
case M_SOS: return c;
|
||||
case M_DHT: {
|
||||
read_dht_marker();
|
||||
break;
|
||||
if (read_dht_marker()) break;
|
||||
else return M_EOI;
|
||||
}
|
||||
// No arithmetic support - dumb patents!
|
||||
case M_DAC: {
|
||||
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
|
||||
break;
|
||||
return M_EOI;
|
||||
}
|
||||
case M_DQT: {
|
||||
read_dqt_marker();
|
||||
break;
|
||||
if (read_dqt_marker()) break;
|
||||
else return M_EOI;
|
||||
}
|
||||
case M_DRI: {
|
||||
read_dri_marker();
|
||||
break;
|
||||
if (read_dri_marker()) break;
|
||||
else return M_EOI;
|
||||
}
|
||||
//case M_APP0: /* no need to read the JFIF marker */
|
||||
case M_JPG:
|
||||
|
@ -1408,11 +1404,11 @@ int jpeg_decoder::process_markers()
|
|||
case M_RST7:
|
||||
case M_TEM: {
|
||||
stop_decoding(JPGD_UNEXPECTED_MARKER);
|
||||
break;
|
||||
return M_EOI;
|
||||
}
|
||||
default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
|
||||
skip_variable_marker();
|
||||
break;
|
||||
if (skip_variable_marker()) break;
|
||||
else return M_EOI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1422,71 +1418,62 @@ int jpeg_decoder::process_markers()
|
|||
// Finds the start of image (SOI) marker.
|
||||
// This code is rather defensive: it only checks the first 512 bytes to avoid
|
||||
// false positives.
|
||||
void jpeg_decoder::locate_soi_marker()
|
||||
bool jpeg_decoder::locate_soi_marker()
|
||||
{
|
||||
uint32_t lastchar = get_bits(8);
|
||||
uint32_t thischar = get_bits(8);
|
||||
|
||||
/* ok if it's a normal JPEG file without a special header */
|
||||
if ((lastchar == 0xFF) && (thischar == M_SOI)) return;
|
||||
if ((lastchar == 0xFF) && (thischar == M_SOI)) return true;
|
||||
|
||||
uint32_t bytesleft = 4096; //512;
|
||||
|
||||
while (true) {
|
||||
if (--bytesleft == 0) stop_decoding(JPGD_NOT_JPEG);
|
||||
if (--bytesleft == 0) return stop_decoding(JPGD_NOT_JPEG);
|
||||
|
||||
lastchar = thischar;
|
||||
thischar = get_bits(8);
|
||||
|
||||
if (lastchar == 0xFF) {
|
||||
if (thischar == M_SOI) break;
|
||||
else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
|
||||
else if (thischar == M_EOI) return stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
|
||||
}
|
||||
}
|
||||
|
||||
// Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
|
||||
thischar = (m_bit_buf >> 24) & 0xFF;
|
||||
if (thischar != 0xFF) stop_decoding(JPGD_NOT_JPEG);
|
||||
if (thischar != 0xFF) return stop_decoding(JPGD_NOT_JPEG);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Find a start of frame (SOF) marker.
|
||||
void jpeg_decoder::locate_sof_marker()
|
||||
bool jpeg_decoder::locate_sof_marker()
|
||||
{
|
||||
locate_soi_marker();
|
||||
int c = process_markers();
|
||||
|
||||
switch (c) {
|
||||
if (!locate_soi_marker()) return false;
|
||||
switch (process_markers()) {
|
||||
case M_SOF2: {
|
||||
m_progressive_flag = true;
|
||||
read_sof_marker();
|
||||
break;
|
||||
return read_sof_marker();
|
||||
}
|
||||
case M_SOF0: /* baseline DCT */
|
||||
case M_SOF1: { /* extended sequential DCT */
|
||||
read_sof_marker();
|
||||
break;
|
||||
}
|
||||
case M_SOF9: { /* Arithmetic coding */
|
||||
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
stop_decoding(JPGD_UNSUPPORTED_MARKER);
|
||||
break;
|
||||
return read_sof_marker();
|
||||
}
|
||||
case M_SOF9: return stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
|
||||
default: return stop_decoding(JPGD_UNSUPPORTED_MARKER);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Find a start of scan (SOS) marker.
|
||||
int jpeg_decoder::locate_sos_marker()
|
||||
bool jpeg_decoder::locate_sos_marker()
|
||||
{
|
||||
int c = process_markers();
|
||||
auto c = process_markers();
|
||||
if (c == M_EOI) return false;
|
||||
else if (c != M_SOS) stop_decoding(JPGD_UNEXPECTED_MARKER);
|
||||
read_sos_marker();
|
||||
return true;
|
||||
else if (c != M_SOS) return stop_decoding(JPGD_UNEXPECTED_MARKER);
|
||||
return read_sos_marker();
|
||||
}
|
||||
|
||||
|
||||
|
@ -1828,7 +1815,7 @@ void jpeg_decoder::load_next_row()
|
|||
|
||||
|
||||
// Restart interval processing.
|
||||
void jpeg_decoder::process_restart()
|
||||
bool jpeg_decoder::process_restart()
|
||||
{
|
||||
int i;
|
||||
int c = 0;
|
||||
|
@ -1842,15 +1829,15 @@ void jpeg_decoder::process_restart()
|
|||
for (i = 1536; i > 0; i--) {
|
||||
if (get_char() == 0xFF) break;
|
||||
}
|
||||
if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
|
||||
if (i == 0) return stop_decoding(JPGD_BAD_RESTART_MARKER);
|
||||
|
||||
for ( ; i > 0; i--) {
|
||||
if ((c = get_char()) != 0xFF) break;
|
||||
}
|
||||
if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
|
||||
if (i == 0) return stop_decoding(JPGD_BAD_RESTART_MARKER);
|
||||
|
||||
// Is it the expected marker? If not, something bad happened.
|
||||
if (c != (m_next_restart_num + M_RST0)) stop_decoding(JPGD_BAD_RESTART_MARKER);
|
||||
if (c != (m_next_restart_num + M_RST0)) return stop_decoding(JPGD_BAD_RESTART_MARKER);
|
||||
|
||||
// Reset each component's DC prediction values.
|
||||
memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
|
||||
|
@ -1863,6 +1850,8 @@ void jpeg_decoder::process_restart()
|
|||
m_bits_left = 16;
|
||||
get_bits_no_markers(16);
|
||||
get_bits_no_markers(16);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1873,10 +1862,12 @@ static inline int dequantize_ac(int c, int q)
|
|||
}
|
||||
|
||||
// Decodes and dequantizes the next row of coefficients.
|
||||
void jpeg_decoder::decode_next_row()
|
||||
bool jpeg_decoder::decode_next_row()
|
||||
{
|
||||
for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
|
||||
if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
|
||||
if ((m_restart_interval) && (m_restarts_left == 0)) {
|
||||
if (!process_restart()) return false;
|
||||
}
|
||||
|
||||
jpgd_block_t* p = m_pMCU_coefficients;
|
||||
|
||||
|
@ -1903,7 +1894,7 @@ void jpeg_decoder::decode_next_row()
|
|||
|
||||
if (s) {
|
||||
if (r) {
|
||||
if ((k + r) > 63) stop_decoding(JPGD_DECODE_ERROR);
|
||||
if ((k + r) > 63) return stop_decoding(JPGD_DECODE_ERROR);
|
||||
if (k < prev_num_set) {
|
||||
int n = JPGD_MIN(r, prev_num_set - k);
|
||||
int kt = k;
|
||||
|
@ -1916,7 +1907,7 @@ void jpeg_decoder::decode_next_row()
|
|||
p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
|
||||
} else {
|
||||
if (r == 15) {
|
||||
if ((k + 16) > 64) stop_decoding(JPGD_DECODE_ERROR);
|
||||
if ((k + 16) > 64) return stop_decoding(JPGD_DECODE_ERROR);
|
||||
if (k < prev_num_set) {
|
||||
int n = JPGD_MIN(16, prev_num_set - k);
|
||||
int kt = k;
|
||||
|
@ -1942,6 +1933,7 @@ void jpeg_decoder::decode_next_row()
|
|||
else transform_mcu(mcu_row);
|
||||
m_restarts_left--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2183,9 +2175,8 @@ int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len)
|
|||
if ((m_error_code) || (!m_ready_flag)) return JPGD_FAILED;
|
||||
if (m_total_lines_left == 0) return JPGD_DONE;
|
||||
if (m_mcu_lines_left == 0) {
|
||||
if (setjmp(m_jmp_state)) return JPGD_FAILED;
|
||||
if (m_progressive_flag) load_next_row();
|
||||
else decode_next_row();
|
||||
else if (!decode_next_row()) return JPGD_FAILED;
|
||||
// Find the EOI marker if that was the last row.
|
||||
if (m_total_lines_left <= m_max_mcu_y_size) find_eoi();
|
||||
m_mcu_lines_left = m_max_mcu_y_size;
|
||||
|
@ -2341,20 +2332,21 @@ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
|
|||
|
||||
|
||||
// Verifies the quantization tables needed for this scan are available.
|
||||
void jpeg_decoder::check_quant_tables()
|
||||
bool jpeg_decoder::check_quant_tables()
|
||||
{
|
||||
for (int i = 0; i < m_comps_in_scan; i++) {
|
||||
if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr) stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
|
||||
if (!m_quant[m_comp_quant[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Verifies that all the Huffman tables needed for this scan are available.
|
||||
void jpeg_decoder::check_huff_tables()
|
||||
bool jpeg_decoder::check_huff_tables()
|
||||
{
|
||||
for (int i = 0; i < m_comps_in_scan; i++) {
|
||||
if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
|
||||
if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
|
||||
if ((m_spectral_start == 0) && !m_huff_num[m_comp_dc_tab[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
|
||||
if ((m_spectral_end > 0) && !m_huff_num[m_comp_ac_tab[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) {
|
||||
|
@ -2363,6 +2355,7 @@ void jpeg_decoder::check_huff_tables()
|
|||
make_huff_table(i, m_pHuff_tabs[i]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2418,7 +2411,7 @@ int jpeg_decoder::init_scan()
|
|||
|
||||
calc_mcu_block_order();
|
||||
check_huff_tables();
|
||||
check_quant_tables();
|
||||
if (!check_quant_tables()) return false;
|
||||
|
||||
memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
|
||||
|
||||
|
@ -2435,19 +2428,20 @@ int jpeg_decoder::init_scan()
|
|||
|
||||
// Starts a frame. Determines if the number of components or sampling factors
|
||||
// are supported.
|
||||
void jpeg_decoder::init_frame()
|
||||
bool jpeg_decoder::init_frame()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (m_comps_in_frame == 1) {
|
||||
if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
|
||||
if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
|
||||
m_scan_type = JPGD_GRAYSCALE;
|
||||
m_max_blocks_per_mcu = 1;
|
||||
m_max_mcu_x_size = 8;
|
||||
m_max_mcu_y_size = 8;
|
||||
} else if (m_comps_in_frame == 3) {
|
||||
if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)))
|
||||
stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
|
||||
if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1))) {
|
||||
return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
|
||||
}
|
||||
|
||||
if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) {
|
||||
m_scan_type = JPGD_YH1V1;
|
||||
|
@ -2469,8 +2463,8 @@ void jpeg_decoder::init_frame()
|
|||
m_max_blocks_per_mcu = 6;
|
||||
m_max_mcu_x_size = 16;
|
||||
m_max_mcu_y_size = 16;
|
||||
} else stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
|
||||
} else stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
|
||||
} else return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
|
||||
} else return stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
|
||||
|
||||
m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
|
||||
m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
|
||||
|
@ -2491,7 +2485,7 @@ void jpeg_decoder::init_frame()
|
|||
m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
|
||||
|
||||
// Should never happen
|
||||
if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) stop_decoding(JPGD_ASSERTION_ERROR);
|
||||
if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) return stop_decoding(JPGD_ASSERTION_ERROR);
|
||||
|
||||
// Allocate the coefficient buffer, enough for one MCU
|
||||
m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
|
||||
|
@ -2517,6 +2511,8 @@ void jpeg_decoder::init_frame()
|
|||
m_total_lines_left = m_image_y_size;
|
||||
m_mcu_lines_left = 0;
|
||||
create_look_ups();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2546,7 +2542,7 @@ inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, in
|
|||
|
||||
// The following methods decode the various types of m_blocks encountered
|
||||
// in progressively encoded images.
|
||||
void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
bool jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
{
|
||||
int s, r;
|
||||
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
|
||||
|
@ -2557,25 +2553,28 @@ void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int
|
|||
}
|
||||
pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
|
||||
p[0] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
bool jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
{
|
||||
if (pD->get_bits_no_markers(1)) {
|
||||
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
|
||||
p[0] |= (1 << pD->m_successive_low);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
bool jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
{
|
||||
int k, s, r;
|
||||
|
||||
if (pD->m_eob_run) {
|
||||
pD->m_eob_run--;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
|
||||
|
||||
|
@ -2584,13 +2583,13 @@ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int
|
|||
r = s >> 4;
|
||||
s &= 15;
|
||||
if (s) {
|
||||
if ((k += r) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
|
||||
if ((k += r) > 63) return pD->stop_decoding(JPGD_DECODE_ERROR);
|
||||
r = pD->get_bits_no_markers(s);
|
||||
s = JPGD_HUFF_EXTEND(r, s);
|
||||
p[g_ZAG[k]] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
|
||||
} else {
|
||||
if (r == 15) {
|
||||
if ((k += 15) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
|
||||
if ((k += 15) > 63) return pD->stop_decoding(JPGD_DECODE_ERROR);
|
||||
} else {
|
||||
pD->m_eob_run = 1 << r;
|
||||
if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
|
||||
|
@ -2599,10 +2598,11 @@ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int
|
|||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
bool jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
|
||||
{
|
||||
int s, k, r;
|
||||
int p1 = 1 << pD->m_successive_low;
|
||||
|
@ -2619,7 +2619,7 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
|
|||
r = s >> 4;
|
||||
s &= 15;
|
||||
if (s) {
|
||||
if (s != 1) pD->stop_decoding(JPGD_DECODE_ERROR);
|
||||
if (s != 1) return pD->stop_decoding(JPGD_DECODE_ERROR);
|
||||
if (pD->get_bits_no_markers(1)) s = p1;
|
||||
else s = m1;
|
||||
} else {
|
||||
|
@ -2667,11 +2667,12 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
|
|||
}
|
||||
pD->m_eob_run--;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Decode a scan in a progressively encoded image.
|
||||
void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
|
||||
bool jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
|
||||
{
|
||||
int mcu_row, mcu_col, mcu_block;
|
||||
int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS];
|
||||
|
@ -2685,11 +2686,13 @@ void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
|
|||
for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
|
||||
int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
|
||||
|
||||
if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
|
||||
if ((m_restart_interval) && (m_restarts_left == 0)) {
|
||||
if (!process_restart()) return false;
|
||||
}
|
||||
|
||||
for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
|
||||
component_id = m_mcu_org[mcu_block];
|
||||
decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
|
||||
if (!decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs)) return false;
|
||||
|
||||
if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
|
||||
else {
|
||||
|
@ -2714,15 +2717,16 @@ void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
|
|||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Decode a progressively encoded image.
|
||||
void jpeg_decoder::init_progressive()
|
||||
bool jpeg_decoder::init_progressive()
|
||||
{
|
||||
int i;
|
||||
|
||||
if (m_comps_in_frame == 4) stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
|
||||
if (m_comps_in_frame == 4) return stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
|
||||
|
||||
// Allocate the coefficient buffers.
|
||||
for (i = 0; i < m_comps_in_frame; i++) {
|
||||
|
@ -2739,15 +2743,13 @@ void jpeg_decoder::init_progressive()
|
|||
dc_only_scan = (m_spectral_start == 0);
|
||||
refinement_scan = (m_successive_high != 0);
|
||||
|
||||
if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
|
||||
if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) return stop_decoding(JPGD_BAD_SOS_SPECTRAL);
|
||||
|
||||
if (dc_only_scan) {
|
||||
if (m_spectral_end) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
|
||||
} else if (m_comps_in_scan != 1) { /* AC scans can only contain one component */
|
||||
stop_decoding(JPGD_BAD_SOS_SPECTRAL);
|
||||
}
|
||||
if (m_spectral_end) return stop_decoding(JPGD_BAD_SOS_SPECTRAL);
|
||||
} else if (m_comps_in_scan != 1) return stop_decoding(JPGD_BAD_SOS_SPECTRAL); // AC scans can only contain one component
|
||||
|
||||
if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
|
||||
if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) return stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
|
||||
|
||||
if (dc_only_scan) {
|
||||
if (refinement_scan) decode_block_func = decode_block_dc_refine;
|
||||
|
@ -2756,7 +2758,7 @@ void jpeg_decoder::init_progressive()
|
|||
if (refinement_scan) decode_block_func = decode_block_ac_refine;
|
||||
else decode_block_func = decode_block_ac_first;
|
||||
}
|
||||
decode_scan(decode_block_func);
|
||||
if (!decode_scan(decode_block_func)) return false;
|
||||
m_bits_left = 16;
|
||||
get_bits(16);
|
||||
get_bits(16);
|
||||
|
@ -2769,20 +2771,23 @@ void jpeg_decoder::init_progressive()
|
|||
}
|
||||
|
||||
calc_mcu_block_order();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void jpeg_decoder::init_sequential()
|
||||
bool jpeg_decoder::init_sequential()
|
||||
{
|
||||
if (!init_scan()) stop_decoding(JPGD_UNEXPECTED_MARKER);
|
||||
if (!init_scan()) return stop_decoding(JPGD_UNEXPECTED_MARKER);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void jpeg_decoder::decode_start()
|
||||
bool jpeg_decoder::decode_start()
|
||||
{
|
||||
init_frame();
|
||||
if (m_progressive_flag) init_progressive();
|
||||
else init_sequential();
|
||||
if (!init_frame()) return false;
|
||||
if (m_progressive_flag) return init_progressive();
|
||||
return init_sequential();
|
||||
}
|
||||
|
||||
|
||||
|
@ -2795,7 +2800,6 @@ void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
|
|||
|
||||
jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
|
||||
{
|
||||
if (setjmp(m_jmp_state)) return;
|
||||
decode_init(pStream);
|
||||
}
|
||||
|
||||
|
@ -2804,9 +2808,7 @@ int jpeg_decoder::begin_decoding()
|
|||
{
|
||||
if (m_ready_flag) return JPGD_SUCCESS;
|
||||
if (m_error_code) return JPGD_FAILED;
|
||||
if (setjmp(m_jmp_state)) return JPGD_FAILED;
|
||||
|
||||
decode_start();
|
||||
if (!decode_start()) return JPGD_FAILED;
|
||||
m_ready_flag = true;
|
||||
|
||||
return JPGD_SUCCESS;
|
||||
|
@ -2970,7 +2972,7 @@ unsigned char* jpgdDecompress(jpeg_decoder* decoder)
|
|||
if (!pImage_data) return nullptr;
|
||||
|
||||
for (int y = 0; y < image_height; y++) {
|
||||
const uint8_t* pScan_line;
|
||||
const uint8_t* pScan_line = nullptr;
|
||||
uint32_t scan_line_len;
|
||||
if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) {
|
||||
tvg::free(pImage_data);
|
||||
|
|
|
@ -352,13 +352,11 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
|
|||
|
||||
for (int i = 0; i < repeater->cnt; ++i) {
|
||||
auto multiplier = repeater->offset + static_cast<float>(i);
|
||||
|
||||
ARRAY_FOREACH(p, propagators) {
|
||||
auto shape = static_cast<Shape*>((*p)->duplicate());
|
||||
SHAPE(shape)->rs.path = SHAPE(path)->rs.path;
|
||||
|
||||
auto opacity = repeater->interpOpacity ? tvg::lerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity;
|
||||
shape->opacity(opacity);
|
||||
auto opacity = tvg::lerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt);
|
||||
shape->opacity(MULTIPLY((*p)->opacity(), opacity));
|
||||
|
||||
Matrix m;
|
||||
tvg::identity(&m);
|
||||
|
@ -366,11 +364,10 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
|
|||
scale(&m, {powf(repeater->scale.x * 0.01f, multiplier), powf(repeater->scale.y * 0.01f, multiplier)});
|
||||
rotate(&m, repeater->rotation * multiplier);
|
||||
translateR(&m, -repeater->anchor);
|
||||
m = repeater->transform * m;
|
||||
|
||||
Matrix inv;
|
||||
inverse(&repeater->transform, &inv);
|
||||
shape->transform(m * (inv * shape->transform()));
|
||||
shape->transform((repeater->transform * m) * (inv * shape->transform()));
|
||||
shapes.push(shape);
|
||||
}
|
||||
}
|
||||
|
@ -613,7 +610,7 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
|||
auto radius = star->outerRadius(frameNo, tween, exps);
|
||||
auto outerRoundness = star->outerRoundness(frameNo, tween, exps) * 0.01f;
|
||||
|
||||
auto angle = deg2rad(-90.0f);
|
||||
auto angle = -MATH_PI2;
|
||||
auto anglePerPoint = 2.0f * MATH_PI / float(ptsCnt);
|
||||
auto direction = star->clockwise ? 1.0f : -1.0f;
|
||||
auto hasRoundness = !tvg::zero(outerRoundness);
|
||||
|
@ -641,6 +638,7 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
|||
auto in = Point{x, y} * transform;
|
||||
shape->moveTo(in.x, in.y);
|
||||
|
||||
auto coeff = anglePerPoint * radius * outerRoundness * POLYGON_MAGIC_NUMBER;
|
||||
for (size_t i = 0; i < ptsCnt; i++) {
|
||||
auto previousX = x;
|
||||
auto previousY = y;
|
||||
|
@ -649,16 +647,11 @@ void LottieBuilder::updatePolygon(LottieGroup* parent, LottiePolyStar* star, flo
|
|||
|
||||
if (hasRoundness) {
|
||||
auto cp1Theta = tvg::atan2(previousY, previousX) - MATH_PI2 * direction;
|
||||
auto cp1Dx = cosf(cp1Theta);
|
||||
auto cp1Dy = sinf(cp1Theta);
|
||||
auto cp1x = coeff * cosf(cp1Theta);
|
||||
auto cp1y = coeff * sinf(cp1Theta);
|
||||
auto cp2Theta = tvg::atan2(y, x) - MATH_PI2 * direction;
|
||||
auto cp2Dx = cosf(cp2Theta);
|
||||
auto cp2Dy = sinf(cp2Theta);
|
||||
|
||||
auto cp1x = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp1Dx;
|
||||
auto cp1y = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp1Dy;
|
||||
auto cp2x = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp2Dx;
|
||||
auto cp2y = radius * outerRoundness * POLYGON_MAGIC_NUMBER * cp2Dy;
|
||||
auto cp2x = coeff * cosf(cp2Theta);
|
||||
auto cp2y = coeff * sinf(cp2Theta);
|
||||
|
||||
auto in2 = Point{previousX - cp1x, previousY - cp1y} * transform;
|
||||
auto in3 = Point{x + cp2x, y + cp2y} * transform;
|
||||
|
@ -743,7 +736,6 @@ void LottieBuilder::updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject*
|
|||
r.startOpacity = repeater->startOpacity(frameNo, tween, exps);
|
||||
r.endOpacity = repeater->endOpacity(frameNo, tween, exps);
|
||||
r.inorder = repeater->inorder;
|
||||
r.interpOpacity = (r.startOpacity == r.endOpacity) ? false : true;
|
||||
ctx->repeaters.push(r);
|
||||
|
||||
ctx->merging = nullptr;
|
||||
|
@ -938,7 +930,7 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
|
|||
|
||||
if (!p || !text->font) return;
|
||||
|
||||
if (text->font->origin != LottieFont::Origin::Embedded) {
|
||||
if (text->font->origin != LottieFont::Origin::Local || text->font->chars.empty()) {
|
||||
_fontText(doc, layer->scene);
|
||||
return;
|
||||
}
|
||||
|
@ -1178,8 +1170,8 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
|
|||
{
|
||||
if (layer->masks.count == 0) return;
|
||||
|
||||
//Introduce an intermediate scene for embracing the matte + masking
|
||||
if (layer->matteTarget) {
|
||||
//Introduce an intermediate scene for embracing matte + masking or precomp clipping + masking replaced by clipping
|
||||
if (layer->matteTarget || layer->type == LottieLayer::Precomp) {
|
||||
auto scene = Scene::gen();
|
||||
scene->push(layer->scene);
|
||||
layer->scene = scene;
|
||||
|
@ -1196,7 +1188,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
|
|||
auto method = mask->method;
|
||||
auto opacity = mask->opacity(frameNo);
|
||||
auto expand = mask->expand(frameNo);
|
||||
auto fastTrack = false; //single clipping
|
||||
|
||||
//the first mask
|
||||
if (!pShape) {
|
||||
|
@ -1207,7 +1198,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
|
|||
if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha) {
|
||||
layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity));
|
||||
layer->scene->clip(pShape);
|
||||
fastTrack = true;
|
||||
} else {
|
||||
layer->scene->mask(pShape, compMethod);
|
||||
}
|
||||
|
@ -1233,9 +1223,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
|
|||
auto offset = LottieOffsetModifier(expand);
|
||||
mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, tween, exps, &offset);
|
||||
}
|
||||
|
||||
if (fastTrack) return;
|
||||
|
||||
pOpacity = opacity;
|
||||
pMethod = method;
|
||||
}
|
||||
|
@ -1245,7 +1232,7 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
|
|||
bool LottieBuilder::updateMatte(LottieComposition* comp, float frameNo, Scene* scene, LottieLayer* layer)
|
||||
{
|
||||
auto target = layer->matteTarget;
|
||||
if (!target) return true;
|
||||
if (!target || target->type == LottieLayer::Null) return true;
|
||||
|
||||
updateLayer(comp, scene, target, frameNo);
|
||||
|
||||
|
|
|
@ -42,7 +42,6 @@ struct RenderRepeater
|
|||
float rotation;
|
||||
uint8_t startOpacity;
|
||||
uint8_t endOpacity;
|
||||
bool interpOpacity;
|
||||
bool inorder;
|
||||
};
|
||||
|
||||
|
|
|
@ -80,36 +80,47 @@ float LottieTextFollowPath::prepare(LottieMask* mask, float frameNo, float scale
|
|||
|
||||
Point LottieTextFollowPath::position(float lenSearched, float& angle)
|
||||
{
|
||||
//position before the start of the curve
|
||||
if (lenSearched <= 0.0f) {
|
||||
//shape is closed -> wrapping
|
||||
if (path.cmds.last() == PathCommand::Close) {
|
||||
while (lenSearched < 0.0f) lenSearched += totalLen;
|
||||
pts = path.pts.data;
|
||||
cmds = path.cmds.data;
|
||||
cmdsCnt = path.cmds.count;
|
||||
currentLen = 0.0f;
|
||||
//linear interpolation
|
||||
} else {
|
||||
if (cmds >= path.cmds.data + path.cmds.count - 1) return *start;
|
||||
switch (*(cmds + 1)) {
|
||||
case PathCommand::LineTo: {
|
||||
auto dp = *(pts + 1) - *pts;
|
||||
angle = tvg::atan2(dp.y, dp.x);
|
||||
return {pts->x + lenSearched * cos(angle), pts->y + lenSearched * sin(angle)};
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
angle = deg2rad(Bezier{*pts, *(pts + 1), *(pts + 2), *(pts + 3)}.angle(0.0001f));
|
||||
return {pts->x + lenSearched * cos(angle), pts->y + lenSearched * sin(angle)};
|
||||
}
|
||||
default:
|
||||
angle = 0.0f;
|
||||
return *start;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto shift = [&]() -> void {
|
||||
switch (*cmds) {
|
||||
case PathCommand::MoveTo:
|
||||
start = pts;
|
||||
++pts;
|
||||
break;
|
||||
case PathCommand::LineTo:
|
||||
++pts;
|
||||
break;
|
||||
case PathCommand::CubicTo:
|
||||
pts += 3;
|
||||
break;
|
||||
case PathCommand::Close:
|
||||
break;
|
||||
case PathCommand::MoveTo: start = pts; ++pts; break;
|
||||
case PathCommand::LineTo: ++pts; break;
|
||||
case PathCommand::CubicTo: pts += 3; break;
|
||||
case PathCommand::Close: break;
|
||||
}
|
||||
++cmds;
|
||||
--cmdsCnt;
|
||||
};
|
||||
|
||||
auto length = [&]() -> float {
|
||||
switch (*cmds) {
|
||||
case PathCommand::MoveTo: return 0.0f;
|
||||
case PathCommand::LineTo: return tvg::length(pts - 1, pts);
|
||||
case PathCommand::CubicTo: return Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length();
|
||||
case PathCommand::Close: return tvg::length(pts - 1, start);
|
||||
}
|
||||
return 0.0f;
|
||||
};
|
||||
|
||||
//beyond the curve
|
||||
//position beyond the end of the curve
|
||||
if (lenSearched >= totalLen) {
|
||||
//shape is closed -> wrapping
|
||||
if (path.cmds.last() == PathCommand::Close) {
|
||||
|
@ -146,6 +157,24 @@ Point LottieTextFollowPath::position(float lenSearched, float& angle)
|
|||
}
|
||||
}
|
||||
|
||||
//reset required if text partially crosses curve start
|
||||
if (lenSearched < currentLen) {
|
||||
pts = path.pts.data;
|
||||
cmds = path.cmds.data;
|
||||
cmdsCnt = path.cmds.count;
|
||||
currentLen = 0.0f;
|
||||
}
|
||||
|
||||
auto length = [&]() -> float {
|
||||
switch (*cmds) {
|
||||
case PathCommand::MoveTo: return 0.0f;
|
||||
case PathCommand::LineTo: return tvg::length(pts - 1, pts);
|
||||
case PathCommand::CubicTo: return Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length();
|
||||
case PathCommand::Close: return tvg::length(pts - 1, start);
|
||||
default: return 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
while (cmdsCnt > 0) {
|
||||
auto dLen = length();
|
||||
if (currentLen + dLen < lenSearched) {
|
||||
|
@ -664,7 +693,7 @@ void LottieLayer::prepare(RGB24* color)
|
|||
|
||||
float LottieLayer::remap(LottieComposition* comp, float frameNo, LottieExpressions* exp)
|
||||
{
|
||||
if (timeRemap.frames || timeRemap.value) {
|
||||
if (timeRemap.frames || timeRemap.value >= 0.0f) {
|
||||
frameNo = comp->frameAtTime(timeRemap(frameNo, exp));
|
||||
} else {
|
||||
frameNo -= startFrame;
|
||||
|
|
|
@ -386,7 +386,7 @@ struct LottieTextRange
|
|||
|
||||
struct LottieFont
|
||||
{
|
||||
enum Origin : uint8_t { Local = 0, CssURL, ScriptURL, FontURL, Embedded };
|
||||
enum Origin : uint8_t {Local = 0, CssURL, ScriptURL, FontURL};
|
||||
|
||||
~LottieFont()
|
||||
{
|
||||
|
@ -408,7 +408,7 @@ struct LottieFont
|
|||
char* style = nullptr;
|
||||
size_t dataSize = 0;
|
||||
float ascent = 0.0f;
|
||||
Origin origin = Embedded;
|
||||
Origin origin = Local;
|
||||
|
||||
void prepare();
|
||||
};
|
||||
|
@ -980,7 +980,7 @@ struct LottieLayer : LottieGroup
|
|||
|
||||
char* name = nullptr;
|
||||
LottieLayer* parent = nullptr;
|
||||
LottieFloat timeRemap = 0.0f;
|
||||
LottieFloat timeRemap = -1.0f;
|
||||
LottieLayer* comp = nullptr; //Precompositor, current layer is belonges.
|
||||
LottieTransform* transform = nullptr;
|
||||
Array<LottieMask*> masks;
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static bool _colinear(const Point* p)
|
||||
{
|
||||
return tvg::zero(*p - *(p + 1)) && tvg::zero(*(p + 2) - *(p + 3));
|
||||
}
|
||||
|
||||
|
||||
static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, Point& prev, Point& curr, Point& next, float r)
|
||||
{
|
||||
auto lenPrev = length(prev - curr);
|
||||
|
@ -115,9 +121,12 @@ void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, u
|
|||
auto norm = normal(line.pt1, line.pt2);
|
||||
auto nextNorm = normal(nextLine.pt1, nextLine.pt2);
|
||||
auto miterDirection = (norm + nextNorm) / length(norm + nextNorm);
|
||||
if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) {
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) out.pts.push(intersect);
|
||||
else out.pts.push(nextLine.pt1);
|
||||
out.pts.push(intersect);
|
||||
}
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
out.pts.push(nextLine.pt1);
|
||||
} else {
|
||||
out.cmds.push(PathCommand::LineTo);
|
||||
out.pts.push(nextLine.pt1);
|
||||
|
@ -192,14 +201,10 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt
|
|||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
if (iCmds < inCmdsCnt - 1 && _colinear(inPts + iPts - 1)) {
|
||||
auto& prev = inPts[iPts - 1];
|
||||
auto& curr = inPts[iPts + 2];
|
||||
if (iCmds < inCmdsCnt - 1 &&
|
||||
tvg::zero(inPts[iPts - 1] - inPts[iPts]) &&
|
||||
tvg::zero(inPts[iPts + 1] - inPts[iPts + 2])) {
|
||||
if (inCmds[iCmds + 1] == PathCommand::CubicTo &&
|
||||
tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) &&
|
||||
tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) {
|
||||
if (inCmds[iCmds + 1] == PathCommand::CubicTo && _colinear(inPts + iPts + 2)) {
|
||||
_roundCorner(path.cmds, path.pts, prev, curr, inPts[iPts + 5], r);
|
||||
iPts += 3;
|
||||
break;
|
||||
|
|
|
@ -3401,6 +3401,13 @@ static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content,
|
|||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
|
||||
if (!strncmp(tagName, gradientTags[i].tag, sz)) {
|
||||
loader->gradientStack.pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(graphicsTags) / sizeof(graphicsTags[0]); i++) {
|
||||
if (!strncmp(tagName, graphicsTags[i].tag, sz)) {
|
||||
loader->currentGraphicsNode = nullptr;
|
||||
|
@ -3472,7 +3479,6 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||
if (node->type != SvgNodeType::Defs || !empty) {
|
||||
loader->stack.push(node);
|
||||
}
|
||||
loader->latestGradient = nullptr;
|
||||
} else if ((method = _findGraphicsFactory(tagName))) {
|
||||
if (loader->stack.count > 0) parent = loader->stack.last();
|
||||
else parent = loader->doc;
|
||||
|
@ -3483,10 +3489,11 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||
loader->stack.push(defs);
|
||||
loader->currentGraphicsNode = node;
|
||||
}
|
||||
loader->latestGradient = nullptr;
|
||||
} else if ((gradientMethod = _findGradientFactory(tagName))) {
|
||||
SvgStyleGradient* gradient;
|
||||
gradient = gradientMethod(loader, attrs, attrsLength);
|
||||
//Gradients do not allow nested declarations, so only the earliest declared Gradient is valid.
|
||||
if (loader->gradientStack.count == 0) {
|
||||
//FIXME: The current parsing structure does not distinguish end tags.
|
||||
// There is no way to know if the currently parsed gradient is in defs.
|
||||
// If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
|
||||
|
@ -3498,9 +3505,10 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||
} else {
|
||||
loader->gradients.push(gradient);
|
||||
}
|
||||
loader->latestGradient = gradient;
|
||||
}
|
||||
if (!empty) loader->gradientStack.push(gradient);
|
||||
} else if (STR_AS(tagName, "stop")) {
|
||||
if (!loader->latestGradient) {
|
||||
if (loader->gradientStack.count == 0) {
|
||||
TVGLOG("SVG", "Stop element is used outside of the Gradient element");
|
||||
return;
|
||||
}
|
||||
|
@ -3508,9 +3516,8 @@ static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content,
|
|||
loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
|
||||
loader->svgParse->flags = SvgStopStyleFlags::StopDefault;
|
||||
xmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
|
||||
loader->latestGradient->stops.push(loader->svgParse->gradStop);
|
||||
loader->gradientStack.last()->stops.push(loader->svgParse->gradStop);
|
||||
} else {
|
||||
loader->latestGradient = nullptr;
|
||||
if (!isIgnoreUnsupportedLogElements(tagName)) TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
|
||||
}
|
||||
}
|
||||
|
@ -3831,6 +3838,7 @@ void SvgLoader::clear(bool all)
|
|||
tvg::free(*p);
|
||||
}
|
||||
loaderData.gradients.reset();
|
||||
loaderData.gradientStack.reset();
|
||||
|
||||
_freeNode(loaderData.doc);
|
||||
loaderData.doc = nullptr;
|
||||
|
|
|
@ -590,7 +590,7 @@ struct SvgLoaderData
|
|||
SvgNode* def = nullptr; //also used to store nested graphic nodes
|
||||
SvgNode* cssStyle = nullptr;
|
||||
Array<SvgStyleGradient*> gradients;
|
||||
SvgStyleGradient* latestGradient = nullptr; //For stops
|
||||
Array<SvgStyleGradient*> gradientStack; //For stops
|
||||
SvgParser* svgParse = nullptr;
|
||||
Array<SvgNodeIdPair> cloneNodes;
|
||||
Array<SvgNodeIdPair> nodesToStyle;
|
||||
|
|
|
@ -57,16 +57,17 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
|
|||
|
||||
bool GlGeometry::tesselate(const RenderSurface* image, RenderUpdateFlag flag)
|
||||
{
|
||||
if (flag & RenderUpdateFlag::Image) {
|
||||
if (!(flag & RenderUpdateFlag::Image)) return true;
|
||||
|
||||
fill.clear();
|
||||
|
||||
fill.vertex.reserve(5 * 4);
|
||||
fill.index.reserve(6);
|
||||
|
||||
float left = 0.f;
|
||||
float top = 0.f;
|
||||
float right = image->w;
|
||||
float bottom = image->h;
|
||||
auto left = 0.f;
|
||||
auto top = 0.f;
|
||||
auto right = float(image->w);
|
||||
auto bottom = float(image->h);
|
||||
|
||||
// left top point
|
||||
fill.vertex.push(left);
|
||||
|
@ -101,11 +102,7 @@ bool GlGeometry::tesselate(const RenderSurface* image, RenderUpdateFlag flag)
|
|||
fill.index.push(1);
|
||||
fill.index.push(3);
|
||||
|
||||
bounds.x = 0;
|
||||
bounds.y = 0;
|
||||
bounds.w = image->w;
|
||||
bounds.h = image->h;
|
||||
}
|
||||
bounds = {{0, 0}, {int32_t(image->w), int32_t(image->h)}};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -155,31 +152,20 @@ GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag)
|
|||
|
||||
RenderRegion GlGeometry::getBounds() const
|
||||
{
|
||||
if (tvg::identity(&matrix)) {
|
||||
return bounds;
|
||||
} else {
|
||||
Point lt{static_cast<float>(bounds.x), static_cast<float>(bounds.y)};
|
||||
Point lb{static_cast<float>(bounds.x), static_cast<float>(bounds.y + bounds.h)};
|
||||
Point rt{static_cast<float>(bounds.x + bounds.w), static_cast<float>(bounds.y)};
|
||||
Point rb{static_cast<float>(bounds.x + bounds.w), static_cast<float>(bounds.y + bounds.h)};
|
||||
if (tvg::identity(&matrix)) return bounds;
|
||||
|
||||
lt *= matrix;
|
||||
lb *= matrix;
|
||||
rt *= matrix;
|
||||
rb *= matrix;
|
||||
auto lt = Point{float(bounds.min.x), float(bounds.min.y)} * matrix;
|
||||
auto lb = Point{float(bounds.min.x), float(bounds.max.y)} * matrix;
|
||||
auto rt = Point{float(bounds.max.x), float(bounds.min.y)} * matrix;
|
||||
auto rb = Point{float(bounds.max.x), float(bounds.max.y)} * matrix;
|
||||
|
||||
float left = min(min(lt.x, lb.x), min(rt.x, rb.x));
|
||||
float top = min(min(lt.y, lb.y), min(rt.y, rb.y));
|
||||
float right = max(max(lt.x, lb.x), max(rt.x, rb.x));
|
||||
float bottom = max(max(lt.y, lb.y), max(rt.y, rb.y));
|
||||
auto left = min(min(lt.x, lb.x), min(rt.x, rb.x));
|
||||
auto top = min(min(lt.y, lb.y), min(rt.y, rb.y));
|
||||
auto right = max(max(lt.x, lb.x), max(rt.x, rb.x));
|
||||
auto bottom = max(max(lt.y, lb.y), max(rt.y, rb.y));
|
||||
|
||||
auto bounds = RenderRegion {{int32_t(floor(left)), int32_t(floor(top))}, {int32_t(ceil(right)), int32_t(ceil(bottom))}};
|
||||
if (bounds.valid()) return bounds;
|
||||
return this->bounds;
|
||||
|
||||
auto bounds = RenderRegion {
|
||||
static_cast<int32_t>(floor(left)),
|
||||
static_cast<int32_t>(floor(top)),
|
||||
static_cast<int32_t>(ceil(right - floor(left))),
|
||||
static_cast<int32_t>(ceil(bottom - floor(top))),
|
||||
};
|
||||
if (bounds.w < 0 || bounds.h < 0) return this->bounds;
|
||||
else return bounds;
|
||||
}
|
||||
}
|
|
@ -52,17 +52,17 @@ void GlRenderPass::addRenderTask(GlRenderTask* task)
|
|||
|
||||
void GlRenderPass::getMatrix(float *dst, const Matrix &matrix) const
|
||||
{
|
||||
const auto& vp = getViewport();
|
||||
|
||||
Matrix postMatrix{};
|
||||
tvg::identity(&postMatrix);
|
||||
translate(&postMatrix, {(float)-vp.x, (float)-vp.y});
|
||||
|
||||
const auto& vp = getViewport();
|
||||
translate(&postMatrix, {(float)-vp.sx(), (float)-vp.sy()});
|
||||
|
||||
auto m = postMatrix * matrix;
|
||||
|
||||
float modelMatrix[16];
|
||||
GET_MATRIX44(m, modelMatrix);
|
||||
MVP_MATRIX(vp.w, vp.h);
|
||||
MVP_MATRIX(vp.w(), vp.h());
|
||||
|
||||
MULTIPLY_MATRIX(mvp, modelMatrix, dst);
|
||||
}
|
||||
|
|
|
@ -62,10 +62,7 @@ public:
|
|||
}
|
||||
|
||||
auto task = new T(program, targetFbo, mFbo, std::move(mTasks));
|
||||
|
||||
const auto& vp = mFbo->getViewport();
|
||||
|
||||
task->setRenderSize(static_cast<uint32_t>(vp.w), static_cast<uint32_t>(vp.h));
|
||||
task->setRenderSize(mFbo->getViewport().w(), mFbo->getViewport().h());
|
||||
|
||||
return task;
|
||||
}
|
||||
|
|
|
@ -22,26 +22,20 @@
|
|||
|
||||
#include "tvgGlRenderTarget.h"
|
||||
|
||||
GlRenderTarget::GlRenderTarget(uint32_t width, uint32_t height): mWidth(width), mHeight(height) {}
|
||||
GlRenderTarget::GlRenderTarget() {}
|
||||
|
||||
GlRenderTarget::~GlRenderTarget()
|
||||
{
|
||||
if (mFbo == 0) return;
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
GL_CHECK(glDeleteFramebuffers(1, &mFbo));
|
||||
|
||||
if (mColorTex != 0) {
|
||||
GL_CHECK(glDeleteTextures(1, &mColorTex));
|
||||
}
|
||||
if (mDepthStencilBuffer != 0) {
|
||||
GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer));
|
||||
}
|
||||
reset();
|
||||
}
|
||||
|
||||
void GlRenderTarget::init(GLint resolveId)
|
||||
void GlRenderTarget::init(uint32_t width, uint32_t height, GLint resolveId)
|
||||
{
|
||||
if (mFbo != 0 || mWidth == 0 || mHeight == 0) return;
|
||||
if (mFbo != GL_INVALID_VALUE || width == 0 || height == 0) return;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
|
||||
//TODO: fbo is used. maybe we can consider the direct rendering with resolveId as well.
|
||||
GL_CHECK(glGenFramebuffers(1, &mFbo));
|
||||
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mFbo));
|
||||
|
@ -81,6 +75,18 @@ void GlRenderTarget::init(GLint resolveId)
|
|||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, resolveId));
|
||||
}
|
||||
|
||||
void GlRenderTarget::reset()
|
||||
{
|
||||
if (mFbo == 0) return;
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
GL_CHECK(glDeleteFramebuffers(1, &mFbo));
|
||||
GL_CHECK(glDeleteRenderbuffers(1, &mColorBuffer));
|
||||
GL_CHECK(glDeleteRenderbuffers(1, &mDepthStencilBuffer));
|
||||
GL_CHECK(glDeleteFramebuffers(1, &mResolveFbo));
|
||||
GL_CHECK(glDeleteTextures(1, &mColorTex));
|
||||
mFbo = GL_INVALID_VALUE;
|
||||
}
|
||||
|
||||
GlRenderTargetPool::GlRenderTargetPool(uint32_t maxWidth, uint32_t maxHeight): mMaxWidth(maxWidth), mMaxHeight(maxHeight), mPool() {}
|
||||
|
||||
GlRenderTargetPool::~GlRenderTargetPool()
|
||||
|
@ -101,31 +107,28 @@ uint32_t alignPow2(uint32_t value)
|
|||
|
||||
GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLuint resolveId)
|
||||
{
|
||||
uint32_t width = static_cast<uint32_t>(vp.w);
|
||||
uint32_t height = static_cast<uint32_t>(vp.h);
|
||||
auto width = vp.w();
|
||||
auto height = vp.h();
|
||||
|
||||
// pow2 align width and height
|
||||
if (width >= mMaxWidth) width = mMaxWidth;
|
||||
else width = alignPow2(width);
|
||||
|
||||
if (width >= mMaxWidth) width = mMaxWidth;
|
||||
|
||||
if (height >= mMaxHeight) height = mMaxHeight;
|
||||
else height = alignPow2(height);
|
||||
|
||||
if (height >= mMaxHeight) height = mMaxHeight;
|
||||
|
||||
for (uint32_t i = 0; i < mPool.count; i++) {
|
||||
auto rt = mPool[i];
|
||||
|
||||
if (rt->getWidth() == width && rt->getHeight() == height) {
|
||||
rt->setViewport(vp);
|
||||
return rt;
|
||||
}
|
||||
}
|
||||
|
||||
auto rt = new GlRenderTarget(width, height);
|
||||
rt->init(resolveId);
|
||||
auto rt = new GlRenderTarget();
|
||||
rt->init(width, height, resolveId);
|
||||
rt->setViewport(vp);
|
||||
mPool.push(rt);
|
||||
return rt;
|
||||
|
|
|
@ -28,11 +28,11 @@
|
|||
class GlRenderTarget
|
||||
{
|
||||
public:
|
||||
GlRenderTarget() = default;
|
||||
GlRenderTarget(uint32_t width, uint32_t height);
|
||||
GlRenderTarget();
|
||||
~GlRenderTarget();
|
||||
|
||||
void init(GLint resolveId);
|
||||
void init(uint32_t width, uint32_t height, GLint resolveId);
|
||||
void reset();
|
||||
|
||||
GLuint getFboId() { return mFbo; }
|
||||
GLuint getResolveFboId() { return mResolveFbo; }
|
||||
|
@ -44,12 +44,13 @@ public:
|
|||
void setViewport(const RenderRegion& vp) { mViewport = vp; }
|
||||
const RenderRegion& getViewport() const { return mViewport; }
|
||||
|
||||
bool invalid() const { return mFbo == GL_INVALID_VALUE; }
|
||||
|
||||
private:
|
||||
uint32_t mWidth = 0;
|
||||
uint32_t mHeight = 0;
|
||||
RenderRegion mViewport{};
|
||||
GLuint mFbo = 0;
|
||||
GLuint mFbo = GL_INVALID_VALUE;
|
||||
GLuint mColorBuffer = 0;
|
||||
GLuint mDepthStencilBuffer = 0;
|
||||
GLuint mResolveFbo = 0;
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include "tvgGlProgram.h"
|
||||
#include "tvgGlRenderPass.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* GlRenderTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program)
|
||||
{
|
||||
|
@ -33,6 +36,7 @@ GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(pr
|
|||
mIndexCount = other->mIndexCount;
|
||||
}
|
||||
|
||||
|
||||
void GlRenderTask::run()
|
||||
{
|
||||
// bind shader
|
||||
|
@ -45,7 +49,7 @@ void GlRenderTask::run()
|
|||
}
|
||||
|
||||
// setup scissor rect
|
||||
GL_CHECK(glScissor(mViewport.x, mViewport.y, mViewport.w, mViewport.h));
|
||||
GL_CHECK(glScissor(mViewport.sx(), mViewport.sy(), mViewport.sw(), mViewport.sh()));
|
||||
|
||||
// setup attribute layout
|
||||
for (uint32_t i = 0; i < mVertexLayout.count; i++) {
|
||||
|
@ -81,36 +85,44 @@ void GlRenderTask::run()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void GlRenderTask::addVertexLayout(const GlVertexLayout &layout)
|
||||
{
|
||||
mVertexLayout.push(layout);
|
||||
}
|
||||
|
||||
|
||||
void GlRenderTask::addBindResource(const GlBindingResource &binding)
|
||||
{
|
||||
mBindingResources.push(binding);
|
||||
}
|
||||
|
||||
|
||||
void GlRenderTask::setDrawRange(uint32_t offset, uint32_t count)
|
||||
{
|
||||
mIndexOffset = offset;
|
||||
mIndexCount = count;
|
||||
}
|
||||
|
||||
|
||||
void GlRenderTask::setViewport(const RenderRegion &viewport)
|
||||
{
|
||||
mViewport = viewport;
|
||||
if (mViewport.w < 0) {
|
||||
mViewport.w = 0;
|
||||
}
|
||||
|
||||
if (mViewport.h < 0) {
|
||||
mViewport.h = 0;
|
||||
}
|
||||
if (mViewport.max.x < mViewport.min.x) mViewport.max.x = mViewport.min.x;
|
||||
if (mViewport.max.y < mViewport.min.y) mViewport.max.y = mViewport.min.y;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* GlStencilCoverTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode)
|
||||
:GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover), mStencilMode(mode) {}
|
||||
:GlRenderTask(nullptr), mStencilTask(stencil), mCoverTask(cover), mStencilMode(mode)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
GlStencilCoverTask::~GlStencilCoverTask()
|
||||
{
|
||||
|
@ -118,6 +130,7 @@ GlStencilCoverTask::~GlStencilCoverTask()
|
|||
delete mCoverTask;
|
||||
}
|
||||
|
||||
|
||||
void GlStencilCoverTask::run()
|
||||
{
|
||||
GL_CHECK(glEnable(GL_STENCIL_TEST));
|
||||
|
@ -151,12 +164,18 @@ void GlStencilCoverTask::run()
|
|||
GL_CHECK(glDisable(GL_STENCIL_TEST));
|
||||
}
|
||||
|
||||
|
||||
void GlStencilCoverTask::normalizeDrawDepth(int32_t maxDepth)
|
||||
{
|
||||
mCoverTask->normalizeDrawDepth(maxDepth);
|
||||
mStencilTask->normalizeDrawDepth(maxDepth);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* GlComposeTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks)
|
||||
:GlRenderTask(program) ,mTargetFbo(target), mFbo(fbo), mTasks()
|
||||
{
|
||||
|
@ -164,33 +183,32 @@ GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GlRenderTarget*
|
|||
tasks.clear();
|
||||
}
|
||||
|
||||
|
||||
GlComposeTask::~GlComposeTask()
|
||||
{
|
||||
ARRAY_FOREACH(p, mTasks) delete(*p);
|
||||
mTasks.clear();
|
||||
}
|
||||
|
||||
|
||||
void GlComposeTask::run()
|
||||
{
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getSelfFbo()));
|
||||
|
||||
// clear this fbo
|
||||
if (mClearBuffer) {
|
||||
// we must clear all area of fbo
|
||||
GL_CHECK(glViewport(0, 0, mFbo->getWidth(), mFbo->getHeight()));
|
||||
GL_CHECK(glScissor(0, 0, mFbo->getWidth(), mFbo->getHeight()));
|
||||
GL_CHECK(glClearColor(0, 0, 0, 0));
|
||||
GL_CHECK(glClearStencil(0));
|
||||
#ifdef THORVG_GL_TARGET_GLES
|
||||
#ifdef THORVG_GL_TARGET_GLES
|
||||
GL_CHECK(glClearDepthf(0.0));
|
||||
#else
|
||||
#else
|
||||
GL_CHECK(glClearDepth(0.0));
|
||||
#endif
|
||||
#endif
|
||||
GL_CHECK(glDepthMask(1));
|
||||
|
||||
GL_CHECK(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||
GL_CHECK(glDepthMask(0));
|
||||
}
|
||||
|
||||
GL_CHECK(glViewport(0, 0, mRenderWidth, mRenderHeight));
|
||||
GL_CHECK(glScissor(0, 0, mRenderWidth, mRenderHeight));
|
||||
|
@ -209,29 +227,43 @@ void GlComposeTask::run()
|
|||
onResolve();
|
||||
}
|
||||
|
||||
GLuint GlComposeTask::getSelfFbo() { return mFbo->getFboId(); }
|
||||
|
||||
GLuint GlComposeTask::getResolveFboId() { return mFbo->getResolveFboId(); }
|
||||
GLuint GlComposeTask::getSelfFbo()
|
||||
{
|
||||
return mFbo->getFboId();
|
||||
}
|
||||
|
||||
void GlComposeTask::onResolve() {
|
||||
|
||||
GLuint GlComposeTask::getResolveFboId()
|
||||
{
|
||||
return mFbo->getResolveFboId();
|
||||
}
|
||||
|
||||
|
||||
void GlComposeTask::onResolve()
|
||||
{
|
||||
GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, getSelfFbo()));
|
||||
GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getResolveFboId()));
|
||||
|
||||
|
||||
GL_CHECK(glBlitFramebuffer(0, 0, mRenderWidth, mRenderHeight, 0, 0, mRenderWidth, mRenderHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* GlBlitTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlBlitTask::GlBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks)
|
||||
: GlComposeTask(program, target, fbo, std::move(tasks)), mColorTex(fbo->getColorTexture())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void GlBlitTask::run()
|
||||
{
|
||||
GlComposeTask::run();
|
||||
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo()));
|
||||
GL_CHECK(glViewport(mTargetViewport.x, mTargetViewport.y, mTargetViewport.w, mTargetViewport.h));
|
||||
GL_CHECK(glViewport(mTargetViewport.x(), mTargetViewport.y(), mTargetViewport.w(), mTargetViewport.h()));
|
||||
|
||||
if (mClearBuffer) {
|
||||
GL_CHECK(glClearColor(0, 0, 0, 0));
|
||||
|
@ -246,16 +278,24 @@ void GlBlitTask::run()
|
|||
GlRenderTask::run();
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* GlDrawBlitTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
GlDrawBlitTask::GlDrawBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks)
|
||||
: GlComposeTask(program, target, fbo, std::move(tasks))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
GlDrawBlitTask::~GlDrawBlitTask()
|
||||
{
|
||||
if (mPrevTask) delete mPrevTask;
|
||||
}
|
||||
|
||||
|
||||
void GlDrawBlitTask::run()
|
||||
{
|
||||
if (mPrevTask) mPrevTask->run();
|
||||
|
@ -269,15 +309,22 @@ void GlDrawBlitTask::run()
|
|||
GlRenderTask::run();
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* GlClipTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlClipTask::GlClipTask(GlRenderTask* clip, GlRenderTask* mask)
|
||||
:GlRenderTask(nullptr), mClipTask(clip), mMaskTask(mask) {}
|
||||
|
||||
|
||||
GlClipTask::~GlClipTask()
|
||||
{
|
||||
delete mClipTask;
|
||||
delete mMaskTask;
|
||||
}
|
||||
|
||||
|
||||
void GlClipTask::run()
|
||||
{
|
||||
GL_CHECK(glEnable(GL_STENCIL_TEST));
|
||||
|
@ -304,14 +351,22 @@ void GlClipTask::run()
|
|||
GL_CHECK(glDisable(GL_STENCIL_TEST));
|
||||
}
|
||||
|
||||
|
||||
void GlClipTask::normalizeDrawDepth(int32_t maxDepth)
|
||||
{
|
||||
mClipTask->normalizeDrawDepth(maxDepth);
|
||||
mMaskTask->normalizeDrawDepth(maxDepth);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GlSimpleBlendTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
GlSimpleBlendTask::GlSimpleBlendTask(BlendMethod method, GlProgram* program)
|
||||
: GlRenderTask(program), mBlendMethod(method) {}
|
||||
: GlRenderTask(program), mBlendMethod(method)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void GlSimpleBlendTask::run()
|
||||
{
|
||||
|
@ -332,8 +387,17 @@ void GlSimpleBlendTask::run()
|
|||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* GlComplexBlendTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask)
|
||||
: GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mStencilTask(stencilTask), mComposeTask(composeTask) {}
|
||||
: GlRenderTask(program), mDstFbo(dstFbo), mDstCopyFbo(dstCopyFbo), mStencilTask(stencilTask), mComposeTask(composeTask)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
GlComplexBlendTask::~GlComplexBlendTask()
|
||||
{
|
||||
|
@ -341,6 +405,7 @@ GlComplexBlendTask::~GlComplexBlendTask()
|
|||
delete mComposeTask;
|
||||
}
|
||||
|
||||
|
||||
void GlComplexBlendTask::run()
|
||||
{
|
||||
mComposeTask->run();
|
||||
|
@ -349,12 +414,11 @@ void GlComplexBlendTask::run()
|
|||
GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId()));
|
||||
GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->getResolveFboId()));
|
||||
|
||||
GL_CHECK(glViewport(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h));
|
||||
GL_CHECK(glScissor(0, 0, mDstFbo->getViewport().w, mDstFbo->getViewport().h));
|
||||
GL_CHECK(glViewport(0, 0, mDstFbo->getViewport().w(), mDstFbo->getViewport().h()));
|
||||
GL_CHECK(glScissor(0, 0, mDstFbo->getViewport().w(), mDstFbo->getViewport().h()));
|
||||
|
||||
const auto& vp = getViewport();
|
||||
|
||||
GL_CHECK(glBlitFramebuffer(vp.x, vp.y, vp.x + vp.w, vp.y + vp.h, 0, 0, vp.w, vp.h, GL_COLOR_BUFFER_BIT, GL_LINEAR));
|
||||
GL_CHECK(glBlitFramebuffer(vp.min.x, vp.min.y, vp.max.x, vp.max.y, 0, 0, vp.w(), vp.h(), GL_COLOR_BUFFER_BIT, GL_LINEAR));
|
||||
|
||||
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->getFboId()));
|
||||
|
||||
|
@ -381,12 +445,17 @@ void GlComplexBlendTask::run()
|
|||
GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
|
||||
}
|
||||
|
||||
|
||||
void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth)
|
||||
{
|
||||
mStencilTask->normalizeDrawDepth(maxDepth);
|
||||
GlRenderTask::normalizeDrawDepth(maxDepth);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GlGaussianBlurTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void GlGaussianBlurTask::run()
|
||||
{
|
||||
const auto vp = getViewport();
|
||||
|
@ -439,6 +508,9 @@ void GlGaussianBlurTask::run()
|
|||
GL_CHECK(glEnable(GL_BLEND));
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GlEffectDropShadowTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void GlEffectDropShadowTask::run()
|
||||
{
|
||||
|
@ -492,6 +564,9 @@ void GlEffectDropShadowTask::run()
|
|||
GL_CHECK(glEnable(GL_BLEND));
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* GlEffectColorTransformTask Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void GlEffectColorTransformTask::run()
|
||||
{
|
||||
|
|
|
@ -130,12 +130,10 @@ public:
|
|||
|
||||
protected:
|
||||
GLuint getTargetFbo() { return mTargetFbo; }
|
||||
|
||||
GLuint getSelfFbo();
|
||||
|
||||
GLuint getResolveFboId();
|
||||
|
||||
void onResolve();
|
||||
|
||||
private:
|
||||
GLuint mTargetFbo;
|
||||
GlRenderTarget* mFbo;
|
||||
|
|
|
@ -52,6 +52,8 @@ void GlRenderer::flush()
|
|||
{
|
||||
clearDisposes();
|
||||
|
||||
mRootTarget.reset();
|
||||
|
||||
ARRAY_FOREACH(p, mComposePool) delete(*p);
|
||||
mComposePool.clear();
|
||||
|
||||
|
@ -181,10 +183,10 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
|
|||
bbox.intersect(vp);
|
||||
}
|
||||
|
||||
auto x = bbox.x - vp.x;
|
||||
auto y = bbox.y - vp.y;
|
||||
auto w = bbox.w;
|
||||
auto h = bbox.h;
|
||||
auto x = bbox.sx() - vp.sx();
|
||||
auto y = bbox.sy() - vp.sy();
|
||||
auto w = bbox.sw();
|
||||
auto h = bbox.sh();
|
||||
|
||||
GlRenderTask* task = nullptr;
|
||||
if (mBlendMethod != BlendMethod::Normal && !complexBlend) task = new GlSimpleBlendTask(mBlendMethod, mPrograms[RT_Color]);
|
||||
|
@ -197,7 +199,8 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
|
|||
return;
|
||||
}
|
||||
|
||||
task->setViewport({x, vp.h - y - h, w, h});
|
||||
y = vp.sh() - y - h;
|
||||
task->setViewport({{x, y}, {x + w, y + h}});
|
||||
|
||||
GlRenderTask* stencilTask = nullptr;
|
||||
|
||||
|
@ -266,7 +269,6 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
{
|
||||
auto vp = currentPass()->getViewport();
|
||||
auto bbox = sdata.geometry.viewport;
|
||||
|
||||
bbox.intersect(vp);
|
||||
|
||||
const Fill::ColorStop* stops = nullptr;
|
||||
|
@ -275,13 +277,9 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
|
||||
GlRenderTask* task = nullptr;
|
||||
|
||||
if (fill->type() == Type::LinearGradient) {
|
||||
task = new GlRenderTask(mPrograms[RT_LinGradient]);
|
||||
} else if (fill->type() == Type::RadialGradient) {
|
||||
task = new GlRenderTask(mPrograms[RT_RadGradient]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (fill->type() == Type::LinearGradient) task = new GlRenderTask(mPrograms[RT_LinGradient]);
|
||||
else if (fill->type() == Type::RadialGradient) task = new GlRenderTask(mPrograms[RT_RadGradient]);
|
||||
else return;
|
||||
|
||||
task->setDrawDepth(depth);
|
||||
|
||||
|
@ -291,13 +289,11 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
}
|
||||
|
||||
auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds());
|
||||
|
||||
if (complexBlend) vp = currentPass()->getViewport();
|
||||
|
||||
auto x = bbox.x - vp.x;
|
||||
auto y = bbox.y - vp.y;
|
||||
|
||||
task->setViewport({x, vp.h - y - bbox.h, bbox.w, bbox.h});
|
||||
auto x = bbox.sx() - vp.sx();
|
||||
auto y = vp.sh() - (bbox.sy() - vp.sy()) - bbox.sh();
|
||||
task->setViewport({{x, y}, {x + bbox.sw(), y + bbox.sh()}});
|
||||
|
||||
GlRenderTask* stencilTask = nullptr;
|
||||
GlStencilMode stencilMode = sdata.geometry.getStencilMode(flag);
|
||||
|
@ -496,22 +492,18 @@ void GlRenderer::drawClip(Array<RenderData>& clips)
|
|||
|
||||
for (uint32_t i = 0; i < clips.count; ++i) {
|
||||
auto sdata = static_cast<GlShape*>(clips[i]);
|
||||
|
||||
auto clipTask = new GlRenderTask(mPrograms[RT_Stencil]);
|
||||
|
||||
clipTask->setDrawDepth(clipDepths[i]);
|
||||
|
||||
auto flag = (sdata->geometry.stroke.vertex.count > 0) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path;
|
||||
sdata->geometry.draw(clipTask, &mGpuBuffer, flag);
|
||||
|
||||
auto bbox = sdata->geometry.viewport;
|
||||
|
||||
bbox.intersect(vp);
|
||||
|
||||
auto x = bbox.x - vp.x;
|
||||
auto y = bbox.y - vp.y;
|
||||
|
||||
clipTask->setViewport({x, vp.h - y - bbox.h, bbox.w, bbox.h});
|
||||
auto x = bbox.sx() - vp.sx();
|
||||
auto y = vp.sh() - (bbox.sy() - vp.sy()) - bbox.sh();
|
||||
clipTask->setViewport({{x, y}, {x + bbox.sw(), y + bbox.sh()}});
|
||||
|
||||
float matrix44[16];
|
||||
currentPass()->getMatrix(matrix44, sdata->geometry.matrix);
|
||||
|
@ -527,7 +519,7 @@ void GlRenderer::drawClip(Array<RenderData>& clips)
|
|||
maskTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), identityVertexOffset});
|
||||
maskTask->addBindResource(GlBindingResource{0, loc, mGpuBuffer.getBufferId(), mat4Offset, 16 * sizeof(float), });
|
||||
maskTask->setDrawRange(identityIndexOffset, 6);
|
||||
maskTask->setViewport({0, 0, static_cast<int32_t>(vp.w), static_cast<int32_t>(vp.h)});
|
||||
maskTask->setViewport({{0, 0}, {vp.sw(), vp.sh()}});
|
||||
|
||||
currentPass()->addRenderTask(new GlClipTask(clipTask, maskTask));
|
||||
}
|
||||
|
@ -541,11 +533,10 @@ GlRenderPass* GlRenderer::currentPass()
|
|||
|
||||
bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds)
|
||||
{
|
||||
if (vp.w == 0 || vp.h == 0) return false;
|
||||
if (vp.invalid()) return false;
|
||||
|
||||
bounds.intersect(vp);
|
||||
|
||||
if (bounds.w == 0 || bounds.h == 0) return false;
|
||||
if (bounds.invalid()) return false;
|
||||
|
||||
if (mBlendMethod == BlendMethod::Normal || mBlendMethod == BlendMethod::Add || mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) return false;
|
||||
|
||||
|
@ -571,20 +562,12 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
|
|||
if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h));
|
||||
auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp);
|
||||
|
||||
{
|
||||
const auto& passVp = currentPass()->getViewport();
|
||||
|
||||
auto x = vp.x;
|
||||
auto y = vp.y;
|
||||
auto w = vp.w;
|
||||
auto h = vp.h;
|
||||
|
||||
stencilTask->setViewport({x, passVp.h - y - h, w, h});
|
||||
}
|
||||
auto x = vp.sx();
|
||||
auto y = currentPass()->getViewport().sh() - vp.sy() - vp.sh();
|
||||
stencilTask->setViewport({{x, y}, {x + vp.sw(), y + vp.sh()}});
|
||||
|
||||
stencilTask->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
|
||||
{
|
||||
// set view matrix
|
||||
float matrix44[16];
|
||||
currentPass()->getMatrix(matrix44, matrix);
|
||||
|
@ -596,12 +579,9 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
|
|||
viewOffset,
|
||||
16 * sizeof(float),
|
||||
});
|
||||
}
|
||||
|
||||
auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask);
|
||||
|
||||
prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight());
|
||||
|
||||
task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
|
||||
// src and dst texture
|
||||
|
@ -632,8 +612,7 @@ GlProgram* GlRenderer::getBlendProgram()
|
|||
|
||||
void GlRenderer::prepareBlitTask(GlBlitTask* task)
|
||||
{
|
||||
RenderRegion region{0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)};
|
||||
prepareCmpTask(task, region, surface.w, surface.h);
|
||||
prepareCmpTask(task, {{0, 0}, {int32_t(surface.w), int32_t(surface.h)}}, surface.w, surface.h);
|
||||
task->addBindResource(GlBindingResource{0, task->getColorTexture(), task->getProgram()->getUniformLocation("uSrcTexture")});
|
||||
}
|
||||
|
||||
|
@ -648,13 +627,13 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint
|
|||
auto taskVp = vp;
|
||||
taskVp.intersect(passVp);
|
||||
|
||||
auto x = taskVp.x - passVp.x;
|
||||
auto y = taskVp.y - passVp.y;
|
||||
auto w = taskVp.w;
|
||||
auto h = taskVp.h;
|
||||
auto x = taskVp.sx() - passVp.sx();
|
||||
auto y = taskVp.sy() - passVp.sy();
|
||||
auto w = taskVp.sw();
|
||||
auto h = taskVp.sh();
|
||||
|
||||
float rw = static_cast<float>(passVp.w);
|
||||
float rh = static_cast<float>(passVp.h);
|
||||
float rw = static_cast<float>(passVp.w());
|
||||
float rh = static_cast<float>(passVp.h());
|
||||
|
||||
float l = static_cast<float>(x);
|
||||
float t = static_cast<float>(rh - y);
|
||||
|
@ -705,10 +684,9 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint
|
|||
|
||||
task->addVertexLayout(GlVertexLayout{0, 2, 4 * sizeof(float), vertexOffset});
|
||||
task->addVertexLayout(GlVertexLayout{1, 2, 4 * sizeof(float), vertexOffset + 2 * sizeof(float)});
|
||||
|
||||
task->setDrawRange(indexOffset, indices.count);
|
||||
|
||||
task->setViewport({x, static_cast<int32_t>((passVp.h - y - h)), w, h});
|
||||
y = (passVp.sh() - y - h);
|
||||
task->setViewport({{x, y}, {x + w, y + h}});
|
||||
}
|
||||
|
||||
|
||||
|
@ -741,11 +719,11 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
if (program && !selfPass->isEmpty() && !maskPass->isEmpty()) {
|
||||
auto prev_task = maskPass->endRenderPass<GlComposeTask>(nullptr, currentPass()->getFboId());
|
||||
prev_task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
prev_task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
|
||||
prev_task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h());
|
||||
prev_task->setViewport(glCmp->bbox);
|
||||
|
||||
auto compose_task = selfPass->endRenderPass<GlDrawBlitTask>(program, currentPass()->getFboId());
|
||||
compose_task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
|
||||
compose_task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h());
|
||||
compose_task->setPrevTask(prev_task);
|
||||
|
||||
prepareCmpTask(compose_task, glCmp->bbox, selfPass->getFboWidth(), selfPass->getFboHeight());
|
||||
|
@ -754,7 +732,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
compose_task->addBindResource(GlBindingResource{1, maskPass->getTextureId(), program->getUniformLocation("uMaskTexture")});
|
||||
|
||||
compose_task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
compose_task->setParentSize(static_cast<uint32_t>(currentPass()->getViewport().w), static_cast<uint32_t>(currentPass()->getViewport().h));
|
||||
compose_task->setParentSize(currentPass()->getViewport().w(), currentPass()->getViewport().h());
|
||||
currentPass()->addRenderTask(compose_task);
|
||||
}
|
||||
|
||||
|
@ -767,7 +745,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
|
||||
if (!renderPass->isEmpty()) {
|
||||
auto task = renderPass->endRenderPass<GlDrawBlitTask>(mPrograms[RT_Image], currentPass()->getFboId());
|
||||
task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
|
||||
task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h());
|
||||
prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight());
|
||||
task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
|
||||
|
@ -795,7 +773,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
|
||||
// texture id
|
||||
task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uTexture")});
|
||||
task->setParentSize(static_cast<uint32_t>(currentPass()->getViewport().w), static_cast<uint32_t>(currentPass()->getViewport().h));
|
||||
task->setParentSize(currentPass()->getViewport().w(), currentPass()->getViewport().h());
|
||||
currentPass()->addRenderTask(std::move(task));
|
||||
}
|
||||
delete(renderPass);
|
||||
|
@ -809,6 +787,8 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
|
||||
bool GlRenderer::clear()
|
||||
{
|
||||
if (mRootTarget.invalid()) return false;
|
||||
|
||||
mClearBuffer = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -832,9 +812,8 @@ bool GlRenderer::target(void* context, int32_t id, uint32_t w, uint32_t h)
|
|||
|
||||
currentContext();
|
||||
|
||||
mRootTarget = GlRenderTarget(surface.w, surface.h);
|
||||
mRootTarget.setViewport({0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)});
|
||||
mRootTarget.init(mTargetFboId);
|
||||
mRootTarget.setViewport({{0, 0}, {int32_t(surface.w), int32_t(surface.h)}});
|
||||
mRootTarget.init(surface.w, surface.h, mTargetFboId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -861,7 +840,7 @@ bool GlRenderer::sync()
|
|||
prepareBlitTask(task);
|
||||
|
||||
task->mClearBuffer = mClearBuffer;
|
||||
task->setTargetViewport({0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)});
|
||||
task->setTargetViewport({{0, 0}, {int32_t(surface.w), int32_t(surface.h)}});
|
||||
|
||||
if (mGpuBuffer.flushToGPU()) {
|
||||
mGpuBuffer.bind();
|
||||
|
@ -874,6 +853,9 @@ bool GlRenderer::sync()
|
|||
|
||||
clearDisposes();
|
||||
|
||||
// Reset clear buffer flag to default (false) after use.
|
||||
mClearBuffer = false;
|
||||
|
||||
delete task;
|
||||
|
||||
return true;
|
||||
|
@ -882,7 +864,7 @@ bool GlRenderer::sync()
|
|||
|
||||
RenderRegion GlRenderer::region(RenderData data)
|
||||
{
|
||||
if (currentPass()->isEmpty()) return {0, 0, 0, 0};
|
||||
if (currentPass()->isEmpty()) return {};
|
||||
|
||||
auto shape = reinterpret_cast<GlShape*>(data);
|
||||
auto bounds = shape->geometry.getBounds();
|
||||
|
@ -896,6 +878,8 @@ RenderRegion GlRenderer::region(RenderData data)
|
|||
|
||||
bool GlRenderer::preRender()
|
||||
{
|
||||
if (mRootTarget.invalid()) return false;
|
||||
|
||||
currentContext();
|
||||
if (mPrograms.empty()) initShaders();
|
||||
mRenderPassStack.push(new GlRenderPass(&mRootTarget));
|
||||
|
@ -930,20 +914,11 @@ bool GlRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_
|
|||
cmp->opacity = opacity;
|
||||
|
||||
uint32_t index = mRenderPassStack.count - 1;
|
||||
|
||||
if (index >= mComposePool.count) {
|
||||
mComposePool.push( new GlRenderTargetPool(surface.w, surface.h));
|
||||
}
|
||||
if (index >= mComposePool.count) mComposePool.push( new GlRenderTargetPool(surface.w, surface.h));
|
||||
|
||||
auto glCmp = static_cast<GlCompositor*>(cmp);
|
||||
|
||||
if (glCmp->bbox.w > 0 && glCmp->bbox.h > 0) {
|
||||
auto renderTarget = mComposePool[index]->getRenderTarget(glCmp->bbox);
|
||||
mRenderPassStack.push(new GlRenderPass(renderTarget));
|
||||
} else {
|
||||
// empty render pass
|
||||
mRenderPassStack.push(new GlRenderPass(nullptr));
|
||||
}
|
||||
if (glCmp->bbox.valid()) mRenderPassStack.push(new GlRenderPass(mComposePool[index]->getRenderTarget(glCmp->bbox)));
|
||||
else mRenderPassStack.push(new GlRenderPass(nullptr));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1064,12 +1039,12 @@ bool GlRenderer::effectGaussianBlurRegion(RenderEffectGaussianBlur* effect)
|
|||
{
|
||||
auto gaussianBlur = (GlGaussianBlur*)effect->rd;
|
||||
if (effect->direction != 2) {
|
||||
effect->extend.x = -gaussianBlur->extend;
|
||||
effect->extend.w = +gaussianBlur->extend * 2;
|
||||
effect->extend.min.x = -gaussianBlur->extend;
|
||||
effect->extend.max.x = +gaussianBlur->extend;
|
||||
}
|
||||
if (effect->direction != 1) {
|
||||
effect->extend.y = -gaussianBlur->extend;
|
||||
effect->extend.h = +gaussianBlur->extend * 2;
|
||||
effect->extend.min.y = -gaussianBlur->extend;
|
||||
effect->extend.max.y = +gaussianBlur->extend;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
@ -1078,10 +1053,10 @@ bool GlRenderer::effectGaussianBlurRegion(RenderEffectGaussianBlur* effect)
|
|||
bool GlRenderer::effectDropShadowRegion(RenderEffectDropShadow* effect)
|
||||
{
|
||||
auto gaussianBlur = (GlDropShadow*)effect->rd;
|
||||
effect->extend.x = -gaussianBlur->extend;
|
||||
effect->extend.w = +gaussianBlur->extend * 2;
|
||||
effect->extend.y = -gaussianBlur->extend;
|
||||
effect->extend.h = +gaussianBlur->extend * 2;
|
||||
effect->extend.min.x = -gaussianBlur->extend;
|
||||
effect->extend.max.x = +gaussianBlur->extend;
|
||||
effect->extend.min.y = -gaussianBlur->extend;
|
||||
effect->extend.max.y = +gaussianBlur->extend;
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -1131,7 +1106,6 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
|
|||
auto voffset = mGpuBuffer.push((void*)vdata, sizeof(vdata));
|
||||
auto ioffset = mGpuBuffer.pushIndex((void*)idata, sizeof(idata));
|
||||
|
||||
// effect gaussian blur
|
||||
if (effect->type == SceneEffect::GaussianBlur) {
|
||||
// get gaussian programs
|
||||
GlProgram* programHorz = mPrograms[RT_GaussianHorz];
|
||||
|
@ -1147,7 +1121,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
|
|||
// create gaussian blur tasks
|
||||
auto gaussianTask = new GlGaussianBlurTask(dstFbo, dstCopyFbo0, dstCopyFbo1);
|
||||
gaussianTask->effect = (RenderEffectGaussianBlur*)effect;
|
||||
gaussianTask->setViewport({0, 0, vp.w, vp.h});
|
||||
gaussianTask->setViewport({{0, 0}, {vp.sw(), vp.sh()}});
|
||||
// horizontal blur task and geometry
|
||||
gaussianTask->horzTask = new GlRenderTask(programHorz);
|
||||
gaussianTask->horzTask->addBindResource(GlBindingResource{0, programHorz->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), blurOffset, sizeof(GlGaussianBlur)});
|
||||
|
@ -1160,8 +1134,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
|
|||
gaussianTask->vertTask->setDrawRange(ioffset, 6);
|
||||
// add task to render pipeline
|
||||
pass->addRenderTask(gaussianTask);
|
||||
} // effect drop shadow
|
||||
else if (effect->type == SceneEffect::DropShadow) {
|
||||
} else if (effect->type == SceneEffect::DropShadow) {
|
||||
// get programs
|
||||
GlProgram* program = mPrograms[RT_DropShadow];
|
||||
GlProgram* programHorz = mPrograms[RT_GaussianHorz];
|
||||
|
@ -1177,7 +1150,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
|
|||
// create gaussian blur tasks
|
||||
auto task = new GlEffectDropShadowTask(program, dstFbo, dstCopyFbo0, dstCopyFbo1);
|
||||
task->effect = (RenderEffectDropShadow*)effect;
|
||||
task->setViewport({0, 0, vp.w, vp.h});
|
||||
task->setViewport({{0, 0}, {vp.sw(), vp.sh()}});
|
||||
task->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("DropShadow"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlDropShadow)});
|
||||
task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset});
|
||||
task->setDrawRange(ioffset, 6);
|
||||
|
@ -1193,8 +1166,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
|
|||
task->vertTask->setDrawRange(ioffset, 6);
|
||||
// add task to render pipeline
|
||||
pass->addRenderTask(task);
|
||||
} // effect fill, tint, tritone
|
||||
else if ((effect->type == SceneEffect::Fill) || (effect->type == SceneEffect::Tint) || (effect->type == SceneEffect::Tritone)) {
|
||||
} else if ((effect->type == SceneEffect::Fill) || (effect->type == SceneEffect::Tint) || (effect->type == SceneEffect::Tritone)) {
|
||||
GlProgram* program{};
|
||||
if (effect->type == SceneEffect::Fill) program = mPrograms[RT_EffectFill];
|
||||
else if (effect->type == SceneEffect::Tint) program = mPrograms[RT_EffectTint];
|
||||
|
@ -1208,7 +1180,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
|
|||
auto paramsOffset = mGpuBuffer.push(params, sizeof(GlEffectParams), true);
|
||||
// create and setup task
|
||||
auto task = new GlEffectColorTransformTask(program, dstFbo, dstCopyFbo);
|
||||
task->setViewport({0, 0, vp.w, vp.h});
|
||||
task->setViewport({{0, 0}, {vp.sw(), vp.sh()}});
|
||||
task->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("Params"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlEffectParams)});
|
||||
task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset});
|
||||
task->setDrawRange(ioffset, 6);
|
||||
|
@ -1251,25 +1223,18 @@ bool GlRenderer::blend(BlendMethod method)
|
|||
bool GlRenderer::renderImage(void* data)
|
||||
{
|
||||
auto sdata = static_cast<GlShape*>(data);
|
||||
|
||||
if (!sdata) return false;
|
||||
|
||||
if (currentPass()->isEmpty()) return true;
|
||||
if (currentPass()->isEmpty() || !(sdata->updateFlag & RenderUpdateFlag::Image)) return true;
|
||||
|
||||
if ((sdata->updateFlag & RenderUpdateFlag::Image) == 0) return true;
|
||||
auto vp = currentPass()->getViewport();
|
||||
|
||||
auto bbox = sdata->geometry.viewport;
|
||||
|
||||
bbox.intersect(vp);
|
||||
if (bbox.invalid()) return true;
|
||||
|
||||
if (bbox.w <= 0 || bbox.h <= 0) return true;
|
||||
|
||||
auto x = bbox.x - vp.x;
|
||||
auto y = bbox.y - vp.y;
|
||||
|
||||
|
||||
int32_t drawDepth = currentPass()->nextDrawDepth();
|
||||
auto x = bbox.sx() - vp.sx();
|
||||
auto y = bbox.sy() - vp.sy();
|
||||
auto drawDepth = currentPass()->nextDrawDepth();
|
||||
|
||||
if (!sdata->clips.empty()) drawClip(sdata->clips);
|
||||
|
||||
|
@ -1282,7 +1247,6 @@ bool GlRenderer::renderImage(void* data)
|
|||
}
|
||||
|
||||
bool complexBlend = beginComplexBlending(bbox, sdata->geometry.getBounds());
|
||||
|
||||
if (complexBlend) vp = currentPass()->getViewport();
|
||||
|
||||
// matrix buffer
|
||||
|
@ -1311,7 +1275,11 @@ bool GlRenderer::renderImage(void* data)
|
|||
// texture id
|
||||
task->addBindResource(GlBindingResource{0, sdata->texId, task->getProgram()->getUniformLocation("uTexture")});
|
||||
|
||||
task->setViewport({x, vp.h - y - bbox.h, bbox.w, bbox.h});
|
||||
y = vp.sh() - y - bbox.sh();
|
||||
auto x2 = x + bbox.sw();
|
||||
auto y2 = y + bbox.sh();
|
||||
|
||||
task->setViewport({{x, y}, {x2, y2}});
|
||||
|
||||
currentPass()->addRenderTask(task);
|
||||
|
||||
|
@ -1327,33 +1295,25 @@ bool GlRenderer::renderImage(void* data)
|
|||
|
||||
bool GlRenderer::renderShape(RenderData data)
|
||||
{
|
||||
auto sdata = static_cast<GlShape*>(data);
|
||||
|
||||
if (currentPass()->isEmpty()) return true;
|
||||
|
||||
auto sdata = static_cast<GlShape*>(data);
|
||||
if (sdata->updateFlag == RenderUpdateFlag::None) return true;
|
||||
|
||||
const auto& vp = currentPass()->getViewport();
|
||||
|
||||
auto bbox = sdata->geometry.viewport;
|
||||
bbox.intersect(vp);
|
||||
|
||||
if (bbox.w <= 0 || bbox.h <= 0) return true;
|
||||
bbox.intersect(currentPass()->getViewport());
|
||||
if (bbox.invalid()) return true;
|
||||
|
||||
int32_t drawDepth1 = 0, drawDepth2 = 0;
|
||||
|
||||
size_t flags = static_cast<size_t>(sdata->updateFlag);
|
||||
|
||||
if (flags == 0) return false;
|
||||
|
||||
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Color)) drawDepth1 = currentPass()->nextDrawDepth();
|
||||
|
||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke)) drawDepth2 = currentPass()->nextDrawDepth();
|
||||
if (sdata->updateFlag == RenderUpdateFlag::None) return false;
|
||||
if (sdata->updateFlag & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Color)) drawDepth1 = currentPass()->nextDrawDepth();
|
||||
if (sdata->updateFlag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke)) drawDepth2 = currentPass()->nextDrawDepth();
|
||||
|
||||
if (!sdata->clips.empty()) drawClip(sdata->clips);
|
||||
|
||||
auto processFill = [&]() {
|
||||
if (flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient)) {
|
||||
if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient)) {
|
||||
if (const auto& gradient = sdata->rshape->fill) {
|
||||
drawPrimitive(*sdata, gradient, RenderUpdateFlag::Gradient, drawDepth1);
|
||||
} else if (sdata->rshape->color.a > 0) {
|
||||
|
@ -1364,7 +1324,7 @@ bool GlRenderer::renderShape(RenderData data)
|
|||
|
||||
auto processStroke = [&]() {
|
||||
if (!sdata->rshape->stroke) return;
|
||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke)) {
|
||||
if (sdata->updateFlag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke)) {
|
||||
if (const auto& gradient = sdata->rshape->strokeFill()) {
|
||||
drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke, drawDepth2);
|
||||
} else if (sdata->rshape->stroke->color.a > 0) {
|
||||
|
@ -1525,6 +1485,8 @@ bool GlRenderer::viewport(const RenderRegion& vp)
|
|||
|
||||
bool GlRenderer::preUpdate()
|
||||
{
|
||||
if (mRootTarget.invalid()) return false;
|
||||
|
||||
currentContext();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -153,7 +153,7 @@ private:
|
|||
} mDisposed;
|
||||
|
||||
BlendMethod mBlendMethod = BlendMethod::Normal;
|
||||
bool mClearBuffer = true; //FIXME: clear buffer should be optional (default is false)
|
||||
bool mClearBuffer = false;
|
||||
};
|
||||
|
||||
#endif /* _TVG_GL_RENDERER_H_ */
|
||||
|
|
|
@ -1516,12 +1516,7 @@ void Stroker::stroke(const RenderShape *rshape, const RenderPath& path)
|
|||
|
||||
RenderRegion Stroker::bounds() const
|
||||
{
|
||||
return RenderRegion {
|
||||
static_cast<int32_t>(floor(mLeftTop.x)),
|
||||
static_cast<int32_t>(floor(mLeftTop.y)),
|
||||
static_cast<int32_t>(ceil(mRightBottom.x - floor(mLeftTop.x))),
|
||||
static_cast<int32_t>(ceil(mRightBottom.y - floor(mLeftTop.y))),
|
||||
};
|
||||
return {{int32_t(floor(mLeftTop.x)), int32_t(floor(mLeftTop.y))}, {int32_t(ceil(mRightBottom.x)), int32_t(ceil(mRightBottom.y))}};
|
||||
}
|
||||
|
||||
|
||||
|
@ -2197,26 +2192,15 @@ void BWTessellator::tessellate(const RenderPath& path, const Matrix& matrix)
|
|||
|
||||
RenderRegion BWTessellator::bounds() const
|
||||
{
|
||||
return RenderRegion {
|
||||
static_cast<int32_t>(floor(bbox.min.x)),
|
||||
static_cast<int32_t>(floor(bbox.min.y)),
|
||||
static_cast<int32_t>(ceil(bbox.max.x - floor(bbox.min.x))),
|
||||
static_cast<int32_t>(ceil(bbox.max.y - floor(bbox.min.y))),
|
||||
};
|
||||
return {{int32_t(floor(bbox.min.x)), int32_t(floor(bbox.min.y))}, {int32_t(ceil(bbox.max.x)), int32_t(ceil(bbox.max.y))}};
|
||||
}
|
||||
|
||||
|
||||
uint32_t BWTessellator::pushVertex(float x, float y)
|
||||
{
|
||||
auto index = _pushVertex(mBuffer->vertex, x, y);
|
||||
|
||||
if (index == 0) {
|
||||
bbox.max = bbox.min = {x, y};
|
||||
} else {
|
||||
bbox.min = {std::min(bbox.min.x, x), std::min(bbox.min.y, y)};
|
||||
bbox.max = {std::max(bbox.max.x, x), std::max(bbox.max.y, y)};
|
||||
}
|
||||
|
||||
if (index == 0) bbox.max = bbox.min = {x, y};
|
||||
else bbox = {{std::min(bbox.min.x, x), std::min(bbox.min.y, y)}, {std::max(bbox.max.x, x), std::max(bbox.max.y, y)}};
|
||||
return index;
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ private:
|
|||
void pushTriangle(uint32_t a, uint32_t b, uint32_t c);
|
||||
|
||||
GlGeometryBuffer* mBuffer;
|
||||
BBox bbox = {{}, {}};
|
||||
BBox bbox = {};
|
||||
};
|
||||
|
||||
} // namespace tvg
|
||||
|
|
|
@ -121,19 +121,20 @@ struct SwSpan
|
|||
|
||||
struct SwRle
|
||||
{
|
||||
SwSpan *spans;
|
||||
uint32_t alloc;
|
||||
uint32_t size;
|
||||
};
|
||||
Array<SwSpan> spans;
|
||||
|
||||
struct SwBBox
|
||||
{
|
||||
SwPoint min, max;
|
||||
|
||||
void reset()
|
||||
bool invalid() const
|
||||
{
|
||||
min.x = min.y = max.x = max.y = 0;
|
||||
return spans.empty();
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return !invalid();
|
||||
}
|
||||
|
||||
uint32_t size() const { return spans.count; }
|
||||
SwSpan* data() const { return spans.data; }
|
||||
};
|
||||
|
||||
struct SwFill
|
||||
|
@ -215,7 +216,7 @@ struct SwShape
|
|||
SwFill* fill = nullptr;
|
||||
SwRle* rle = nullptr;
|
||||
SwRle* strokeRle = nullptr;
|
||||
SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
|
||||
RenderRegion bbox; //Keep it boundary without stroke region. Using for optimal filling.
|
||||
|
||||
bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
|
||||
};
|
||||
|
@ -279,7 +280,7 @@ struct SwCompositor : RenderCompositor
|
|||
SwSurface* recoverSfc; //Recover surface when composition is started
|
||||
SwCompositor* recoverCmp; //Recover compositor when composition is done
|
||||
SwImage image;
|
||||
SwBBox bbox;
|
||||
RenderRegion bbox;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
|
@ -499,16 +500,15 @@ SwFixed mathLength(const SwPoint& pt);
|
|||
int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
|
||||
SwPoint mathTransform(const Point* to, const Matrix& transform);
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack);
|
||||
|
||||
void shapeReset(SwShape* shape);
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepared(const SwShape* shape);
|
||||
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
|
||||
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid);
|
||||
void shapeFree(SwShape* shape);
|
||||
void shapeDelStroke(SwShape* shape);
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
|
||||
|
@ -523,8 +523,8 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
|||
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
|
||||
void strokeFree(SwStroke* stroke);
|
||||
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid);
|
||||
bool imageGenRle(SwImage* image, const RenderRegion& bbox, bool antiAlias);
|
||||
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
|
||||
void imageReset(SwImage* image);
|
||||
void imageFree(SwImage* image);
|
||||
|
@ -547,13 +547,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
|
||||
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
|
||||
SwRle* rleRender(const SwBBox* bbox);
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const RenderRegion& bbox, bool antiAlias);
|
||||
SwRle* rleRender(const RenderRegion* bbox);
|
||||
void rleFree(SwRle* rle);
|
||||
void rleReset(SwRle* rle);
|
||||
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
|
||||
bool rleClip(SwRle* rle, const SwRle* clip);
|
||||
bool rleClip(SwRle* rle, const SwBBox* clip);
|
||||
bool rleClip(SwRle* rle, const RenderRegion* clip);
|
||||
|
||||
SwMpool* mpoolInit(uint32_t threads);
|
||||
bool mpoolTerm(SwMpool* mpool);
|
||||
|
@ -568,7 +568,11 @@ void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
|
|||
bool rasterCompositor(SwSurface* surface);
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
|
||||
bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c);
|
||||
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
|
||||
bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
|
||||
bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
|
||||
bool rasterDirectImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, uint8_t opacity);
|
||||
bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
|
||||
bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity);
|
||||
bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c);
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
|
||||
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
|
||||
|
@ -576,7 +580,7 @@ void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
|
|||
void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
|
||||
void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
|
||||
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
|
||||
void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped);
|
||||
void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, bool flipped);
|
||||
void rasterUnpremultiply(RenderSurface* surface);
|
||||
void rasterPremultiply(RenderSurface* surface);
|
||||
bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
|
||||
|
|
|
@ -72,7 +72,7 @@ static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool,
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
image->direct = _onlyShifted(transform);
|
||||
|
||||
|
@ -91,13 +91,13 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg
|
|||
}
|
||||
|
||||
if (!_genOutline(image, transform, mpool, tid)) return false;
|
||||
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
|
||||
return mathUpdateOutlineBBox(image->outline, clipBox, renderBox, image->direct);
|
||||
}
|
||||
|
||||
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
|
||||
bool imageGenRle(SwImage* image, const RenderRegion& renderBox, bool antiAlias)
|
||||
{
|
||||
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
|
||||
if ((image->rle = rleRender(image->rle, image->outline, renderBox, antiAlias))) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -271,27 +271,12 @@ SwPoint mathTransform(const Point* to, const Matrix& transform)
|
|||
}
|
||||
|
||||
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee)
|
||||
{
|
||||
clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x;
|
||||
clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y;
|
||||
clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x;
|
||||
clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y;
|
||||
|
||||
//Check boundary
|
||||
if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y ||
|
||||
clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack)
|
||||
{
|
||||
if (!outline) return false;
|
||||
|
||||
if (outline->pts.empty() || outline->cntrs.empty()) {
|
||||
renderRegion.reset();
|
||||
renderBox.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -310,16 +295,13 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
|
|||
}
|
||||
|
||||
if (fastTrack) {
|
||||
renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
|
||||
renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
|
||||
renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
|
||||
renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
|
||||
renderBox.min = {int32_t(round(xMin / 64.0f)), int32_t(round(yMin / 64.0f))};
|
||||
renderBox.max = {int32_t(round(xMax / 64.0f)), int32_t(round(yMax / 64.0f))};
|
||||
} else {
|
||||
renderRegion.min.x = xMin >> 6;
|
||||
renderRegion.max.x = (xMax + 63) >> 6;
|
||||
renderRegion.min.y = yMin >> 6;
|
||||
renderRegion.max.y = (yMax + 63) >> 6;
|
||||
renderBox.min = {xMin >> 6, yMin >> 6};
|
||||
renderBox.max = {(xMax + 63) >> 6, (yMax + 63) >> 6};
|
||||
}
|
||||
|
||||
return mathClipBBox(clipRegion, renderRegion);
|
||||
renderBox.intersect(clipBox);
|
||||
return renderBox.valid();
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ static inline int _gaussianRemap(int end, int idx)
|
|||
|
||||
//TODO: SIMD OPTIMIZATION?
|
||||
template<int border = 0>
|
||||
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, bool flipped)
|
||||
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, int32_t dimension, bool flipped)
|
||||
{
|
||||
if (flipped) {
|
||||
src += (bbox.min.x * stride + bbox.min.y) << 2;
|
||||
|
@ -136,17 +136,17 @@ static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality)
|
|||
|
||||
bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params)
|
||||
{
|
||||
//bbox region expansion for feathering
|
||||
auto& region = params->extend;
|
||||
//region expansion for feathering
|
||||
auto& bbox = params->extend;
|
||||
auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends;
|
||||
|
||||
if (params->direction != 2) {
|
||||
region.x = -extra;
|
||||
region.w = extra * 2;
|
||||
bbox.min.x = -extra;
|
||||
bbox.max.x = extra;
|
||||
}
|
||||
if (params->direction != 1) {
|
||||
region.y = -extra;
|
||||
region.h = extra * 2;
|
||||
bbox.min.y = -extra;
|
||||
bbox.max.y = extra;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -230,7 +230,7 @@ struct SwDropShadow : SwGaussianBlur
|
|||
|
||||
|
||||
//TODO: SIMD OPTIMIZATION?
|
||||
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const SwBBox& bbox, int32_t dimension, uint32_t color, bool flipped)
|
||||
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const RenderRegion& bbox, int32_t dimension, uint32_t color, bool flipped)
|
||||
{
|
||||
if (flipped) {
|
||||
src += (bbox.min.x * stride + bbox.min.y);
|
||||
|
@ -267,20 +267,20 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i
|
|||
}
|
||||
|
||||
|
||||
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct)
|
||||
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, RenderRegion& bbox, SwPoint& offset, uint8_t opacity, bool direct)
|
||||
{
|
||||
src += (region.min.y * sstride + region.min.x);
|
||||
dst += (region.min.y * dstride + region.min.x);
|
||||
src += (bbox.min.y * sstride + bbox.min.x);
|
||||
dst += (bbox.min.y * dstride + bbox.min.x);
|
||||
|
||||
auto w = region.max.x - region.min.x;
|
||||
auto h = region.max.y - region.min.y;
|
||||
auto w = bbox.max.x - bbox.min.x;
|
||||
auto h = bbox.max.y - bbox.min.y;
|
||||
auto translucent = (direct || opacity < 255);
|
||||
|
||||
//shift offset
|
||||
if (region.min.x + offset.x < 0) src -= offset.x;
|
||||
if (bbox.min.x + offset.x < 0) src -= offset.x;
|
||||
else dst += offset.x;
|
||||
|
||||
if (region.min.y + offset.y < 0) src -= (offset.y * sstride);
|
||||
if (bbox.min.y + offset.y < 0) src -= (offset.y * sstride);
|
||||
else dst += (offset.y * dstride);
|
||||
|
||||
for (auto y = 0; y < h; ++y) {
|
||||
|
@ -294,20 +294,19 @@ static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstr
|
|||
|
||||
bool effectDropShadowRegion(RenderEffectDropShadow* params)
|
||||
{
|
||||
//bbox region expansion for feathering
|
||||
auto& region = params->extend;
|
||||
//region expansion for feathering
|
||||
auto& bbox = params->extend;
|
||||
auto& offset = static_cast<SwDropShadow*>(params->rd)->offset;
|
||||
auto extra = static_cast<SwDropShadow*>(params->rd)->extends;
|
||||
|
||||
region.x = -extra;
|
||||
region.w = extra * 2;
|
||||
region.y = -extra;
|
||||
region.h = extra * 2;
|
||||
bbox.min = {-extra, -extra};
|
||||
bbox.max = {extra, extra};
|
||||
|
||||
region.x = std::min(region.x + (int32_t)offset.x, region.x);
|
||||
region.y = std::min(region.y + (int32_t)offset.y, region.y);
|
||||
region.w += abs(offset.x);
|
||||
region.h += abs(offset.y);
|
||||
if (offset.x < 0) bbox.min.x += (int32_t) offset.x;
|
||||
else bbox.max.x += offset.x;
|
||||
|
||||
if (offset.y < 0) bbox.min.y += (int32_t) offset.y;
|
||||
else bbox.max.y += offset.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -99,15 +99,15 @@ static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32
|
|||
}
|
||||
|
||||
|
||||
static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c)
|
||||
static bool avxRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
auto h = bbox.h();
|
||||
auto w = bbox.w();
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
|
||||
uint32_t ialpha = 255 - c.a;
|
||||
|
||||
|
@ -145,7 +145,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, c
|
|||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = ~c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
@ -160,14 +160,12 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, c
|
|||
|
||||
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
uint32_t src;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
|
@ -213,7 +211,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
|
||||
else src = c.a;
|
||||
|
|
|
@ -94,13 +94,11 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int
|
|||
|
||||
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
uint32_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
@ -112,7 +110,7 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, c
|
|||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
|
||||
else src = c.a;
|
||||
|
@ -126,29 +124,26 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, c
|
|||
}
|
||||
|
||||
|
||||
static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c)
|
||||
static bool inline cRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = 255 - c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
for (uint32_t y = 0; y < bbox.h(); ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = ~c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
for (uint32_t y = 0; y < bbox.h(); ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) {
|
||||
*dst = c.a + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,8 +91,6 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3
|
|||
|
||||
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
|
@ -100,7 +98,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
uint8x8_t *vDst = nullptr;
|
||||
uint16_t align;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
||||
|
@ -132,7 +130,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
|
||||
else src = c.a;
|
||||
|
@ -146,15 +144,15 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
}
|
||||
|
||||
|
||||
static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c)
|
||||
static bool neonRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
auto h = bbox.h();
|
||||
auto w = bbox.w();
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = 255 - c.a;
|
||||
|
||||
auto vColor = vdup_n_u32(color);
|
||||
|
@ -185,7 +183,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
|
|||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = ~c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
|
|
@ -51,100 +51,57 @@ static float dxdya, dxdyb, dudya, dvdya;
|
|||
static float xa, xb, ua, va;
|
||||
|
||||
|
||||
//Y Range exception handling
|
||||
static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
|
||||
static inline int32_t _modf(float v)
|
||||
{
|
||||
int32_t regionTop, regionBottom;
|
||||
|
||||
if (region) {
|
||||
regionTop = region->min.y;
|
||||
regionBottom = region->max.y;
|
||||
} else {
|
||||
regionTop = image->rle->spans->y;
|
||||
regionBottom = image->rle->spans[image->rle->size - 1].y;
|
||||
}
|
||||
|
||||
if (yStart < regionTop) yStart = regionTop;
|
||||
if (yEnd > regionBottom) yEnd = regionBottom;
|
||||
|
||||
return yEnd > yStart;
|
||||
return 255 - ((int(v * 256.0f)) & 255);
|
||||
}
|
||||
|
||||
|
||||
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
|
||||
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
|
||||
{
|
||||
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
|
||||
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
|
||||
{
|
||||
float _dudx = dudx, _dvdx = dvdx;
|
||||
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
||||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||
auto sbuf = image->buf32;
|
||||
auto sbuf = image.buf32;
|
||||
auto dbuf = surface->buf32;
|
||||
int32_t sw = static_cast<int32_t>(image->w);
|
||||
int32_t sh = static_cast<int32_t>(image->h);
|
||||
int32_t sw = static_cast<int32_t>(image.w);
|
||||
int32_t sh = static_cast<int32_t>(image.h);
|
||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
int32_t vv = 0, uu = 0;
|
||||
int32_t minx = INT32_MAX, maxx = 0;
|
||||
float dx, u, v, iptr;
|
||||
float dx, u, v;
|
||||
uint32_t* buf;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return;
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
||||
if (region) {
|
||||
minx = region->min.x;
|
||||
maxx = region->max.x;
|
||||
} else {
|
||||
span = image->rle->spans;
|
||||
while (span->y < yStart) {
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
if (yStart < bbox.min.y) yStart = bbox.min.y;
|
||||
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
|
||||
|
||||
y = yStart;
|
||||
|
||||
while (y < yEnd) {
|
||||
x1 = (int32_t)_xa;
|
||||
x2 = (int32_t)_xb;
|
||||
|
||||
if (!region) {
|
||||
minx = INT32_MAX;
|
||||
maxx = 0;
|
||||
//one single row, could be consisted of multiple spans.
|
||||
while (span->y == y && spanIdx < image->rle->size) {
|
||||
if (minx > span->x) minx = span->x;
|
||||
if (maxx < span->x + span->len) maxx = span->x + span->len;
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
if (x1 < minx) x1 = minx;
|
||||
if (x2 > maxx) x2 = maxx;
|
||||
x1 = std::max((int32_t)_xa, bbox.min.x);
|
||||
x2 = std::min((int32_t)_xb, bbox.max.x);
|
||||
|
||||
//Anti-Aliasing frames
|
||||
if (aaSpans) {
|
||||
ay = y - aaSpans->yStart;
|
||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
||||
}
|
||||
|
||||
//Range allowed
|
||||
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
|
||||
if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
|
||||
|
||||
//Perform subtexel pre-stepping on UV
|
||||
dx = 1 - (_xa - x1);
|
||||
u = _ua + dx * _dudx;
|
||||
v = _va + dx * _dvdx;
|
||||
|
||||
buf = dbuf + ((y * surface->stride) + x1);
|
||||
|
||||
x = x1;
|
||||
|
||||
//Draw horizontal line
|
||||
|
@ -152,29 +109,29 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
uu = (int) u;
|
||||
vv = (int) v;
|
||||
|
||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
||||
if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
ar = _modf(u);
|
||||
ab = _modf(v);
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * image->stride) + uu);
|
||||
px = *(sbuf + (vv * image.stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
||||
int px2 = *(sbuf + (vv * image.stride) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
||||
int px2 = *(sbuf + (irv * image.stride) + uu);
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
||||
int px3 = *(sbuf + (irv * image.stride) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
|
@ -195,8 +152,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
_ua += _dudya;
|
||||
_va += _dvdya;
|
||||
|
||||
if (!region && spanIdx >= image->rle->size) break;
|
||||
|
||||
++y;
|
||||
}
|
||||
xa = _xa;
|
||||
|
@ -206,122 +161,95 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
}
|
||||
|
||||
|
||||
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
|
||||
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
|
||||
{
|
||||
float _dudx = dudx, _dvdx = dvdx;
|
||||
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
||||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||
auto sbuf = image->buf32;
|
||||
auto sbuf = image.buf32;
|
||||
auto dbuf = surface->buf32;
|
||||
int32_t sw = static_cast<int32_t>(image->w);
|
||||
int32_t sh = static_cast<int32_t>(image->h);
|
||||
int32_t sw = static_cast<int32_t>(image.w);
|
||||
int32_t sh = static_cast<int32_t>(image.h);
|
||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
int32_t vv = 0, uu = 0;
|
||||
int32_t minx = INT32_MAX, maxx = 0;
|
||||
float dx, u, v, iptr;
|
||||
float dx, u, v;
|
||||
uint32_t* buf;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
//for matting(composition)
|
||||
auto csize = matting ? surface->compositor->image.channelSize: 0;
|
||||
auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
|
||||
uint8_t* cmp = nullptr;
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return;
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
||||
if (region) {
|
||||
minx = region->min.x;
|
||||
maxx = region->max.x;
|
||||
} else {
|
||||
span = image->rle->spans;
|
||||
while (span->y < yStart) {
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
if (yStart < bbox.min.y) yStart = bbox.min.y;
|
||||
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
|
||||
|
||||
y = yStart;
|
||||
|
||||
while (y < yEnd) {
|
||||
x1 = (int32_t)_xa;
|
||||
x2 = (int32_t)_xb;
|
||||
|
||||
if (!region) {
|
||||
minx = INT32_MAX;
|
||||
maxx = 0;
|
||||
//one single row, could be consisted of multiple spans.
|
||||
while (span->y == y && spanIdx < image->rle->size) {
|
||||
if (minx > span->x) minx = span->x;
|
||||
if (maxx < span->x + span->len) maxx = span->x + span->len;
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
if (x1 < minx) x1 = minx;
|
||||
if (x2 > maxx) x2 = maxx;
|
||||
x1 = std::max((int32_t)_xa, bbox.min.x);
|
||||
x2 = std::min((int32_t)_xb, bbox.max.x);
|
||||
|
||||
//Anti-Aliasing frames
|
||||
if (aaSpans) {
|
||||
ay = y - aaSpans->yStart;
|
||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
||||
}
|
||||
|
||||
//Range allowed
|
||||
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
|
||||
if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
|
||||
|
||||
//Perform subtexel pre-stepping on UV
|
||||
dx = 1 - (_xa - x1);
|
||||
u = _ua + dx * _dudx;
|
||||
v = _va + dx * _dvdx;
|
||||
|
||||
buf = dbuf + ((y * surface->stride) + x1);
|
||||
|
||||
x = x1;
|
||||
|
||||
if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize];
|
||||
|
||||
if (opacity == 255) {
|
||||
const auto fullOpacity = (opacity == 255);
|
||||
|
||||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
vv = (int) v;
|
||||
|
||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
||||
if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
|
||||
|
||||
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
|
||||
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
|
||||
ar = _modf(u);
|
||||
ab = _modf(v);
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * image->stride) + uu);
|
||||
px = *(sbuf + (vv * image.stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
||||
int px2 = *(sbuf + (vv * image.stride) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
||||
int px2 = *(sbuf + (irv * image.stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
||||
int px3 = *(sbuf + (irv * image.stride) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
uint32_t src;
|
||||
if (matting) {
|
||||
src = ALPHA_BLEND(px, alpha(cmp));
|
||||
auto a = alpha(cmp);
|
||||
src = fullOpacity ? ALPHA_BLEND(px, a) : ALPHA_BLEND(px, MULTIPLY(opacity, a));
|
||||
cmp += csize;
|
||||
} else {
|
||||
src = px;
|
||||
src = fullOpacity ? px : ALPHA_BLEND(px, opacity);
|
||||
}
|
||||
*buf = src + ALPHA_BLEND(*buf, IA(src));
|
||||
++buf;
|
||||
|
@ -330,55 +258,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
u += _dudx;
|
||||
v += _dvdx;
|
||||
}
|
||||
} else {
|
||||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
vv = (int) v;
|
||||
|
||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
||||
|
||||
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
|
||||
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
uint32_t src;
|
||||
if (matting) {
|
||||
src = ALPHA_BLEND(px, MULTIPLY(opacity, alpha(cmp)));
|
||||
cmp += csize;
|
||||
} else {
|
||||
src = ALPHA_BLEND(px, opacity);
|
||||
}
|
||||
*buf = src + ALPHA_BLEND(*buf, IA(src));
|
||||
++buf;
|
||||
|
||||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Step along both edges
|
||||
|
@ -387,8 +266,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
_ua += _dudya;
|
||||
_va += _dvdya;
|
||||
|
||||
if (!region && spanIdx >= image->rle->size) break;
|
||||
|
||||
++y;
|
||||
}
|
||||
xa = _xa;
|
||||
|
@ -399,7 +276,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
|
||||
|
||||
/* This mapping algorithm is based on Mikael Kalms's. */
|
||||
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
|
||||
static void _rasterPolygonImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
|
||||
{
|
||||
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
|
||||
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
|
||||
|
@ -460,7 +337,6 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
|
||||
if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
|
||||
|
||||
auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
|
||||
auto compositing = _compositing(surface); //Composition required
|
||||
auto blending = _blending(surface); //Blending required
|
||||
|
||||
|
@ -479,7 +355,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
|
||||
//Draw upper segment if possibly visible
|
||||
if (yi[0] < yi[1]) {
|
||||
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
|
||||
off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
|
||||
xa += (off_y * dxdya);
|
||||
ua += (off_y * dudya);
|
||||
va += (off_y * dvdya);
|
||||
|
@ -489,18 +365,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
xb = x[0] + dy * dxdyb + (off_y * dxdyb);
|
||||
|
||||
if (compositing) {
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1);
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 1);
|
||||
} else if (blending) {
|
||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
|
||||
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
|
||||
} else {
|
||||
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
|
||||
_rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
|
||||
}
|
||||
upper = true;
|
||||
}
|
||||
//Draw lower segment if possibly visible
|
||||
if (yi[1] < yi[2]) {
|
||||
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
|
||||
off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
|
||||
if (!upper) {
|
||||
xa += (off_y * dxdya);
|
||||
ua += (off_y * dudya);
|
||||
|
@ -510,12 +386,12 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
dxdyb = dxdy[2];
|
||||
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
|
||||
if (compositing) {
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2);
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 2);
|
||||
} else if (blending) {
|
||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
|
||||
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
|
||||
} else {
|
||||
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
|
||||
_rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
|
||||
}
|
||||
}
|
||||
//Longer edge is on the right side
|
||||
|
@ -527,7 +403,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
|
||||
//Draw upper segment if possibly visible
|
||||
if (yi[0] < yi[1]) {
|
||||
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
|
||||
off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
|
||||
xb += (off_y *dxdyb);
|
||||
|
||||
// Set slopes along left edge and perform subpixel pre-stepping
|
||||
|
@ -540,18 +416,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
va = v[0] + dy * dvdya + (off_y * dvdya);
|
||||
|
||||
if (compositing) {
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3);
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 3);
|
||||
} else if (blending) {
|
||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
|
||||
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
|
||||
} else {
|
||||
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
|
||||
_rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
|
||||
}
|
||||
upper = true;
|
||||
}
|
||||
//Draw lower segment if possibly visible
|
||||
if (yi[1] < yi[2]) {
|
||||
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
|
||||
off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
|
||||
if (!upper) xb += (off_y *dxdyb);
|
||||
|
||||
// Set slopes along left edge and perform subpixel pre-stepping
|
||||
|
@ -564,25 +440,20 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
va = v[1] + dy * dvdya + (off_y * dvdya);
|
||||
|
||||
if (compositing) {
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4);
|
||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
|
||||
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 4);
|
||||
} else if (blending) {
|
||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
|
||||
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
|
||||
} else {
|
||||
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
|
||||
_rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
|
||||
static AASpans* _AASpans(int yStart, int yEnd)
|
||||
{
|
||||
auto yStart = static_cast<int>(ymin);
|
||||
auto yEnd = static_cast<int>(ymax);
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return nullptr;
|
||||
|
||||
auto aaSpans = tvg::malloc<AASpans*>(sizeof(AASpans));
|
||||
aaSpans->yStart = yStart;
|
||||
aaSpans->yEnd = yEnd;
|
||||
|
@ -661,14 +532,9 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
|
|||
ptx[1] = tx[1]; \
|
||||
} while (0)
|
||||
|
||||
struct Point
|
||||
{
|
||||
int32_t x, y;
|
||||
};
|
||||
|
||||
int32_t y = 0;
|
||||
Point pEdge = {-1, -1}; //previous edge point
|
||||
Point edgeDiff = {0, 0}; //temporary used for point distance
|
||||
SwPoint pEdge = {-1, -1}; //previous edge point
|
||||
SwPoint edgeDiff = {0, 0}; //temporary used for point distance
|
||||
|
||||
/* store bigger to tx[0] between prev and current edge's x positions. */
|
||||
int32_t tx[2] = {0, 0};
|
||||
|
@ -791,29 +657,24 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
|
|||
}
|
||||
|
||||
|
||||
static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
||||
static void _apply(SwSurface* surface, AASpans* aaSpans)
|
||||
{
|
||||
auto end = surface->buf32 + surface->h * surface->stride;
|
||||
auto buf = surface->buf32 + surface->stride * aaSpans->yStart;
|
||||
auto y = aaSpans->yStart;
|
||||
uint32_t pixel;
|
||||
auto line = aaSpans->lines;
|
||||
uint32_t pix;
|
||||
uint32_t* dst;
|
||||
int32_t pos;
|
||||
|
||||
//left side
|
||||
_calcAAEdge(aaSpans, 0);
|
||||
//right side
|
||||
_calcAAEdge(aaSpans, 1);
|
||||
_calcAAEdge(aaSpans, 0); //left side
|
||||
_calcAAEdge(aaSpans, 1); //right side
|
||||
|
||||
while (y < aaSpans->yEnd) {
|
||||
auto line = &aaSpans->lines[y - aaSpans->yStart];
|
||||
auto width = line->x[1] - line->x[0];
|
||||
if (width > 0) {
|
||||
auto offset = y * surface->stride;
|
||||
|
||||
if (line->x[1] - line->x[0] > 0) {
|
||||
//Left edge
|
||||
dst = surface->buf32 + (offset + line->x[0]);
|
||||
if (line->x[0] > 1) pixel = *(dst - 1);
|
||||
else pixel = *dst;
|
||||
dst = buf + line->x[0];
|
||||
pix = *(dst - ((line->x[0] > 1) ? 1 : 0));
|
||||
pos = 1;
|
||||
|
||||
//exceptional handling. out of memory bound.
|
||||
|
@ -822,34 +683,31 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
|||
}
|
||||
|
||||
while (pos <= line->length[0]) {
|
||||
*dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
|
||||
*dst = INTERPOLATE(*dst, pix, line->coverage[0] * pos);
|
||||
++dst;
|
||||
++pos;
|
||||
}
|
||||
|
||||
//Right edge
|
||||
dst = surface->buf32 + offset + line->x[1] - 1;
|
||||
|
||||
if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
|
||||
else pixel = *dst;
|
||||
dst = buf + line->x[1] - 1;
|
||||
pix = *(dst + (line->x[1] < (int32_t)(surface->w - 1) ? 1 : 0));
|
||||
pos = line->length[1];
|
||||
|
||||
//exceptional handling. out of memory bound.
|
||||
if (dst - pos < surface->buf32) --pos;
|
||||
|
||||
while (pos > 0) {
|
||||
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
|
||||
*dst = INTERPOLATE(*dst, pix, 255 - (line->coverage[1] * pos));
|
||||
--dst;
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
y++;
|
||||
buf += surface->stride;
|
||||
++line;
|
||||
++y;
|
||||
}
|
||||
|
||||
tvg::free(aaSpans->lines);
|
||||
tvg::free(aaSpans);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -863,23 +721,19 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
|||
| / |
|
||||
3 -- 2
|
||||
*/
|
||||
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity)
|
||||
bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
|
||||
{
|
||||
if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Exceptions: No dedicated drawing area?
|
||||
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
|
||||
|
||||
/* Prepare vertices.
|
||||
shift XY coordinates to match the sub-pixeling technique. */
|
||||
//Prepare vertices. Shift XY coordinates to match the sub-pixeling technique.
|
||||
Vertex vertices[4];
|
||||
vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
|
||||
vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
|
||||
vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
|
||||
vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
|
||||
vertices[1] = {{float(image.w), 0.0f}, {float(image.w), 0.0f}};
|
||||
vertices[2] = {{float(image.w), float(image.h)}, {float(image.w), float(image.h)}};
|
||||
vertices[3] = {{0.0f, float(image.h)}, {0.0f, float(image.h)}};
|
||||
|
||||
float ys = FLT_MAX, ye = -1.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
@ -888,8 +742,9 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
|
||||
}
|
||||
|
||||
auto aaSpans = _AASpans(ys, ye, image, region);
|
||||
if (!aaSpans) return true;
|
||||
auto yStart = std::max(static_cast<int>(ys), bbox.min.y);
|
||||
auto yEnd = std::min(static_cast<int>(ye), bbox.max.y);
|
||||
auto aaSpans = rightAngle(transform) ? nullptr : _AASpans(yStart, yEnd);
|
||||
|
||||
Polygon polygon;
|
||||
|
||||
|
@ -898,19 +753,20 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
polygon.vertex[1] = vertices[1];
|
||||
polygon.vertex[2] = vertices[3];
|
||||
|
||||
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
|
||||
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
|
||||
|
||||
//Draw the second polygon
|
||||
polygon.vertex[0] = vertices[1];
|
||||
polygon.vertex[1] = vertices[2];
|
||||
polygon.vertex[2] = vertices[3];
|
||||
|
||||
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
|
||||
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
|
||||
|
||||
#if 0
|
||||
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
|
||||
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
|
||||
}
|
||||
#endif
|
||||
return _apply(surface, aaSpans);
|
||||
if (aaSpans) _apply(surface, aaSpans);
|
||||
return true;
|
||||
}
|
|
@ -40,7 +40,7 @@ struct SwTask : Task
|
|||
{
|
||||
SwSurface* surface = nullptr;
|
||||
SwMpool* mpool = nullptr;
|
||||
SwBBox bbox; //Rendering Region
|
||||
RenderRegion bbox; //Rendering Region
|
||||
Matrix transform;
|
||||
Array<RenderData> clips;
|
||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||
|
@ -48,22 +48,11 @@ struct SwTask : Task
|
|||
bool pushed = false; //Pushed into task list?
|
||||
bool disposed = false; //Disposed task?
|
||||
|
||||
RenderRegion bounds()
|
||||
const RenderRegion& bounds()
|
||||
{
|
||||
//Can we skip the synchronization?
|
||||
done();
|
||||
|
||||
RenderRegion region;
|
||||
|
||||
//Range over?
|
||||
region.x = bbox.min.x > 0 ? bbox.min.x : 0;
|
||||
region.y = bbox.min.y > 0 ? bbox.min.y : 0;
|
||||
region.w = bbox.max.x - region.x;
|
||||
region.h = bbox.max.y - region.y;
|
||||
if (region.w < 0) region.w = 0;
|
||||
if (region.h < 0) region.h = 0;
|
||||
|
||||
return region;
|
||||
return bbox;
|
||||
}
|
||||
|
||||
virtual void dispose() = 0;
|
||||
|
@ -117,7 +106,7 @@ struct SwShapeTask : SwTask
|
|||
}
|
||||
|
||||
auto strokeWidth = validStrokeWidth(clipper);
|
||||
SwBBox renderRegion{};
|
||||
RenderRegion renderBox{};
|
||||
auto updateShape = flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip);
|
||||
auto updateFill = false;
|
||||
|
||||
|
@ -126,11 +115,11 @@ struct SwShapeTask : SwTask
|
|||
updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill);
|
||||
if (updateShape) shapeReset(&shape);
|
||||
if (updateFill || clipper) {
|
||||
if (shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
|
||||
if (shapePrepare(&shape, rshape, transform, bbox, renderBox, mpool, tid, clips.count > 0 ? true : false)) {
|
||||
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
|
||||
} else {
|
||||
updateFill = false;
|
||||
renderRegion.reset();
|
||||
renderBox.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +135,7 @@ struct SwShapeTask : SwTask
|
|||
if (updateShape || flags & RenderUpdateFlag::Stroke) {
|
||||
if (strokeWidth > 0.0f) {
|
||||
shapeResetStroke(&shape, rshape, transform);
|
||||
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err;
|
||||
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderBox, mpool, tid)) goto err;
|
||||
if (auto fill = rshape->strokeFill()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||
if (ctable) shapeResetStrokeFill(&shape);
|
||||
|
@ -168,7 +157,7 @@ struct SwShapeTask : SwTask
|
|||
if (!clipShapeRle && !clipStrokeRle) goto err;
|
||||
}
|
||||
|
||||
bbox = renderRegion; //sync
|
||||
bbox = renderBox; //sync
|
||||
|
||||
return;
|
||||
|
||||
|
@ -199,7 +188,7 @@ struct SwImageTask : SwTask
|
|||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
auto clipRegion = bbox;
|
||||
auto clipBox = bbox;
|
||||
|
||||
//Convert colorspace if it's not aligned.
|
||||
rasterConvertCS(source, surface->cs);
|
||||
|
@ -216,7 +205,7 @@ struct SwImageTask : SwTask
|
|||
imageReset(&image);
|
||||
if (!image.data || image.w == 0 || image.h == 0) goto end;
|
||||
|
||||
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
|
||||
if (!imagePrepare(&image, transform, clipBox, bbox, mpool, tid)) goto end;
|
||||
|
||||
if (clips.count > 0) {
|
||||
if (!imageGenRle(&image, bbox, false)) goto end;
|
||||
|
@ -245,26 +234,26 @@ struct SwImageTask : SwTask
|
|||
};
|
||||
|
||||
|
||||
static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
|
||||
static void _renderFill(SwShapeTask* task, SwSurface* surface)
|
||||
{
|
||||
if (auto fill = task->rshape->fill) {
|
||||
rasterGradientShape(surface, &task->shape, fill, opacity);
|
||||
rasterGradientShape(surface, &task->shape, fill, task->opacity);
|
||||
} else {
|
||||
RenderColor c;
|
||||
task->rshape->fillColor(&c.r, &c.g, &c.b, &c.a);
|
||||
c.a = MULTIPLY(opacity, c.a);
|
||||
c.a = MULTIPLY(task->opacity, c.a);
|
||||
if (c.a > 0) rasterShape(surface, &task->shape, c);
|
||||
}
|
||||
}
|
||||
|
||||
static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
|
||||
static void _renderStroke(SwShapeTask* task, SwSurface* surface)
|
||||
{
|
||||
if (auto strokeFill = task->rshape->strokeFill()) {
|
||||
rasterGradientStroke(surface, &task->shape, strokeFill, opacity);
|
||||
rasterGradientStroke(surface, &task->shape, strokeFill, task->opacity);
|
||||
} else {
|
||||
RenderColor c;
|
||||
if (task->rshape->strokeFill(&c.r, &c.g, &c.b, &c.a)) {
|
||||
c.a = MULTIPLY(opacity, c.a);
|
||||
c.a = MULTIPLY(task->opacity, c.a);
|
||||
if (c.a > 0) rasterStroke(surface, &task->shape, c);
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +348,7 @@ bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h,
|
|||
|
||||
bool SwRenderer::preUpdate()
|
||||
{
|
||||
return true;
|
||||
return surface != nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -371,7 +360,7 @@ bool SwRenderer::postUpdate()
|
|||
|
||||
bool SwRenderer::preRender()
|
||||
{
|
||||
return true;
|
||||
return surface != nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -394,12 +383,6 @@ bool SwRenderer::postRender()
|
|||
rasterUnpremultiply(surface);
|
||||
}
|
||||
|
||||
ARRAY_FOREACH(p, tasks) {
|
||||
if ((*p)->disposed) delete(*p);
|
||||
else (*p)->pushed = false;
|
||||
}
|
||||
tasks.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -411,7 +394,32 @@ bool SwRenderer::renderImage(RenderData data)
|
|||
|
||||
if (task->opacity == 0) return true;
|
||||
|
||||
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
|
||||
//Outside of the viewport, skip the rendering
|
||||
auto& bbox = task->bbox;
|
||||
if (bbox.invalid() || bbox.x() >= surface->w || bbox.y() >= surface->h) return true;
|
||||
|
||||
auto& image = task->image;
|
||||
|
||||
//RLE Image
|
||||
if (image.rle) {
|
||||
if (image.direct) return rasterDirectRleImage(surface, image, task->opacity);
|
||||
else if (image.scaled) return rasterScaledRleImage(surface, image, task->transform, bbox, task->opacity);
|
||||
else {
|
||||
//create a intermediate buffer for rle clipping
|
||||
auto cmp = request(sizeof(pixel_t), false);
|
||||
cmp->compositor->method = MaskMethod::None;
|
||||
cmp->compositor->valid = true;
|
||||
cmp->compositor->image.rle = image.rle;
|
||||
rasterClear(cmp, bbox.x(), bbox.y(), bbox.w(), bbox.h(), 0);
|
||||
rasterTexmapPolygon(cmp, image, task->transform, bbox, 255);
|
||||
return rasterDirectRleImage(surface, cmp->compositor->image, task->opacity);
|
||||
}
|
||||
//Whole Image
|
||||
} else {
|
||||
if (image.direct) return rasterDirectImage(surface, image, bbox, task->opacity);
|
||||
else if (image.scaled) return rasterScaledImage(surface, image, task->transform, bbox, task->opacity);
|
||||
else return rasterTexmapPolygon(surface, image, task->transform, bbox, task->opacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -426,11 +434,11 @@ bool SwRenderer::renderShape(RenderData data)
|
|||
|
||||
//Main raster stage
|
||||
if (task->rshape->strokeFirst()) {
|
||||
_renderStroke(task, surface, task->opacity);
|
||||
_renderFill(task, surface, task->opacity);
|
||||
_renderStroke(task, surface);
|
||||
_renderFill(task, surface);
|
||||
} else {
|
||||
_renderFill(task, surface, task->opacity);
|
||||
_renderStroke(task, surface, task->opacity);
|
||||
_renderFill(task, surface);
|
||||
_renderStroke(task, surface);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -570,38 +578,19 @@ SwSurface* SwRenderer::request(int channelSize, bool square)
|
|||
|
||||
RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags)
|
||||
{
|
||||
auto x = region.x;
|
||||
auto y = region.y;
|
||||
auto w = region.w;
|
||||
auto h = region.h;
|
||||
auto sw = static_cast<int32_t>(surface->w);
|
||||
auto sh = static_cast<int32_t>(surface->h);
|
||||
|
||||
//Out of boundary
|
||||
if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
|
||||
auto bbox = RenderRegion::intersect(region, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}});
|
||||
if (bbox.invalid()) return nullptr;
|
||||
|
||||
auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing));
|
||||
|
||||
//Boundary Check
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (x + w > sw) w = (sw - x);
|
||||
if (y + h > sh) h = (sh - y);
|
||||
|
||||
if (w == 0 || h == 0) return nullptr;
|
||||
|
||||
cmp->compositor->recoverSfc = surface;
|
||||
cmp->compositor->recoverCmp = surface->compositor;
|
||||
cmp->compositor->valid = false;
|
||||
cmp->compositor->bbox.min.x = x;
|
||||
cmp->compositor->bbox.min.y = y;
|
||||
cmp->compositor->bbox.max.x = x + w;
|
||||
cmp->compositor->bbox.max.y = y + h;
|
||||
cmp->compositor->bbox = bbox;
|
||||
|
||||
/* TODO: Currently, only blending might work.
|
||||
Blending and composition must be handled together. */
|
||||
auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000;
|
||||
rasterClear(cmp, x, y, w, h, color);
|
||||
rasterClear(cmp, bbox.x(), bbox.y(), bbox.w(), bbox.h(), color);
|
||||
|
||||
//Switch render target
|
||||
surface = cmp;
|
||||
|
@ -626,8 +615,7 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
|
|||
|
||||
//Default is alpha blending
|
||||
if (p->method == MaskMethod::None) {
|
||||
auto m = tvg::identity();
|
||||
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
|
||||
return rasterDirectImage(surface, p->image, p->bbox, p->opacity);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -722,6 +710,20 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
|
|||
{
|
||||
if (!surface) return task;
|
||||
if (flags == RenderUpdateFlag::None) return task;
|
||||
if ((transform.e11 == 0.0f && transform.e12 == 0.0f) || (transform.e21 == 0.0f && transform.e22 == 0.0f)) return task; //zero size?
|
||||
|
||||
task->surface = surface;
|
||||
task->mpool = mpool;
|
||||
task->bbox = RenderRegion::intersect(vport, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}});
|
||||
task->transform = transform;
|
||||
task->clips = clips;
|
||||
task->opacity = opacity;
|
||||
task->flags = flags;
|
||||
|
||||
if (!task->pushed) {
|
||||
task->pushed = true;
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
//TODO: Failed threading them. It would be better if it's possible.
|
||||
//See: https://github.com/thorvg/thorvg/issues/1409
|
||||
|
@ -730,27 +732,6 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
|
|||
static_cast<SwTask*>(*p)->done();
|
||||
}
|
||||
|
||||
task->clips = clips;
|
||||
task->transform = transform;
|
||||
|
||||
//zero size?
|
||||
if (task->transform.e11 == 0.0f && task->transform.e12 == 0.0f) return task; //zero width
|
||||
if (task->transform.e21 == 0.0f && task->transform.e22 == 0.0f) return task; //zero height
|
||||
|
||||
task->opacity = opacity;
|
||||
task->surface = surface;
|
||||
task->mpool = mpool;
|
||||
task->flags = flags;
|
||||
task->bbox.min.x = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
|
||||
task->bbox.min.y = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
|
||||
task->bbox.max.x = std::min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
|
||||
task->bbox.max.y = std::min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
|
||||
|
||||
if (!task->pushed) {
|
||||
task->pushed = true;
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
TaskScheduler::request(task);
|
||||
|
||||
return task;
|
||||
|
|
|
@ -56,6 +56,8 @@ public:
|
|||
bool sync() override;
|
||||
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
|
||||
|
||||
SwSurface* request(int channelSize, bool square);
|
||||
|
||||
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
|
||||
bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override;
|
||||
bool endComposite(RenderCompositor* cmp) override;
|
||||
|
@ -80,7 +82,6 @@ private:
|
|||
SwRenderer();
|
||||
~SwRenderer();
|
||||
|
||||
SwSurface* request(int channelSize, bool square);
|
||||
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
|
||||
};
|
||||
|
||||
|
|
|
@ -324,27 +324,18 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
|
|||
if (!rw.antiAlias) coverage = 255;
|
||||
|
||||
//see whether we can add this span to the current list
|
||||
if (rle->size > 0) {
|
||||
auto span = rle->spans + rle->size - 1;
|
||||
if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
|
||||
if (!rle->spans.empty()) {
|
||||
auto& span = rle->spans.last();
|
||||
if ((span.coverage == coverage) && (span.y == y) && (span.x + span.len == x)) {
|
||||
//Clip x range
|
||||
SwCoord xOver = 0;
|
||||
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
|
||||
if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
|
||||
span->len += (aCount + xOver);
|
||||
span.len += (aCount + xOver);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//span pool is full, grow it.
|
||||
if (rle->size >= rle->alloc) {
|
||||
auto newSize = (rle->size > 0) ? (rle->size * 2) : 256;
|
||||
if (rle->alloc < newSize) {
|
||||
rle->alloc = newSize;
|
||||
rle->spans = tvg::realloc<SwSpan*>(rle->spans, rle->alloc * sizeof(SwSpan));
|
||||
}
|
||||
}
|
||||
|
||||
//Clip x range
|
||||
SwCoord xOver = 0;
|
||||
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
|
||||
|
@ -357,12 +348,7 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
|
|||
if (aCount + xOver <= 0) return;
|
||||
|
||||
//add a span to the current list
|
||||
auto span = rle->spans + rle->size;
|
||||
span->x = x;
|
||||
span->y = y;
|
||||
span->len = (aCount + xOver);
|
||||
span->coverage = coverage;
|
||||
rle->size++;
|
||||
rle->spans.next() = {(uint16_t)x, (uint16_t)y, uint16_t(aCount + xOver), (uint8_t)coverage};
|
||||
}
|
||||
|
||||
|
||||
|
@ -737,110 +723,11 @@ static bool _genRle(RleWorker& rw)
|
|||
}
|
||||
|
||||
|
||||
static SwSpan* _intersectSpansRegion(const SwRle *clip, const SwRle *target, SwSpan *outSpans, uint32_t outSpansCnt)
|
||||
{
|
||||
auto out = outSpans;
|
||||
auto spans = target->spans;
|
||||
auto end = target->spans + target->size;
|
||||
auto clipSpans = clip->spans;
|
||||
auto clipEnd = clip->spans + clip->size;
|
||||
|
||||
while (spans < end && clipSpans < clipEnd) {
|
||||
//align y-coordinates.
|
||||
if (clipSpans->y > spans->y) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
if (spans->y > clipSpans->y) {
|
||||
++clipSpans;
|
||||
continue;
|
||||
}
|
||||
|
||||
//Try clipping with all clip spans which have a same y-coordinate.
|
||||
auto temp = clipSpans;
|
||||
while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
|
||||
auto sx1 = spans->x;
|
||||
auto sx2 = sx1 + spans->len;
|
||||
auto cx1 = temp->x;
|
||||
auto cx2 = cx1 + temp->len;
|
||||
|
||||
//The span must be left(x1) to right(x2) direction. Not intersected.
|
||||
if (cx2 < sx1 || sx2 < cx1) {
|
||||
++temp;
|
||||
continue;
|
||||
}
|
||||
|
||||
//clip span region.
|
||||
auto x = sx1 > cx1 ? sx1 : cx1;
|
||||
auto len = (sx2 < cx2 ? sx2 : cx2) - x;
|
||||
if (len > 0) {
|
||||
out->x = x;
|
||||
out->y = temp->y;
|
||||
out->len = len;
|
||||
out->coverage = (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8);
|
||||
++out;
|
||||
--outSpansCnt;
|
||||
}
|
||||
++temp;
|
||||
}
|
||||
++spans;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
|
||||
{
|
||||
auto out = outSpans;
|
||||
auto spans = targetRle->spans;
|
||||
auto end = targetRle->spans + targetRle->size;
|
||||
auto minx = static_cast<int16_t>(bbox->min.x);
|
||||
auto miny = static_cast<int16_t>(bbox->min.y);
|
||||
auto maxx = minx + static_cast<int16_t>(bbox->max.x - bbox->min.x) - 1;
|
||||
auto maxy = miny + static_cast<int16_t>(bbox->max.y - bbox->min.y) - 1;
|
||||
|
||||
while (outSpansCnt > 0 && spans < end) {
|
||||
if (spans->y > maxy) {
|
||||
spans = end;
|
||||
break;
|
||||
}
|
||||
if (spans->y < miny || spans->x > maxx || spans->x + spans->len <= minx) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
if (spans->x < minx) {
|
||||
out->len = (spans->len - (minx - spans->x)) < (maxx - minx + 1) ? (spans->len - (minx - spans->x)) : (maxx - minx + 1);
|
||||
out->x = minx;
|
||||
}
|
||||
else {
|
||||
out->x = spans->x;
|
||||
out->len = spans->len < (maxx - spans->x + 1) ? spans->len : (maxx - spans->x + 1);
|
||||
}
|
||||
if (out->len > 0) {
|
||||
out->y = spans->y;
|
||||
out->coverage = spans->coverage;
|
||||
++out;
|
||||
--outSpansCnt;
|
||||
}
|
||||
++spans;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
|
||||
{
|
||||
tvg::free(rle->spans);
|
||||
rle->spans = clippedSpans;
|
||||
rle->size = rle->alloc = size;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const RenderRegion& bbox, bool antiAlias)
|
||||
{
|
||||
if (!outline) return nullptr;
|
||||
|
||||
|
@ -861,8 +748,8 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
|
|||
rw.area = 0;
|
||||
rw.cover = 0;
|
||||
rw.invalid = true;
|
||||
rw.cellMin = renderRegion.min;
|
||||
rw.cellMax = renderRegion.max;
|
||||
rw.cellMin = {bbox.min.x, bbox.min.y};
|
||||
rw.cellMax = {bbox.max.x, bbox.max.y};
|
||||
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
|
||||
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
|
||||
rw.outline = const_cast<SwOutline*>(outline);
|
||||
|
@ -870,8 +757,9 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
|
|||
rw.bandShoot = 0;
|
||||
rw.antiAlias = antiAlias;
|
||||
|
||||
if (!rle) rw.rle = tvg::calloc<SwRle*>(1, sizeof(SwRle));
|
||||
if (!rle) rw.rle = new SwRle;
|
||||
else rw.rle = rle;
|
||||
rw.rle->spans.reserve(256);
|
||||
|
||||
//Generate RLE
|
||||
Band bands[BAND_SIZE];
|
||||
|
@ -934,7 +822,10 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
|
|||
|
||||
/* This is too complex for a single scanline; there must
|
||||
be some problems */
|
||||
if (middle == bottom) goto error;
|
||||
if (middle == bottom) {
|
||||
rleFree(rw.rle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (bottom - top >= rw.bandSize) ++rw.bandShoot;
|
||||
|
||||
|
@ -945,34 +836,26 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
|
|||
++band;
|
||||
}
|
||||
}
|
||||
|
||||
if (rw.bandShoot > 8 && rw.bandSize > 16)
|
||||
if (rw.bandShoot > 8 && rw.bandSize > 16) {
|
||||
rw.bandSize = (rw.bandSize >> 1);
|
||||
|
||||
}
|
||||
return rw.rle;
|
||||
|
||||
error:
|
||||
tvg::free(rw.rle);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
SwRle* rleRender(const SwBBox* bbox)
|
||||
SwRle* rleRender(const RenderRegion* bbox)
|
||||
{
|
||||
auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
|
||||
auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
|
||||
auto rle = tvg::calloc<SwRle*>(sizeof(SwRle), 1);
|
||||
rle->spans.reserve(bbox->h());
|
||||
rle->spans.count = bbox->h();
|
||||
|
||||
auto rle = tvg::malloc<SwRle*>(sizeof(SwRle));
|
||||
rle->spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * height);
|
||||
rle->size = height;
|
||||
rle->alloc = height;
|
||||
//cheaper without push()
|
||||
auto x = uint16_t(bbox->min.x);
|
||||
auto y = uint16_t(bbox->min.y);
|
||||
auto len = uint16_t(bbox->w());
|
||||
|
||||
auto span = rle->spans;
|
||||
for (uint16_t i = 0; i < height; ++i, ++span) {
|
||||
span->x = bbox->min.x;
|
||||
span->y = bbox->min.y + i;
|
||||
span->len = width;
|
||||
span->coverage = 255;
|
||||
ARRAY_FOREACH(p, rle->spans) {
|
||||
*p = {x, y++, len, 255};
|
||||
}
|
||||
|
||||
return rle;
|
||||
|
@ -981,44 +864,88 @@ SwRle* rleRender(const SwBBox* bbox)
|
|||
|
||||
void rleReset(SwRle* rle)
|
||||
{
|
||||
if (!rle) return;
|
||||
rle->size = 0;
|
||||
if (rle) rle->spans.clear();
|
||||
}
|
||||
|
||||
|
||||
void rleFree(SwRle* rle)
|
||||
{
|
||||
if (!rle) return;
|
||||
if (rle->spans) tvg::free(rle->spans);
|
||||
tvg::free(rle);
|
||||
delete(rle);
|
||||
}
|
||||
|
||||
|
||||
bool rleClip(SwRle *rle, const SwRle *clip)
|
||||
{
|
||||
if (rle->size == 0 || clip->size == 0) return false;
|
||||
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
|
||||
auto spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * (spanCnt));
|
||||
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
|
||||
if (rle->spans.empty() || clip->spans.empty()) return false;
|
||||
|
||||
_replaceClipSpan(rle, spans, spansEnd - spans);
|
||||
Array<SwSpan> out;
|
||||
out.reserve(std::max(rle->spans.count, clip->spans.count));
|
||||
|
||||
TVGLOG("SW_ENGINE", "Using Path Clipping!");
|
||||
auto spans = rle->data();
|
||||
auto end = rle->spans.end();
|
||||
auto cspans = clip->data();
|
||||
auto cend = clip->spans.end();
|
||||
|
||||
while(spans < end && cspans < cend) {
|
||||
//align y-coordinates.
|
||||
if (cspans->y > spans->y) {
|
||||
++spans;
|
||||
continue;
|
||||
}
|
||||
if (spans->y > cspans->y) {
|
||||
++cspans;
|
||||
continue;
|
||||
}
|
||||
//try clipping with all clip spans which have a same y-coordinate.
|
||||
auto temp = cspans;
|
||||
while(temp < cend && temp->y == cspans->y) {
|
||||
//span must be left(x1) to right(x2) direction. Not intersected.
|
||||
if ((spans->x + spans->len) < spans->x || (temp->x + temp->len) < temp->x) {
|
||||
++temp;
|
||||
continue;
|
||||
}
|
||||
//clip span region
|
||||
auto x = std::max(spans->x, temp->x);
|
||||
auto len = std::min((spans->x + spans->len), (temp->x + temp->len)) - x;
|
||||
if (len > 0) out.next() = {uint16_t(x), temp->y, uint16_t(len), (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8)};
|
||||
++temp;
|
||||
}
|
||||
++spans;
|
||||
}
|
||||
out.move(rle->spans);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool rleClip(SwRle *rle, const SwBBox* clip)
|
||||
//Need to confirm: dead code?
|
||||
bool rleClip(SwRle *rle, const RenderRegion* clip)
|
||||
{
|
||||
if (rle->size == 0) return false;
|
||||
auto spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * (rle->size));
|
||||
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
|
||||
if (rle->spans.empty() || clip->invalid()) return false;
|
||||
|
||||
_replaceClipSpan(rle, spans, spansEnd - spans);
|
||||
auto& min = clip->min;
|
||||
auto& max = clip->max;
|
||||
|
||||
TVGLOG("SW_ENGINE", "Using Box Clipping!");
|
||||
Array<SwSpan> out;
|
||||
out.reserve(rle->spans.count);
|
||||
auto data = out.data;
|
||||
uint16_t x, len;
|
||||
|
||||
ARRAY_FOREACH(p, rle->spans) {
|
||||
if (p->y >= max.y) break;
|
||||
if (p->y < min.y || p->x >= max.x || (p->x + p->len) <= min.x) continue;
|
||||
if (p->x < min.x) {
|
||||
x = min.x;
|
||||
len = std::min((p->len - (x - p->x)), (max.x - x));
|
||||
} else {
|
||||
x = p->x;
|
||||
len = std::min(p->len, uint16_t(max.x - x));
|
||||
}
|
||||
if (len > 0) {
|
||||
*data = {x, p->y, len, p->coverage};
|
||||
++data;
|
||||
++out.count;
|
||||
}
|
||||
}
|
||||
out.move(rle->spans);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -419,13 +419,13 @@ static SwOutline* _genOutline(SwShape* shape, const RenderShape* rshape, const M
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
if (auto out = _genOutline(shape, rshape, transform, mpool, tid, hasComposite, rshape->trimpath())) shape->outline = out;
|
||||
else return false;
|
||||
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
|
||||
if (!mathUpdateOutlineBBox(shape->outline, clipBox, renderBox, shape->fastTrack)) return false;
|
||||
|
||||
shape->bbox = renderRegion;
|
||||
shape->bbox = renderBox;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -500,7 +500,7 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& t
|
|||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
SwOutline* shapeOutline = nullptr;
|
||||
SwOutline* strokeOutline = nullptr;
|
||||
|
@ -528,12 +528,12 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix&
|
|||
|
||||
strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
|
||||
|
||||
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
|
||||
if (!mathUpdateOutlineBBox(strokeOutline, clipBox, renderBox, false)) {
|
||||
ret = false;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
|
||||
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderBox, true);
|
||||
|
||||
clear:
|
||||
if (dashStroking) mpoolRetDashOutline(mpool, tid);
|
||||
|
|
|
@ -31,7 +31,7 @@ struct Canvas::Impl
|
|||
{
|
||||
Scene* scene;
|
||||
RenderMethod* renderer;
|
||||
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
|
||||
RenderRegion vport = {{0, 0}, {INT32_MAX, INT32_MAX}};
|
||||
Status status = Status::Synced;
|
||||
|
||||
Impl() : scene(Scene::gen())
|
||||
|
@ -119,11 +119,11 @@ struct Canvas::Impl
|
|||
{
|
||||
if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition;
|
||||
|
||||
RenderRegion val = {x, y, w, h};
|
||||
RenderRegion val = {{x, y}, {x + w, y + h}};
|
||||
//intersect if the target buffer is already set.
|
||||
auto surface = renderer->mainSurface();
|
||||
if (surface && surface->w > 0 && surface->h > 0) {
|
||||
val.intersect({0, 0, (int32_t)surface->w, (int32_t)surface->h});
|
||||
val.intersect({{0, 0}, {(int32_t)surface->w, (int32_t)surface->h}});
|
||||
}
|
||||
if (vport == val) return Result::Success;
|
||||
renderer->viewport(val);
|
||||
|
|
|
@ -51,7 +51,7 @@ Result GlCanvas::target(void* context, int32_t id, uint32_t w, uint32_t h, Color
|
|||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(context, id, w, h)) return Result::Unknown;
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
|
|
|
@ -58,7 +58,7 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix&
|
|||
if (tmp[i].y > max.y) max.y = tmp[i].y;
|
||||
}
|
||||
|
||||
float region[4] = {float(before.x), float(before.x + before.w), float(before.y), float(before.y + before.h)};
|
||||
float region[4] = {float(before.min.x), float(before.max.x), float(before.min.y), float(before.max.y)};
|
||||
|
||||
//figure out if the clipper is a superset of the current viewport(before) region
|
||||
if (min.x <= region[0] && max.x >= region[1] && min.y <= region[2] && max.y >= region[3]) {
|
||||
|
@ -66,7 +66,7 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix&
|
|||
return Result::Success;
|
||||
//figure out if the clipper is totally outside of the viewport
|
||||
} else if (max.x <= region[0] || min.x >= region[1] || max.y <= region[2] || min.y >= region[3]) {
|
||||
renderer->viewport({0, 0, 0, 0});
|
||||
renderer->viewport({});
|
||||
return Result::Success;
|
||||
}
|
||||
return Result::InsufficientCondition;
|
||||
|
@ -122,13 +122,13 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
|
|||
if (v1.x > v2.x) std::swap(v1.x, v2.x);
|
||||
if (v1.y > v2.y) std::swap(v1.y, v2.y);
|
||||
|
||||
after.x = static_cast<int32_t>(nearbyint(v1.x));
|
||||
after.y = static_cast<int32_t>(nearbyint(v1.y));
|
||||
after.w = static_cast<int32_t>(nearbyint(v2.x)) - after.x;
|
||||
after.h = static_cast<int32_t>(nearbyint(v2.y)) - after.y;
|
||||
after.min.x = static_cast<int32_t>(nearbyint(v1.x));
|
||||
after.min.y = static_cast<int32_t>(nearbyint(v1.y));
|
||||
after.max.x = static_cast<int32_t>(nearbyint(v2.x));
|
||||
after.max.y = static_cast<int32_t>(nearbyint(v2.y));
|
||||
|
||||
if (after.w < 0) after.w = 0;
|
||||
if (after.h < 0) after.h = 0;
|
||||
if (after.max.x < after.min.x) after.max.x = after.min.x;
|
||||
if (after.max.y < after.min.y) after.max.y = after.min.y;
|
||||
|
||||
after.intersect(before);
|
||||
renderer->viewport(after);
|
||||
|
@ -168,7 +168,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
|
|||
ret->pImpl->opacity = opacity;
|
||||
|
||||
if (maskData) ret->mask(maskData->target->duplicate(), maskData->method);
|
||||
if (clipper) ret->clip(clipper->duplicate());
|
||||
if (clipper) ret->clip(static_cast<Shape*>(clipper->duplicate()));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
|
|||
PAINT_METHOD(region, bounds(renderer));
|
||||
|
||||
if (MASK_REGION_MERGING(maskData->method)) region.add(PAINT(maskData->target)->bounds(renderer));
|
||||
if (region.w == 0 || region.h == 0) return true;
|
||||
if (region.invalid()) return true;
|
||||
cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking);
|
||||
if (renderer->beginComposite(cmp, MaskMethod::None, 255)) {
|
||||
maskData->target->pImpl->render(renderer);
|
||||
|
@ -376,16 +376,18 @@ Paint* Paint::duplicate() const noexcept
|
|||
}
|
||||
|
||||
|
||||
Result Paint::clip(Paint* clipper) noexcept
|
||||
Result Paint::clip(Shape* clipper) noexcept
|
||||
{
|
||||
if (clipper && clipper->type() != Type::Shape) {
|
||||
TVGERR("RENDERER", "Clipping only supports the Shape!");
|
||||
return Result::NonSupport;
|
||||
}
|
||||
return pImpl->clip(clipper);
|
||||
}
|
||||
|
||||
|
||||
Shape* Paint::clip() const noexcept
|
||||
{
|
||||
return pImpl->clipper;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::mask(Paint* target, MaskMethod method) noexcept
|
||||
{
|
||||
return pImpl->mask(target, method);
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace tvg
|
|||
Paint* paint = nullptr;
|
||||
Paint* parent = nullptr;
|
||||
Mask* maskData = nullptr;
|
||||
Paint* clipper = nullptr;
|
||||
Shape* clipper = nullptr;
|
||||
RenderMethod* renderer = nullptr;
|
||||
RenderData rd = nullptr;
|
||||
|
||||
|
@ -80,9 +80,9 @@ namespace tvg
|
|||
} tr;
|
||||
RenderUpdateFlag renderFlag = RenderUpdateFlag::None;
|
||||
BlendMethod blendMethod;
|
||||
uint16_t refCnt = 0; //reference count
|
||||
uint8_t ctxFlag;
|
||||
uint8_t opacity;
|
||||
uint8_t refCnt = 0; //reference count
|
||||
|
||||
Impl(Paint* pnt) : paint(pnt)
|
||||
{
|
||||
|
@ -107,9 +107,7 @@ namespace tvg
|
|||
|
||||
uint8_t ref()
|
||||
{
|
||||
if (refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!");
|
||||
else ++refCnt;
|
||||
return refCnt;
|
||||
return ++refCnt;
|
||||
}
|
||||
|
||||
uint8_t unref(bool free = true)
|
||||
|
@ -163,7 +161,7 @@ namespace tvg
|
|||
return tm;
|
||||
}
|
||||
|
||||
Result clip(Paint* clp)
|
||||
Result clip(Shape* clp)
|
||||
{
|
||||
if (clp && PAINT(clp)->parent) return Result::InsufficientCondition;
|
||||
if (clipper) PAINT(clipper)->unref(clipper != clp);
|
||||
|
|
|
@ -253,7 +253,7 @@ struct PictureImpl : Picture
|
|||
|
||||
bool render(RenderMethod* renderer)
|
||||
{
|
||||
bool ret = false;
|
||||
auto ret = true;
|
||||
renderer->blend(impl.blendMethod);
|
||||
|
||||
if (bitmap) return renderer->renderImage(impl.rd);
|
||||
|
@ -273,7 +273,7 @@ struct PictureImpl : Picture
|
|||
{
|
||||
if (impl.rd) return renderer->region(impl.rd);
|
||||
if (vector) return vector->pImpl->bounds(renderer);
|
||||
return {0, 0, 0, 0};
|
||||
return {};
|
||||
}
|
||||
|
||||
Result load(ImageLoader* loader)
|
||||
|
|
|
@ -106,33 +106,14 @@ bool RenderPath::bounds(Matrix* m, float* x, float* y, float* w, float* h)
|
|||
|
||||
void RenderRegion::intersect(const RenderRegion& rhs)
|
||||
{
|
||||
auto x1 = x + w;
|
||||
auto y1 = y + h;
|
||||
auto x2 = rhs.x + rhs.w;
|
||||
auto y2 = rhs.y + rhs.h;
|
||||
if (min.x < rhs.min.x) min.x = rhs.min.x;
|
||||
if (min.y < rhs.min.y) min.y = rhs.min.y;
|
||||
if (max.x > rhs.max.x) max.x = rhs.max.x;
|
||||
if (max.y > rhs.max.y) max.y = rhs.max.y;
|
||||
|
||||
x = (x > rhs.x) ? x : rhs.x;
|
||||
y = (y > rhs.y) ? y : rhs.y;
|
||||
w = ((x1 < x2) ? x1 : x2) - x;
|
||||
h = ((y1 < y2) ? y1 : y2) - y;
|
||||
|
||||
if (w < 0) w = 0;
|
||||
if (h < 0) h = 0;
|
||||
}
|
||||
|
||||
|
||||
void RenderRegion::add(const RenderRegion& rhs)
|
||||
{
|
||||
if (rhs.x < x) {
|
||||
w += (x - rhs.x);
|
||||
x = rhs.x;
|
||||
}
|
||||
if (rhs.y < y) {
|
||||
h += (y - rhs.y);
|
||||
y = rhs.y;
|
||||
}
|
||||
if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x;
|
||||
if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y;
|
||||
// Not intersected: collapse to zero-area region
|
||||
if (max.x < min.x) max.x = min.x;
|
||||
if (max.y < min.y) max.y = min.y;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -94,16 +94,51 @@ struct RenderCompositor
|
|||
|
||||
struct RenderRegion
|
||||
{
|
||||
int32_t x, y, w, h;
|
||||
struct {
|
||||
int32_t x, y;
|
||||
} min;
|
||||
|
||||
struct {
|
||||
int32_t x, y;
|
||||
} max;
|
||||
|
||||
static constexpr RenderRegion intersect(const RenderRegion& lhs, const RenderRegion& rhs)
|
||||
{
|
||||
RenderRegion ret = {{std::max(lhs.min.x, rhs.min.x), std::max(lhs.min.y, rhs.min.y)}, {std::min(lhs.max.x, rhs.max.x), std::min(lhs.max.y, rhs.max.y)}};
|
||||
// Not intersected: collapse to zero-area region
|
||||
if (ret.min.x > ret.max.x) ret.max.x = ret.min.x;
|
||||
if (ret.min.y > ret.max.y) ret.max.y = ret.min.y;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void intersect(const RenderRegion& rhs);
|
||||
void add(const RenderRegion& rhs);
|
||||
|
||||
void add(const RenderRegion& rhs)
|
||||
{
|
||||
if (rhs.min.x < min.x) min.x = rhs.min.x;
|
||||
if (rhs.min.y < min.y) min.y = rhs.min.y;
|
||||
if (rhs.max.x > max.x) max.x = rhs.max.x;
|
||||
if (rhs.max.y > max.y) max.y = rhs.max.y;
|
||||
}
|
||||
|
||||
bool operator==(const RenderRegion& rhs) const
|
||||
{
|
||||
if (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h) return true;
|
||||
return false;
|
||||
return (min.x == rhs.min.x && min.y == rhs.min.y && max.x == rhs.max.x && max.y == rhs.max.y);
|
||||
}
|
||||
|
||||
void reset() { min.x = min.y = max.x = max.y = 0; }
|
||||
bool valid() const { return (max.x > min.x && max.y > min.y); }
|
||||
bool invalid() const { return !valid(); }
|
||||
|
||||
int32_t sx() const { return min.x; }
|
||||
int32_t sy() const { return min.y; }
|
||||
int32_t sw() const { return max.x - min.x; }
|
||||
int32_t sh() const { return max.y - min.y; }
|
||||
|
||||
uint32_t x() const { return (uint32_t) sx(); }
|
||||
uint32_t y() const { return (uint32_t) sy(); }
|
||||
uint32_t w() const { return (uint32_t) sw(); }
|
||||
uint32_t h() const { return (uint32_t) sh(); }
|
||||
};
|
||||
|
||||
struct RenderPath
|
||||
|
@ -272,7 +307,7 @@ struct RenderShape
|
|||
struct RenderEffect
|
||||
{
|
||||
RenderData rd = nullptr;
|
||||
RenderRegion extend = {0, 0, 0, 0};
|
||||
RenderRegion extend{};
|
||||
SceneEffect type;
|
||||
bool valid = false;
|
||||
|
||||
|
@ -353,7 +388,7 @@ struct RenderEffectTint : RenderEffect
|
|||
inst->white[0] = va_arg(args, int);
|
||||
inst->white[1] = va_arg(args, int);
|
||||
inst->white[2] = va_arg(args, int);
|
||||
inst->intensity = (uint8_t)(va_arg(args, double) * 2.55);
|
||||
inst->intensity = (uint8_t)(static_cast<float>(va_arg(args, double)) * 2.55f);
|
||||
inst->type = SceneEffect::Tint;
|
||||
return inst;
|
||||
}
|
||||
|
|
|
@ -64,10 +64,11 @@ struct SceneImpl : Scene
|
|||
{
|
||||
Paint::Impl impl;
|
||||
list<Paint*> paints; //children list
|
||||
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
|
||||
RenderRegion vport = {};
|
||||
Array<RenderEffect*>* effects = nullptr;
|
||||
uint8_t compFlag = CompositionFlag::Invalid;
|
||||
uint8_t opacity; //for composition
|
||||
bool vdirty = false;
|
||||
|
||||
SceneImpl() : impl(Paint::Impl(this))
|
||||
{
|
||||
|
@ -93,10 +94,11 @@ struct SceneImpl : Scene
|
|||
//Half translucent requires intermediate composition.
|
||||
if (opacity == 255) return compFlag;
|
||||
|
||||
//If scene has several children or only scene, it may require composition.
|
||||
//OPTIMIZE: the bitmap type of the picture would not need the composition.
|
||||
//OPTIMIZE: a single paint of a scene would not need the composition.
|
||||
if (paints.size() == 1 && paints.front()->type() == Type::Shape) return compFlag;
|
||||
//Only shape or picture may not require composition.
|
||||
if (paints.size() == 1) {
|
||||
auto type = paints.front()->type();
|
||||
if (type == Type::Shape || type == Type::Picture) return compFlag;
|
||||
}
|
||||
|
||||
compFlag |= CompositionFlag::Opacity;
|
||||
|
||||
|
@ -105,7 +107,7 @@ struct SceneImpl : Scene
|
|||
|
||||
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
|
||||
{
|
||||
this->vport = renderer->viewport();
|
||||
if (paints.empty()) return nullptr;
|
||||
|
||||
if (needComposition(opacity)) {
|
||||
/* Overriding opacity value. If this scene is half-translucent,
|
||||
|
@ -123,11 +125,17 @@ struct SceneImpl : Scene
|
|||
}
|
||||
}
|
||||
|
||||
//this viewport update is more performant than in bounds()?
|
||||
vport = renderer->viewport();
|
||||
vdirty = true;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool render(RenderMethod* renderer)
|
||||
{
|
||||
if (paints.empty()) return true;
|
||||
|
||||
RenderCompositor* cmp = nullptr;
|
||||
auto ret = true;
|
||||
|
||||
|
@ -157,42 +165,38 @@ struct SceneImpl : Scene
|
|||
return ret;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod* renderer) const
|
||||
RenderRegion bounds(RenderMethod* renderer)
|
||||
{
|
||||
if (paints.empty()) return {0, 0, 0, 0};
|
||||
|
||||
int32_t x1 = INT32_MAX;
|
||||
int32_t y1 = INT32_MAX;
|
||||
int32_t x2 = 0;
|
||||
int32_t y2 = 0;
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto region = paint->pImpl->bounds(renderer);
|
||||
if (paints.empty()) return {};
|
||||
if (!vdirty) return vport;
|
||||
vdirty = false;
|
||||
|
||||
//Merge regions
|
||||
if (region.x < x1) x1 = region.x;
|
||||
if (x2 < region.x + region.w) x2 = (region.x + region.w);
|
||||
if (region.y < y1) y1 = region.y;
|
||||
if (y2 < region.y + region.h) y2 = (region.y + region.h);
|
||||
RenderRegion pRegion = {{INT32_MAX, INT32_MAX}, {0, 0}};
|
||||
for (auto paint : paints) {
|
||||
auto region = paint->pImpl->bounds(renderer);
|
||||
if (region.min.x < pRegion.min.x) pRegion.min.x = region.min.x;
|
||||
if (pRegion.max.x < region.max.x) pRegion.max.x = region.max.x;
|
||||
if (region.min.y < pRegion.min.y) pRegion.min.y = region.min.y;
|
||||
if (pRegion.max.y < region.max.y) pRegion.max.y = region.max.y;
|
||||
}
|
||||
|
||||
//Extends the render region if post effects require
|
||||
int32_t ex = 0, ey = 0, ew = 0, eh = 0;
|
||||
RenderRegion eRegion{};
|
||||
if (effects) {
|
||||
ARRAY_FOREACH(p, *effects) {
|
||||
auto effect = *p;
|
||||
if (effect->valid && renderer->region(effect)) {
|
||||
ex = std::min(ex, effect->extend.x);
|
||||
ey = std::min(ey, effect->extend.y);
|
||||
ew = std::max(ew, effect->extend.w);
|
||||
eh = std::max(eh, effect->extend.h);
|
||||
}
|
||||
if (effect->valid && renderer->region(effect)) eRegion.add(effect->extend);
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh};
|
||||
ret.intersect(this->vport);
|
||||
return ret;
|
||||
pRegion.min.x += eRegion.min.x;
|
||||
pRegion.min.y += eRegion.min.y;
|
||||
pRegion.max.x += eRegion.max.x;
|
||||
pRegion.max.y += eRegion.max.y;
|
||||
|
||||
vport = RenderRegion::intersect(vport, pRegion);
|
||||
return vport;
|
||||
}
|
||||
|
||||
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
|
||||
|
|
|
@ -114,7 +114,7 @@ struct ShapeImpl : Shape
|
|||
|
||||
RenderRegion bounds(RenderMethod* renderer)
|
||||
{
|
||||
if (!impl.rd) return {0, 0, 0, 0};
|
||||
if (!impl.rd) return {};
|
||||
return renderer->region(impl.rd);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
|
|||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//FIXME: The value must be associated with an individual canvas instance.
|
||||
|
|
|
@ -55,7 +55,7 @@ Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w,
|
|||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown;
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
|
|
|
@ -7,6 +7,7 @@ source_file = [
|
|||
'tvgWgRenderData.h',
|
||||
'tvgWgRenderer.h',
|
||||
'tvgWgRenderTarget.h',
|
||||
'tvgWgRenderTask.h',
|
||||
'tvgWgShaderSrc.h',
|
||||
'tvgWgShaderTypes.h',
|
||||
'tvgWgBindGroups.cpp',
|
||||
|
@ -17,6 +18,7 @@ source_file = [
|
|||
'tvgWgRenderData.cpp',
|
||||
'tvgWgRenderer.cpp',
|
||||
'tvgWgRenderTarget.cpp',
|
||||
'tvgWgRenderTask.cpp',
|
||||
'tvgWgShaderSrc.cpp',
|
||||
'tvgWgShaderTypes.cpp'
|
||||
]
|
||||
|
|
|
@ -103,9 +103,15 @@ WGPUBindGroup WgBindGroupLayouts::createBindGroupStrorage3RO(WGPUTextureView tex
|
|||
|
||||
|
||||
WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer1Un(WGPUBuffer buff)
|
||||
{
|
||||
return createBindGroupBuffer1Un(buff, 0, wgpuBufferGetSize(buff));
|
||||
}
|
||||
|
||||
|
||||
WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer1Un(WGPUBuffer buff, uint64_t offset, uint64_t size)
|
||||
{
|
||||
const WGPUBindGroupEntry bindGroupEntrys[] = {
|
||||
{ .binding = 0, .buffer = buff, .size = wgpuBufferGetSize(buff) }
|
||||
{ .binding = 0, .buffer = buff, .offset = offset, .size = size }
|
||||
};
|
||||
const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutBuffer1Un, .entryCount = 1, .entries = bindGroupEntrys };
|
||||
return wgpuDeviceCreateBindGroup(device, &bindGroupDesc);
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
WGPUBindGroup createBindGroupStrorage2RO(WGPUTextureView texView0, WGPUTextureView texView1);
|
||||
WGPUBindGroup createBindGroupStrorage3RO(WGPUTextureView texView0, WGPUTextureView texView1, WGPUTextureView texView2);
|
||||
WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff);
|
||||
WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff, uint64_t offset, uint64_t size);
|
||||
WGPUBindGroup createBindGroupBuffer2Un(WGPUBuffer buff0, WGPUBuffer buff1);
|
||||
WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2);
|
||||
void releaseBindGroup(WGPUBindGroup& bindGroup);
|
||||
|
|
|
@ -200,10 +200,7 @@ bool WgContext::allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint
|
|||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
else {
|
||||
releaseBuffer(buffer);
|
||||
const WGPUBufferDescriptor bufferDesc {
|
||||
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex,
|
||||
.size = size > WG_VERTEX_BUFFER_MIN_SIZE ? size : WG_VERTEX_BUFFER_MIN_SIZE
|
||||
};
|
||||
const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, .size = size };
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
return true;
|
||||
|
@ -218,10 +215,7 @@ bool WgContext::allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, ui
|
|||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
else {
|
||||
releaseBuffer(buffer);
|
||||
const WGPUBufferDescriptor bufferDesc {
|
||||
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
|
||||
.size = size > WG_INDEX_BUFFER_MIN_SIZE ? size : WG_INDEX_BUFFER_MIN_SIZE
|
||||
};
|
||||
const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, .size = size };
|
||||
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
|
||||
return true;
|
||||
|
@ -241,10 +235,7 @@ bool WgContext::allocateBufferIndexFan(uint64_t vertexCount)
|
|||
indexes.push(i + 2);
|
||||
}
|
||||
releaseBuffer(bufferIndexFan);
|
||||
WGPUBufferDescriptor bufferDesc{
|
||||
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
|
||||
.size = indexCount * sizeof(uint32_t)
|
||||
};
|
||||
WGPUBufferDescriptor bufferDesc{ .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, .size = indexCount * sizeof(uint32_t) };
|
||||
bufferIndexFan = wgpuDeviceCreateBuffer(device, &bufferDesc);
|
||||
wgpuQueueWriteBuffer(queue, bufferIndexFan, 0, &indexes[0], indexCount * sizeof(uint32_t));
|
||||
return true;
|
||||
|
@ -269,3 +260,34 @@ void WgContext::releaseQueue(WGPUQueue& queue)
|
|||
queue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WGPUCommandEncoder WgContext::createCommandEncoder()
|
||||
{
|
||||
WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||
return wgpuDeviceCreateCommandEncoder(device, &commandEncoderDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::submitCommandEncoder(WGPUCommandEncoder commandEncoder)
|
||||
{
|
||||
const WGPUCommandBufferDescriptor commandBufferDesc{};
|
||||
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
|
||||
wgpuQueueSubmit(queue, 1, &commandsBuffer);
|
||||
wgpuCommandBufferRelease(commandsBuffer);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::releaseCommandEncoder(WGPUCommandEncoder& commandEncoder)
|
||||
{
|
||||
if (commandEncoder) {
|
||||
wgpuCommandEncoderRelease(commandEncoder);
|
||||
commandEncoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WgContext::invalid()
|
||||
{
|
||||
return !instance || !device;
|
||||
}
|
||||
|
|
|
@ -25,9 +25,6 @@
|
|||
|
||||
#include "tvgWgBindGroups.h"
|
||||
|
||||
#define WG_VERTEX_BUFFER_MIN_SIZE 2048
|
||||
#define WG_INDEX_BUFFER_MIN_SIZE 2048
|
||||
|
||||
struct WgContext {
|
||||
// external webgpu handles
|
||||
WGPUInstance instance{};
|
||||
|
@ -70,6 +67,13 @@ struct WgContext {
|
|||
|
||||
// release buffer objects
|
||||
void releaseBuffer(WGPUBuffer& buffer);
|
||||
|
||||
// command encoder
|
||||
WGPUCommandEncoder createCommandEncoder();
|
||||
void submitCommandEncoder(WGPUCommandEncoder encoder);
|
||||
void releaseCommandEncoder(WGPUCommandEncoder& encoder);
|
||||
|
||||
bool invalid();
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_COMMON_H_
|
||||
|
|
|
@ -38,6 +38,9 @@ class WgCompositor
|
|||
private:
|
||||
// pipelines
|
||||
WgPipelines pipelines{};
|
||||
// stage buffers
|
||||
WgStageBufferGeometry stageBufferGeometry{};
|
||||
WgStageBufferUniform<WgShaderTypePaintSettings> stageBufferPaint;
|
||||
// global stencil/depth buffer handles
|
||||
WGPUTexture texDepthStencil{};
|
||||
WGPUTextureView texViewDepthStencil{};
|
||||
|
@ -52,21 +55,26 @@ private:
|
|||
// current render pass handles
|
||||
WGPURenderPassEncoder renderPassEncoder{};
|
||||
WGPUCommandEncoder commandEncoder{};
|
||||
WgRenderStorage* currentTarget{};
|
||||
// intermediate render storages
|
||||
WgRenderStorage storageTemp0;
|
||||
WgRenderStorage storageTemp1;
|
||||
WgRenderTarget* currentTarget{};
|
||||
// intermediate render targets
|
||||
WgRenderTarget targetTemp0;
|
||||
WgRenderTarget targetTemp1;
|
||||
WGPUBindGroup bindGroupStorageTemp{};
|
||||
// composition and blend geometries
|
||||
WgMeshData meshData;
|
||||
WgMeshData meshDataBlit;
|
||||
// render target dimensions
|
||||
uint32_t width{};
|
||||
uint32_t height{};
|
||||
|
||||
// viewport utilities
|
||||
RenderRegion shrinkRenderRegion(RenderRegion& rect);
|
||||
void copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src);
|
||||
void copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src, const RenderRegion& region);
|
||||
void copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src);
|
||||
void copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src, const RenderRegion& region);
|
||||
|
||||
// base meshes draw
|
||||
void drawMesh(WgContext& context, WgMeshData* meshData);
|
||||
void drawMeshFan(WgContext& context, WgMeshData* meshData);
|
||||
void drawMeshImage(WgContext& context, WgMeshData* meshData);
|
||||
|
||||
// shapes
|
||||
void drawShape(WgContext& context, WgRenderDataShape* renderData);
|
||||
|
@ -84,8 +92,8 @@ private:
|
|||
void clipImage(WgContext& context, WgRenderDataPicture* renderData);
|
||||
|
||||
// scenes
|
||||
void drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose);
|
||||
void blendScene(WgContext& context, WgRenderStorage* src, WgCompose* compose);
|
||||
void drawScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose);
|
||||
void blendScene(WgContext& context, WgRenderTarget* src, WgCompose* compose);
|
||||
|
||||
// the renderer prioritizes clipping with the stroke over the shape's fill
|
||||
void markupClipPath(WgContext& context, WgRenderDataShape* renderData);
|
||||
|
@ -100,24 +108,32 @@ public:
|
|||
void resize(WgContext& context, uint32_t width, uint32_t height);
|
||||
|
||||
// render passes workflow
|
||||
void beginRenderPass(WGPUCommandEncoder encoder, WgRenderStorage* target, bool clear, WGPUColor clearColor = { 0.0, 0.0, 0.0, 0.0 });
|
||||
void beginRenderPass(WGPUCommandEncoder encoder, WgRenderTarget* target, bool clear, WGPUColor clearColor = { 0.0, 0.0, 0.0, 0.0 });
|
||||
void endRenderPass();
|
||||
|
||||
// stage buffers operations
|
||||
void reset(WgContext& context);
|
||||
void flush(WgContext& context);
|
||||
|
||||
// request shapes for drawing (staging)
|
||||
void requestShape(WgRenderDataShape* renderData);
|
||||
void requestImage(WgRenderDataPicture* renderData);
|
||||
|
||||
// render shapes, images and scenes
|
||||
void renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
|
||||
void renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
|
||||
void renderScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose);
|
||||
void composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* compose);
|
||||
void renderScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose);
|
||||
void composeScene(WgContext& context, WgRenderTarget* src, WgRenderTarget* mask, WgCompose* compose);
|
||||
|
||||
// blit render storage to texture view (f.e. screen buffer)
|
||||
void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView);
|
||||
// blit render target to texture view (f.e. screen buffer)
|
||||
void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderTarget* src, WGPUTextureView dstView);
|
||||
|
||||
// effects
|
||||
bool gaussianBlur(WgContext& context, WgRenderStorage* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose);
|
||||
bool dropShadow(WgContext& context, WgRenderStorage* dst, const RenderEffectDropShadow* params, const WgCompose* compose);
|
||||
bool fillEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectFill* params, const WgCompose* compose);
|
||||
bool tintEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTint* params, const WgCompose* compose);
|
||||
bool tritoneEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTritone* params, const WgCompose* compose);
|
||||
bool gaussianBlur(WgContext& context, WgRenderTarget* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose);
|
||||
bool dropShadow(WgContext& context, WgRenderTarget* dst, const RenderEffectDropShadow* params, const WgCompose* compose);
|
||||
bool fillEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectFill* params, const WgCompose* compose);
|
||||
bool tintEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTint* params, const WgCompose* compose);
|
||||
bool tritoneEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTritone* params, const WgCompose* compose);
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_COMPOSITOR_H_
|
||||
|
|
|
@ -167,17 +167,17 @@ void WgPipelines::initialize(WgContext& context)
|
|||
|
||||
const WgBindGroupLayouts& layouts = context.layouts;
|
||||
// bind group layouts helpers
|
||||
const WGPUBindGroupLayout bindGroupLayoutsStencil[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsDepth[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsStencil[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsDepth[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutBuffer1Un };
|
||||
// bind group layouts normal blend
|
||||
const WGPUBindGroupLayout bindGroupLayoutsSolid[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsGradient[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampledBuff2Un };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsImage[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsSolid[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsGradient[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsImage[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsScene[] { layouts.layoutTexSampled, layouts.layoutBuffer1Un };
|
||||
// bind group layouts custom blend
|
||||
const WGPUBindGroupLayout bindGroupLayoutsSolidBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsGradientBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampledBuff2Un, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsImageBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampled, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsSolidBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsGradientBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsImageBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled, layouts.layoutTexSampled };
|
||||
const WGPUBindGroupLayout bindGroupLayoutsSceneBlend[] { layouts.layoutTexSampled, layouts.layoutTexSampled, layouts.layoutBuffer1Un };
|
||||
// bind group layouts scene compose
|
||||
const WGPUBindGroupLayout bindGroupLayoutsSceneCompose[] { layouts.layoutTexSampled, layouts.layoutTexSampled };
|
||||
|
@ -229,7 +229,7 @@ void WgPipelines::initialize(WgContext& context)
|
|||
layout_stencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2);
|
||||
layout_depth = createPipelineLayout(context.device, bindGroupLayoutsDepth, 3);
|
||||
// layouts normal blend
|
||||
layout_solid = createPipelineLayout(context.device, bindGroupLayoutsSolid, 3);
|
||||
layout_solid = createPipelineLayout(context.device, bindGroupLayoutsSolid, 2);
|
||||
layout_gradient = createPipelineLayout(context.device, bindGroupLayoutsGradient, 3);
|
||||
layout_image = createPipelineLayout(context.device, bindGroupLayoutsImage, 3);
|
||||
layout_scene = createPipelineLayout(context.device, bindGroupLayoutsScene, 2);
|
||||
|
|
|
@ -30,163 +30,115 @@
|
|||
// WgMeshData
|
||||
//***********************************************************************
|
||||
|
||||
void WgMeshData::draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
||||
{
|
||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
||||
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void WgMeshData::drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
||||
{
|
||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.bufferIndexFan, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
||||
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
void WgMeshData::drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder)
|
||||
{
|
||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, bufferPosition, 0, vertexCount * sizeof(float) * 2);
|
||||
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, bufferTexCoord, 0, vertexCount * sizeof(float) * 2);
|
||||
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, bufferIndex, WGPUIndexFormat_Uint32, 0, indexCount * sizeof(uint32_t));
|
||||
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, indexCount, 1, 0, 0, 0);
|
||||
};
|
||||
|
||||
|
||||
void WgMeshData::update(WgContext& context, const WgVertexBuffer& vertexBuffer)
|
||||
void WgMeshData::update(const WgVertexBuffer& vertexBuffer)
|
||||
{
|
||||
assert(vertexBuffer.count > 2);
|
||||
vertexCount = vertexBuffer.count;
|
||||
indexCount = (vertexBuffer.count - 2) * 3;
|
||||
context.allocateBufferVertex(bufferPosition, (float*)vertexBuffer.data, vertexCount * sizeof(float) * 2);
|
||||
context.allocateBufferIndexFan(vertexCount);
|
||||
// setup vertex data
|
||||
vbuffer.reserve(vertexBuffer.count);
|
||||
vbuffer.count = vertexBuffer.count;
|
||||
memcpy(vbuffer.data, vertexBuffer.data, sizeof(vertexBuffer.data[0])*vertexBuffer.count);
|
||||
// setup tex coords data
|
||||
tbuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
void WgMeshData::update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd)
|
||||
void WgMeshData::update(const WgIndexedVertexBuffer& vertexBufferInd)
|
||||
{
|
||||
assert(vertexBufferInd.vcount > 2);
|
||||
vertexCount = vertexBufferInd.vcount;
|
||||
indexCount = vertexBufferInd.icount;
|
||||
if (vertexCount > 0) context.allocateBufferVertex(bufferPosition, (float*)vertexBufferInd.vbuff, vertexCount * sizeof(float) * 2);
|
||||
if (indexCount > 0) context.allocateBufferIndex(bufferIndex, vertexBufferInd.ibuff, indexCount * sizeof(uint32_t));
|
||||
// setup vertex data
|
||||
vbuffer.reserve(vertexBufferInd.vcount);
|
||||
vbuffer.count = vertexBufferInd.vcount;
|
||||
memcpy(vbuffer.data, vertexBufferInd.vbuff, sizeof(vertexBufferInd.vbuff[0])*vertexBufferInd.vcount);
|
||||
// setup tex coords data
|
||||
tbuffer.clear();
|
||||
// copy index data
|
||||
ibuffer.reserve(vertexBufferInd.icount);
|
||||
ibuffer.count = vertexBufferInd.icount;
|
||||
memcpy(ibuffer.data, vertexBufferInd.ibuff, sizeof(vertexBufferInd.ibuff[0])*vertexBufferInd.icount);
|
||||
};
|
||||
|
||||
|
||||
void WgMeshData::bbox(WgContext& context, const Point pmin, const Point pmax)
|
||||
void WgMeshData::bbox(const Point pmin, const Point pmax)
|
||||
{
|
||||
vertexCount = 4;
|
||||
indexCount = 6;
|
||||
const float data[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y};
|
||||
context.allocateBufferVertex(bufferPosition, data, sizeof(data));
|
||||
context.allocateBufferIndexFan(vertexCount);
|
||||
// setup vertex data
|
||||
vbuffer.reserve(4);
|
||||
vbuffer.count = 4;
|
||||
memcpy(vbuffer.data, data, sizeof(data));
|
||||
// setup tex coords data
|
||||
tbuffer.clear();
|
||||
}
|
||||
|
||||
|
||||
void WgMeshData::imageBox(WgContext& context, float w, float h)
|
||||
void WgMeshData::imageBox(float w, float h)
|
||||
{
|
||||
vertexCount = 4;
|
||||
indexCount = 6;
|
||||
const float vdata[] = {0.0f, 0.0f, w, 0.0f, w, h, 0.0f, h};
|
||||
const float tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
|
||||
const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
|
||||
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata));
|
||||
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata));
|
||||
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata));
|
||||
// setup vertex data
|
||||
vbuffer.reserve(4);
|
||||
vbuffer.count = 4;
|
||||
memcpy(vbuffer.data, vdata, sizeof(vdata));
|
||||
// setup tex coords data
|
||||
tbuffer.reserve(4);
|
||||
tbuffer.count = 4;
|
||||
memcpy(tbuffer.data, tdata, sizeof(tdata));
|
||||
// setup indexes data
|
||||
ibuffer.reserve(6);
|
||||
ibuffer.count = 6;
|
||||
memcpy(ibuffer.data, idata, sizeof(idata));
|
||||
}
|
||||
|
||||
|
||||
void WgMeshData::blitBox(WgContext& context)
|
||||
void WgMeshData::blitBox()
|
||||
{
|
||||
vertexCount = 4;
|
||||
indexCount = 6;
|
||||
const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f};
|
||||
const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f};
|
||||
const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 };
|
||||
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata));
|
||||
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata));
|
||||
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata));
|
||||
// setup vertex data
|
||||
vbuffer.reserve(4);
|
||||
vbuffer.count = 4;
|
||||
memcpy(vbuffer.data, vdata, sizeof(vdata));
|
||||
// setup tex coords data
|
||||
tbuffer.reserve(4);
|
||||
tbuffer.count = 4;
|
||||
memcpy(tbuffer.data, tdata, sizeof(tdata));
|
||||
// setup indexes data
|
||||
ibuffer.reserve(6);
|
||||
ibuffer.count = 6;
|
||||
memcpy(ibuffer.data, idata, sizeof(idata));
|
||||
}
|
||||
|
||||
|
||||
void WgMeshData::release(WgContext& context)
|
||||
{
|
||||
context.releaseBuffer(bufferIndex);
|
||||
context.releaseBuffer(bufferTexCoord);
|
||||
context.releaseBuffer(bufferPosition);
|
||||
};
|
||||
|
||||
|
||||
//***********************************************************************
|
||||
// WgMeshDataPool
|
||||
//***********************************************************************
|
||||
|
||||
WgMeshData* WgMeshDataPool::allocate(WgContext& context)
|
||||
{
|
||||
WgMeshData* meshData{};
|
||||
if (mPool.count > 0) {
|
||||
meshData = mPool.last();
|
||||
mPool.pop();
|
||||
} else {
|
||||
meshData = new WgMeshData();
|
||||
mList.push(meshData);
|
||||
}
|
||||
return meshData;
|
||||
}
|
||||
|
||||
|
||||
void WgMeshDataPool::free(WgContext& context, WgMeshData* meshData)
|
||||
{
|
||||
mPool.push(meshData);
|
||||
}
|
||||
|
||||
|
||||
void WgMeshDataPool::release(WgContext& context)
|
||||
{
|
||||
ARRAY_FOREACH(p, mList) {
|
||||
(*p)->release(context);
|
||||
delete(*p);
|
||||
}
|
||||
mPool.clear();
|
||||
mList.clear();
|
||||
}
|
||||
|
||||
WgMeshDataPool gMeshDataPoolInstance;
|
||||
WgMeshDataPool* WgMeshDataPool::gMeshDataPool = &gMeshDataPoolInstance;
|
||||
|
||||
//***********************************************************************
|
||||
// WgMeshDataGroup
|
||||
//***********************************************************************
|
||||
|
||||
void WgMeshDataGroup::append(WgContext& context, const WgVertexBuffer& vertexBuffer)
|
||||
void WgMeshDataGroup::append(const WgVertexBuffer& vertexBuffer)
|
||||
{
|
||||
assert(vertexBuffer.count >= 3);
|
||||
meshes.push(WgMeshDataPool::gMeshDataPool->allocate(context));
|
||||
meshes.last()->update(context, vertexBuffer);
|
||||
meshes.push(new WgMeshData());
|
||||
meshes.last()->update(vertexBuffer);
|
||||
}
|
||||
|
||||
|
||||
void WgMeshDataGroup::append(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd)
|
||||
void WgMeshDataGroup::append(const WgIndexedVertexBuffer& vertexBufferInd)
|
||||
{
|
||||
assert(vertexBufferInd.vcount >= 3);
|
||||
meshes.push(WgMeshDataPool::gMeshDataPool->allocate(context));
|
||||
meshes.last()->update(context, vertexBufferInd);
|
||||
meshes.push(new WgMeshData());
|
||||
meshes.last()->update(vertexBufferInd);
|
||||
}
|
||||
|
||||
|
||||
void WgMeshDataGroup::append(WgContext& context, const Point pmin, const Point pmax)
|
||||
void WgMeshDataGroup::append(const Point pmin, const Point pmax)
|
||||
{
|
||||
meshes.push(WgMeshDataPool::gMeshDataPool->allocate(context));
|
||||
meshes.last()->bbox(context, pmin, pmax);
|
||||
meshes.push(new WgMeshData());
|
||||
meshes.last()->bbox(pmin, pmax);
|
||||
}
|
||||
|
||||
|
||||
void WgMeshDataGroup::release(WgContext& context)
|
||||
void WgMeshDataGroup::release()
|
||||
{
|
||||
ARRAY_FOREACH(p, meshes)
|
||||
WgMeshDataPool::gMeshDataPool->free(context, *p);
|
||||
ARRAY_FOREACH(p, meshes) delete *p;
|
||||
meshes.clear();
|
||||
};
|
||||
|
||||
|
@ -209,12 +161,38 @@ void WgImageData::update(WgContext& context, const RenderSurface* surface)
|
|||
if (texHandleChanged) {
|
||||
context.releaseTextureView(textureView);
|
||||
textureView = context.createTextureView(texture);
|
||||
// update bind group
|
||||
context.layouts.releaseBindGroup(bindGroup);
|
||||
bindGroup = context.layouts.createBindGroupTexSampled(context.samplerLinearRepeat, textureView);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void WgImageData::update(WgContext& context, const Fill* fill)
|
||||
{
|
||||
// compute gradient data
|
||||
WgShaderTypeGradientData gradientData;
|
||||
gradientData.update(fill);
|
||||
// allocate new texture handle
|
||||
bool texHandleChanged = context.allocateTexture(texture, WG_TEXTURE_GRADIENT_SIZE, 1, WGPUTextureFormat_RGBA8Unorm, gradientData.data);
|
||||
// update texture view of texture handle was changed
|
||||
if (texHandleChanged) {
|
||||
context.releaseTextureView(textureView);
|
||||
textureView = context.createTextureView(texture);
|
||||
// get sampler by spread type
|
||||
WGPUSampler sampler = context.samplerLinearClamp;
|
||||
if (fill->spread() == FillSpread::Reflect) sampler = context.samplerLinearMirror;
|
||||
if (fill->spread() == FillSpread::Repeat) sampler = context.samplerLinearRepeat;
|
||||
// update bind group
|
||||
context.layouts.releaseBindGroup(bindGroup);
|
||||
bindGroup = context.layouts.createBindGroupTexSampled(sampler, textureView);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void WgImageData::release(WgContext& context)
|
||||
{
|
||||
context.layouts.releaseBindGroup(bindGroup);
|
||||
context.releaseTextureView(textureView);
|
||||
context.releaseTexture(texture);
|
||||
};
|
||||
|
@ -223,52 +201,31 @@ void WgImageData::release(WgContext& context)
|
|||
// WgRenderSettings
|
||||
//***********************************************************************
|
||||
|
||||
void WgRenderSettings::updateFill(WgContext& context, const Fill* fill)
|
||||
void WgRenderSettings::update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity)
|
||||
{
|
||||
rasterType = WgRenderRasterType::Gradient;
|
||||
// get gradient transfrom matrix
|
||||
Matrix invFillTransform;
|
||||
WgShaderTypeMat4x4f gradientTrans; // identity by default
|
||||
if (inverse(&fill->transform(), &invFillTransform))
|
||||
gradientTrans.update(invFillTransform);
|
||||
settings.transform.update(transform);
|
||||
settings.options.update(cs, opacity);
|
||||
}
|
||||
|
||||
void WgRenderSettings::update(WgContext& context, const Fill* fill)
|
||||
{
|
||||
assert(fill);
|
||||
settings.gradient.update(fill);
|
||||
gradientData.update(context, fill);
|
||||
// get gradient rasterisation settings
|
||||
WgShaderTypeGradient gradient;
|
||||
if (fill->type() == Type::LinearGradient) {
|
||||
gradient.update((LinearGradient*)fill);
|
||||
rasterType = WgRenderRasterType::Gradient;
|
||||
if (fill->type() == Type::LinearGradient)
|
||||
fillType = WgRenderSettingsType::Linear;
|
||||
} else if (fill->type() == Type::RadialGradient) {
|
||||
gradient.update((RadialGradient*)fill);
|
||||
else if (fill->type() == Type::RadialGradient)
|
||||
fillType = WgRenderSettingsType::Radial;
|
||||
}
|
||||
// update gpu assets
|
||||
bool bufferGradientSettingsChanged = context.allocateBufferUniform(bufferGroupGradient, &gradient.settings, sizeof(gradient.settings));
|
||||
bool bufferGradientTransformChanged = context.allocateBufferUniform(bufferGroupTransfromGrad, &gradientTrans.mat, sizeof(gradientTrans.mat));
|
||||
bool textureGradientChanged = context.allocateTexture(texGradient, WG_TEXTURE_GRADIENT_SIZE, 1, WGPUTextureFormat_RGBA8Unorm, gradient.texData);
|
||||
if (bufferGradientSettingsChanged || textureGradientChanged || bufferGradientTransformChanged) {
|
||||
// update texture view
|
||||
context.releaseTextureView(texViewGradient);
|
||||
texViewGradient = context.createTextureView(texGradient);
|
||||
// get sampler by spread type
|
||||
WGPUSampler sampler = context.samplerLinearClamp;
|
||||
if (fill->spread() == FillSpread::Reflect) sampler = context.samplerLinearMirror;
|
||||
if (fill->spread() == FillSpread::Repeat) sampler = context.samplerLinearRepeat;
|
||||
// update bind group
|
||||
context.layouts.releaseBindGroup(bindGroupGradient);
|
||||
bindGroupGradient = context.layouts.createBindGroupTexSampledBuff2Un(
|
||||
sampler, texViewGradient, bufferGroupGradient, bufferGroupTransfromGrad);
|
||||
}
|
||||
skip = false;
|
||||
};
|
||||
|
||||
|
||||
void WgRenderSettings::updateColor(WgContext& context, const RenderColor& c)
|
||||
void WgRenderSettings::update(WgContext& context, const RenderColor& c)
|
||||
{
|
||||
settings.color.update(c);
|
||||
rasterType = WgRenderRasterType::Solid;
|
||||
WgShaderTypeVec4f solidColor(c);
|
||||
if (context.allocateBufferUniform(bufferGroupSolid, &solidColor, sizeof(solidColor))) {
|
||||
context.layouts.releaseBindGroup(bindGroupSolid);
|
||||
bindGroupSolid = context.layouts.createBindGroupBuffer1Un(bufferGroupSolid);
|
||||
}
|
||||
fillType = WgRenderSettingsType::Solid;
|
||||
skip = (c.a == 0);
|
||||
};
|
||||
|
@ -276,13 +233,7 @@ void WgRenderSettings::updateColor(WgContext& context, const RenderColor& c)
|
|||
|
||||
void WgRenderSettings::release(WgContext& context)
|
||||
{
|
||||
context.layouts.releaseBindGroup(bindGroupSolid);
|
||||
context.layouts.releaseBindGroup(bindGroupGradient);
|
||||
context.releaseBuffer(bufferGroupSolid);
|
||||
context.releaseBuffer(bufferGroupGradient);
|
||||
context.releaseBuffer(bufferGroupTransfromGrad);
|
||||
context.releaseTexture(texGradient);
|
||||
context.releaseTextureView(texViewGradient);
|
||||
gradientData.release(context);
|
||||
};
|
||||
|
||||
//***********************************************************************
|
||||
|
@ -291,26 +242,10 @@ void WgRenderSettings::release(WgContext& context)
|
|||
|
||||
void WgRenderDataPaint::release(WgContext& context)
|
||||
{
|
||||
context.layouts.releaseBindGroup(bindGroupPaint);
|
||||
context.releaseBuffer(bufferModelMat);
|
||||
context.releaseBuffer(bufferBlendSettings);
|
||||
clips.clear();
|
||||
};
|
||||
|
||||
|
||||
void WgRenderDataPaint::update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity)
|
||||
{
|
||||
WgShaderTypeMat4x4f modelMat(transform);
|
||||
WgShaderTypeVec4f blendSettings(cs, opacity);
|
||||
bool bufferModelMatChanged = context.allocateBufferUniform(bufferModelMat, &modelMat, sizeof(modelMat));
|
||||
bool bufferBlendSettingsChanged = context.allocateBufferUniform(bufferBlendSettings, &blendSettings, sizeof(blendSettings));
|
||||
if (bufferModelMatChanged || bufferBlendSettingsChanged) {
|
||||
context.layouts.releaseBindGroup(bindGroupPaint);
|
||||
bindGroupPaint = context.layouts.createBindGroupBuffer2Un(bufferModelMat, bufferBlendSettings);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgRenderDataPaint::updateClips(tvg::Array<tvg::RenderData> &clips) {
|
||||
this->clips.clear();
|
||||
ARRAY_FOREACH(p, clips) {
|
||||
|
@ -322,24 +257,24 @@ void WgRenderDataPaint::updateClips(tvg::Array<tvg::RenderData> &clips) {
|
|||
// WgRenderDataShape
|
||||
//***********************************************************************
|
||||
|
||||
void WgRenderDataShape::appendShape(WgContext& context, const WgVertexBuffer& vertexBuffer)
|
||||
void WgRenderDataShape::appendShape(const WgVertexBuffer& vertexBuffer)
|
||||
{
|
||||
if (vertexBuffer.count < 3) return;
|
||||
Point pmin{}, pmax{};
|
||||
vertexBuffer.getMinMax(pmin, pmax);
|
||||
meshGroupShapes.append(context, vertexBuffer);
|
||||
meshGroupShapesBBox.append(context, pmin, pmax);
|
||||
meshGroupShapes.append(vertexBuffer);
|
||||
meshGroupShapesBBox.append(pmin, pmax);
|
||||
updateBBox(pmin, pmax);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderDataShape::appendStroke(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd)
|
||||
void WgRenderDataShape::appendStroke(const WgIndexedVertexBuffer& vertexBufferInd)
|
||||
{
|
||||
if (vertexBufferInd.vcount < 3) return;
|
||||
Point pmin{}, pmax{};
|
||||
vertexBufferInd.getMinMax(pmin, pmax);
|
||||
meshGroupStrokes.append(context, vertexBufferInd);
|
||||
meshGroupStrokesBBox.append(context, pmin, pmax);
|
||||
meshGroupStrokes.append(vertexBufferInd);
|
||||
meshGroupStrokesBBox.append(pmin, pmax);
|
||||
updateBBox(pmin, pmax);
|
||||
}
|
||||
|
||||
|
@ -358,14 +293,14 @@ void WgRenderDataShape::updateAABB(const Matrix& tr) {
|
|||
auto p1 = Point{pMax.x, pMin.y} * tr;
|
||||
auto p2 = Point{pMin.x, pMax.y} * tr;
|
||||
auto p3 = Point{pMax.x, pMax.y} * tr;
|
||||
aabb.min = {std::min({p0.x, p1.x, p2.x, p3.x}), std::min({p0.y, p1.y, p2.y, p3.y})};
|
||||
aabb.max = {std::max({p0.x, p1.x, p2.x, p3.x}), std::max({p0.y, p1.y, p2.y, p3.y})};
|
||||
aabb.min = {std::min(std::min(p0.x, p1.x), std::min(p2.x, p3.x)), std::min(std::min(p0.y, p1.y), std::min(p2.y, p3.y))};
|
||||
aabb.max = {std::max(std::max(p0.x, p1.x), std::max(p2.x, p3.x)), std::max(std::max(p0.y, p1.y), std::max(p2.y, p3.y))};
|
||||
}
|
||||
|
||||
|
||||
void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rshape, const Matrix& tr, WgGeometryBufferPool* pool)
|
||||
void WgRenderDataShape::updateMeshes(const RenderShape &rshape, const Matrix& tr, WgGeometryBufferPool* pool)
|
||||
{
|
||||
releaseMeshes(context);
|
||||
releaseMeshes();
|
||||
strokeFirst = rshape.strokeFirst();
|
||||
|
||||
// get object scale
|
||||
|
@ -375,8 +310,8 @@ void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rsha
|
|||
auto pbuff = pool->reqVertexBuffer(scale);
|
||||
|
||||
pbuff->decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) {
|
||||
appendShape(context, path_buff);
|
||||
if ((rshape.stroke) && (rshape.stroke->width > 0)) proceedStrokes(context, rshape.stroke, path_buff, pool);
|
||||
appendShape(path_buff);
|
||||
if ((rshape.stroke) && (rshape.stroke->width > 0)) proceedStrokes(rshape.stroke, path_buff, pool);
|
||||
}, rshape.trimpath());
|
||||
|
||||
// update shapes bbox (with empty path handling)
|
||||
|
@ -384,31 +319,31 @@ void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rsha
|
|||
(this->meshGroupStrokesBBox.meshes.count > 0)) {
|
||||
updateAABB(tr);
|
||||
} else aabb = {{0, 0}, {0, 0}};
|
||||
meshDataBBox.bbox(context, pMin, pMax);
|
||||
meshDataBBox.bbox(pMin, pMax);
|
||||
|
||||
pool->retVertexBuffer(pbuff);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderDataShape::proceedStrokes(WgContext& context, const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool)
|
||||
void WgRenderDataShape::proceedStrokes(const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool)
|
||||
{
|
||||
assert(rstroke);
|
||||
auto strokesGenerator = pool->reqIndexedVertexBuffer(buff.scale);
|
||||
if (rstroke->dash.count == 0) strokesGenerator->appendStrokes(buff, rstroke);
|
||||
else strokesGenerator->appendStrokesDashed(buff, rstroke);
|
||||
|
||||
appendStroke(context, *strokesGenerator);
|
||||
appendStroke(*strokesGenerator);
|
||||
|
||||
pool->retIndexedVertexBuffer(strokesGenerator);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderDataShape::releaseMeshes(WgContext& context)
|
||||
void WgRenderDataShape::releaseMeshes()
|
||||
{
|
||||
meshGroupStrokesBBox.release(context);
|
||||
meshGroupStrokes.release(context);
|
||||
meshGroupShapesBBox.release(context);
|
||||
meshGroupShapes.release(context);
|
||||
meshGroupStrokesBBox.release();
|
||||
meshGroupStrokes.release();
|
||||
meshGroupShapesBBox.release();
|
||||
meshGroupShapes.release();
|
||||
pMin = {FLT_MAX, FLT_MAX};
|
||||
pMax = {0.0f, 0.0f};
|
||||
aabb = {{0, 0}, {0, 0}};
|
||||
|
@ -418,8 +353,7 @@ void WgRenderDataShape::releaseMeshes(WgContext& context)
|
|||
|
||||
void WgRenderDataShape::release(WgContext& context)
|
||||
{
|
||||
releaseMeshes(context);
|
||||
meshDataBBox.release(context);
|
||||
releaseMeshes();
|
||||
renderSettingsStroke.release(context);
|
||||
renderSettingsShape.release(context);
|
||||
WgRenderDataPaint::release(context);
|
||||
|
@ -445,10 +379,10 @@ WgRenderDataShape* WgRenderDataShapePool::allocate(WgContext& context)
|
|||
|
||||
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* renderData)
|
||||
{
|
||||
renderData->meshGroupShapes.release(context);
|
||||
renderData->meshGroupShapesBBox.release(context);
|
||||
renderData->meshGroupStrokes.release(context);
|
||||
renderData->meshGroupStrokesBBox.release(context);
|
||||
renderData->meshGroupShapes.release();
|
||||
renderData->meshGroupShapesBBox.release();
|
||||
renderData->meshGroupStrokes.release();
|
||||
renderData->meshGroupStrokesBBox.release();
|
||||
renderData->clips.clear();
|
||||
mPool.push(renderData);
|
||||
}
|
||||
|
@ -471,22 +405,16 @@ void WgRenderDataShapePool::release(WgContext& context)
|
|||
void WgRenderDataPicture::updateSurface(WgContext& context, const RenderSurface* surface)
|
||||
{
|
||||
// upoate mesh data
|
||||
meshData.imageBox(context, surface->w, surface->h);
|
||||
meshData.imageBox(surface->w, surface->h);
|
||||
// update texture data
|
||||
imageData.update(context, surface);
|
||||
// update texture bind group
|
||||
context.layouts.releaseBindGroup(bindGroupPicture);
|
||||
bindGroupPicture = context.layouts.createBindGroupTexSampled(
|
||||
context.samplerLinearRepeat, imageData.textureView
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void WgRenderDataPicture::release(WgContext& context)
|
||||
{
|
||||
context.layouts.releaseBindGroup(bindGroupPicture);
|
||||
renderSettings.release(context);
|
||||
imageData.release(context);
|
||||
meshData.release(context);
|
||||
WgRenderDataPaint::release(context);
|
||||
}
|
||||
|
||||
|
@ -681,3 +609,85 @@ void WgRenderDataEffectParamsPool::release(WgContext& context)
|
|||
mPool.clear();
|
||||
mList.clear();
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
// WgStageBufferGeometry
|
||||
//***********************************************************************
|
||||
|
||||
void WgStageBufferGeometry::append(WgMeshData* meshData)
|
||||
{
|
||||
assert(meshData);
|
||||
uint32_t vsize = meshData->vbuffer.count * sizeof(meshData->vbuffer[0]);
|
||||
uint32_t tsize = meshData->tbuffer.count * sizeof(meshData->tbuffer[0]);
|
||||
uint32_t isize = meshData->ibuffer.count * sizeof(meshData->ibuffer[0]);
|
||||
vmaxcount = std::max(vmaxcount, meshData->vbuffer.count);
|
||||
// append vertex data
|
||||
if (vbuffer.reserved < vbuffer.count + vsize)
|
||||
vbuffer.grow(std::max(vsize, vbuffer.reserved));
|
||||
if (meshData->vbuffer.count > 0) {
|
||||
meshData->voffset = vbuffer.count;
|
||||
memcpy(vbuffer.data + vbuffer.count, meshData->vbuffer.data, vsize);
|
||||
vbuffer.count += vsize;
|
||||
}
|
||||
// append tex coords data
|
||||
if (vbuffer.reserved < vbuffer.count + tsize)
|
||||
vbuffer.grow(std::max(tsize, vbuffer.reserved));
|
||||
if (meshData->tbuffer.count > 0) {
|
||||
meshData->toffset = vbuffer.count;
|
||||
memcpy(vbuffer.data + vbuffer.count, meshData->tbuffer.data, tsize);
|
||||
vbuffer.count += tsize;
|
||||
}
|
||||
// append index data
|
||||
if (ibuffer.reserved < ibuffer.count + isize)
|
||||
ibuffer.grow(std::max(isize, ibuffer.reserved));
|
||||
if (meshData->ibuffer.count > 0) {
|
||||
meshData->ioffset = ibuffer.count;
|
||||
memcpy(ibuffer.data + ibuffer.count, meshData->ibuffer.data, isize);
|
||||
ibuffer.count += isize;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgStageBufferGeometry::append(WgMeshDataGroup* meshDataGroup)
|
||||
{
|
||||
ARRAY_FOREACH(p, meshDataGroup->meshes) append(*p);
|
||||
}
|
||||
|
||||
|
||||
void WgStageBufferGeometry::append(WgRenderDataShape* renderDataShape)
|
||||
{
|
||||
append(&renderDataShape->meshGroupShapes);
|
||||
append(&renderDataShape->meshGroupShapesBBox);
|
||||
append(&renderDataShape->meshGroupStrokes);
|
||||
append(&renderDataShape->meshGroupStrokesBBox);
|
||||
append(&renderDataShape->meshDataBBox);
|
||||
}
|
||||
|
||||
|
||||
void WgStageBufferGeometry::append(WgRenderDataPicture* renderDataPicture)
|
||||
{
|
||||
append(&renderDataPicture->meshData);
|
||||
}
|
||||
|
||||
|
||||
void WgStageBufferGeometry::release(WgContext& context)
|
||||
{
|
||||
context.releaseBuffer(vbuffer_gpu);
|
||||
context.releaseBuffer(ibuffer_gpu);
|
||||
}
|
||||
|
||||
|
||||
void WgStageBufferGeometry::clear()
|
||||
{
|
||||
vbuffer.clear();
|
||||
ibuffer.clear();
|
||||
vmaxcount = 0;
|
||||
}
|
||||
|
||||
|
||||
void WgStageBufferGeometry::flush(WgContext& context)
|
||||
{
|
||||
context.allocateBufferVertex(vbuffer_gpu, (float *)vbuffer.data, vbuffer.count);
|
||||
context.allocateBufferIndex(ibuffer_gpu, (uint32_t *)ibuffer.data, ibuffer.count);
|
||||
context.allocateBufferIndexFan(vmaxcount);
|
||||
}
|
||||
|
|
|
@ -28,49 +28,36 @@
|
|||
#include "tvgWgShaderTypes.h"
|
||||
|
||||
struct WgMeshData {
|
||||
WGPUBuffer bufferPosition{};
|
||||
WGPUBuffer bufferTexCoord{};
|
||||
WGPUBuffer bufferIndex{};
|
||||
size_t vertexCount{};
|
||||
size_t indexCount{};
|
||||
Array<Point> vbuffer;
|
||||
Array<Point> tbuffer;
|
||||
Array<uint32_t> ibuffer;
|
||||
size_t voffset{};
|
||||
size_t toffset{};
|
||||
size_t ioffset{};
|
||||
|
||||
void draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
|
||||
void drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
|
||||
void drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
|
||||
|
||||
void update(WgContext& context, const WgVertexBuffer& vertexBuffer);
|
||||
void update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd);
|
||||
void bbox(WgContext& context, const Point pmin, const Point pmax);
|
||||
void imageBox(WgContext& context, float w, float h);
|
||||
void blitBox(WgContext& context);
|
||||
void release(WgContext& context);
|
||||
};
|
||||
|
||||
class WgMeshDataPool {
|
||||
private:
|
||||
Array<WgMeshData*> mPool;
|
||||
Array<WgMeshData*> mList;
|
||||
public:
|
||||
static WgMeshDataPool* gMeshDataPool;
|
||||
WgMeshData* allocate(WgContext& context);
|
||||
void free(WgContext& context, WgMeshData* meshData);
|
||||
void release(WgContext& context);
|
||||
void update(const WgVertexBuffer& vertexBuffer);
|
||||
void update(const WgIndexedVertexBuffer& vertexBufferInd);
|
||||
void bbox(const Point pmin, const Point pmax);
|
||||
void imageBox(float w, float h);
|
||||
void blitBox();
|
||||
};
|
||||
|
||||
struct WgMeshDataGroup {
|
||||
Array<WgMeshData*> meshes{};
|
||||
|
||||
void append(WgContext& context, const WgVertexBuffer& vertexBuffer);
|
||||
void append(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd);
|
||||
void append(WgContext& context, const Point pmin, const Point pmax);
|
||||
void release(WgContext& context);
|
||||
void append(const WgVertexBuffer& vertexBuffer);
|
||||
void append(const WgIndexedVertexBuffer& vertexBufferInd);
|
||||
void append(const Point pmin, const Point pmax);
|
||||
void release();
|
||||
};
|
||||
|
||||
struct WgImageData {
|
||||
WGPUTexture texture{};
|
||||
WGPUTextureView textureView{};
|
||||
WGPUBindGroup bindGroup{};
|
||||
|
||||
void update(WgContext& context, const RenderSurface* surface);
|
||||
void update(WgContext& context, const Fill* fill);
|
||||
void release(WgContext& context);
|
||||
};
|
||||
|
||||
|
@ -79,37 +66,29 @@ enum class WgRenderRasterType { Solid = 0, Gradient, Image };
|
|||
|
||||
struct WgRenderSettings
|
||||
{
|
||||
WGPUBuffer bufferGroupSolid{};
|
||||
WGPUBindGroup bindGroupSolid{};
|
||||
WGPUTexture texGradient{};
|
||||
WGPUTextureView texViewGradient{};
|
||||
WGPUBuffer bufferGroupGradient{};
|
||||
WGPUBuffer bufferGroupTransfromGrad{};
|
||||
WGPUBindGroup bindGroupGradient{};
|
||||
uint32_t bindGroupInd{};
|
||||
WgShaderTypePaintSettings settings;
|
||||
WgImageData gradientData;
|
||||
WgRenderSettingsType fillType{};
|
||||
WgRenderRasterType rasterType{};
|
||||
bool skip{};
|
||||
|
||||
void updateFill(WgContext& context, const Fill* fill);
|
||||
void updateColor(WgContext& context, const RenderColor& c);
|
||||
void update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity);
|
||||
void update(WgContext& context, const Fill* fill);
|
||||
void update(WgContext& context, const RenderColor& c);
|
||||
void release(WgContext& context);
|
||||
};
|
||||
|
||||
struct WgRenderDataPaint
|
||||
{
|
||||
WGPUBuffer bufferModelMat{};
|
||||
WGPUBuffer bufferBlendSettings{};
|
||||
WGPUBindGroup bindGroupPaint{};
|
||||
RenderRegion viewport{};
|
||||
BBox aabb{{},{}};
|
||||
float opacity{};
|
||||
RenderRegion viewport{};
|
||||
Array<WgRenderDataPaint*> clips;
|
||||
|
||||
virtual ~WgRenderDataPaint() {};
|
||||
virtual void release(WgContext& context);
|
||||
virtual Type type() { return Type::Undefined; };
|
||||
|
||||
void update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity);
|
||||
void updateClips(tvg::Array<tvg::RenderData> &clips);
|
||||
};
|
||||
|
||||
|
@ -127,13 +106,13 @@ struct WgRenderDataShape: public WgRenderDataPaint
|
|||
bool strokeFirst{};
|
||||
FillRule fillRule{};
|
||||
|
||||
void appendShape(WgContext& context, const WgVertexBuffer& vertexBuffer);
|
||||
void appendStroke(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd);
|
||||
void appendShape(const WgVertexBuffer& vertexBuffer);
|
||||
void appendStroke(const WgIndexedVertexBuffer& vertexBufferInd);
|
||||
void updateBBox(Point pmin, Point pmax);
|
||||
void updateAABB(const Matrix& tr);
|
||||
void updateMeshes(WgContext& context, const RenderShape& rshape, const Matrix& tr, WgGeometryBufferPool* pool);
|
||||
void proceedStrokes(WgContext& context, const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool);
|
||||
void releaseMeshes(WgContext& context);
|
||||
void updateMeshes(const RenderShape& rshape, const Matrix& tr, WgGeometryBufferPool* pool);
|
||||
void proceedStrokes(const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool);
|
||||
void releaseMeshes();
|
||||
void release(WgContext& context) override;
|
||||
Type type() override { return Type::Shape; };
|
||||
};
|
||||
|
@ -150,7 +129,7 @@ public:
|
|||
|
||||
struct WgRenderDataPicture: public WgRenderDataPaint
|
||||
{
|
||||
WGPUBindGroup bindGroupPicture{};
|
||||
WgRenderSettings renderSettings{};
|
||||
WgImageData imageData{};
|
||||
WgMeshData meshData{};
|
||||
|
||||
|
@ -224,4 +203,69 @@ public:
|
|||
void release(WgContext& context);
|
||||
};
|
||||
|
||||
class WgStageBufferGeometry {
|
||||
private:
|
||||
Array<uint8_t> vbuffer;
|
||||
Array<uint8_t> ibuffer;
|
||||
uint32_t vmaxcount{};
|
||||
public:
|
||||
WGPUBuffer vbuffer_gpu{};
|
||||
WGPUBuffer ibuffer_gpu{};
|
||||
|
||||
void append(WgMeshData* meshData);
|
||||
void append(WgMeshDataGroup* meshDataGroup);
|
||||
void append(WgRenderDataShape* renderDataShape);
|
||||
void append(WgRenderDataPicture* renderDataPicture);
|
||||
void initialize(WgContext& context){};
|
||||
void release(WgContext& context);
|
||||
void clear();
|
||||
void flush(WgContext& context);
|
||||
};
|
||||
|
||||
// typed uniform stage buffer with related bind groups handling
|
||||
template<typename T>
|
||||
class WgStageBufferUniform {
|
||||
private:
|
||||
Array<T> ubuffer;
|
||||
WGPUBuffer ubuffer_gpu{};
|
||||
Array<WGPUBindGroup> bbuffer;
|
||||
public:
|
||||
// append uniform data to cpu staged buffer and return related bind group index
|
||||
uint32_t append(const T& value) {
|
||||
ubuffer.push(value);
|
||||
return ubuffer.count - 1;
|
||||
}
|
||||
|
||||
void flush(WgContext& context) {
|
||||
// flush data to gpu buffer from cpu memory including reserved data to prevent future gpu buffer reallocations
|
||||
bool bufferChanged = context.allocateBufferUniform(ubuffer_gpu, (void*)ubuffer.data, ubuffer.reserved*sizeof(T));
|
||||
// if gpu buffer handle was changed we must to remove all created binding groups
|
||||
if (bufferChanged) releaseBindGroups(context);
|
||||
// allocate bind groups for all new data items
|
||||
for (uint32_t i = bbuffer.count; i < ubuffer.count; i++)
|
||||
bbuffer.push(context.layouts.createBindGroupBuffer1Un(ubuffer_gpu, i*sizeof(T), sizeof(T)));
|
||||
assert(bbuffer.count >= ubuffer.count);
|
||||
}
|
||||
|
||||
// please, use index that was returned from append method
|
||||
WGPUBindGroup operator[](const uint32_t index) const {
|
||||
return bbuffer[index];
|
||||
}
|
||||
|
||||
void clear() {
|
||||
ubuffer.clear();
|
||||
}
|
||||
|
||||
void release(WgContext& context) {
|
||||
context.releaseBuffer(ubuffer_gpu);
|
||||
releaseBindGroups(context);
|
||||
}
|
||||
|
||||
void releaseBindGroups(WgContext& context) {
|
||||
ARRAY_FOREACH(p, bbuffer)
|
||||
context.layouts.releaseBindGroup(*p);
|
||||
bbuffer.clear();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_RENDER_DATA_H_
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "tvgWgRenderTarget.h"
|
||||
|
||||
void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
void WgRenderTarget::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
@ -36,7 +36,7 @@ void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t he
|
|||
}
|
||||
|
||||
|
||||
void WgRenderStorage::release(WgContext& context)
|
||||
void WgRenderTarget::release(WgContext& context)
|
||||
{
|
||||
context.layouts.releaseBindGroup(bindGroupTexure);
|
||||
context.layouts.releaseBindGroup(bindGroupWrite);
|
||||
|
@ -50,38 +50,38 @@ void WgRenderStorage::release(WgContext& context)
|
|||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// render storage pool
|
||||
// render target pool
|
||||
//*****************************************************************************
|
||||
|
||||
WgRenderStorage* WgRenderStoragePool::allocate(WgContext& context)
|
||||
WgRenderTarget* WgRenderTargetPool::allocate(WgContext& context)
|
||||
{
|
||||
WgRenderStorage* renderStorage{};
|
||||
WgRenderTarget* renderTarget{};
|
||||
if (pool.count > 0) {
|
||||
renderStorage = pool.last();
|
||||
renderTarget = pool.last();
|
||||
pool.pop();
|
||||
} else {
|
||||
renderStorage = new WgRenderStorage;
|
||||
renderStorage->initialize(context, width, height);
|
||||
list.push(renderStorage);
|
||||
renderTarget = new WgRenderTarget;
|
||||
renderTarget->initialize(context, width, height);
|
||||
list.push(renderTarget);
|
||||
}
|
||||
return renderStorage;
|
||||
return renderTarget;
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStoragePool::free(WgContext& context, WgRenderStorage* renderStorage)
|
||||
void WgRenderTargetPool::free(WgContext& context, WgRenderTarget* renderTarget)
|
||||
{
|
||||
pool.push(renderStorage);
|
||||
pool.push(renderTarget);
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStoragePool::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
void WgRenderTargetPool::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStoragePool::release(WgContext& context)
|
||||
void WgRenderTargetPool::release(WgContext& context)
|
||||
{
|
||||
ARRAY_FOREACH(p, list) {
|
||||
(*p)->release(context);
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "tvgWgPipelines.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
struct WgRenderStorage {
|
||||
struct WgRenderTarget {
|
||||
WGPUTexture texture{};
|
||||
WGPUTexture textureMS{};
|
||||
WGPUTextureView texView{};
|
||||
|
@ -42,15 +42,15 @@ struct WgRenderStorage {
|
|||
};
|
||||
|
||||
|
||||
class WgRenderStoragePool {
|
||||
class WgRenderTargetPool {
|
||||
private:
|
||||
Array<WgRenderStorage*> list;
|
||||
Array<WgRenderStorage*> pool;
|
||||
Array<WgRenderTarget*> list;
|
||||
Array<WgRenderTarget*> pool;
|
||||
uint32_t width{};
|
||||
uint32_t height{};
|
||||
public:
|
||||
WgRenderStorage* allocate(WgContext& context);
|
||||
void free(WgContext& context, WgRenderStorage* renderTarget);
|
||||
WgRenderTarget* allocate(WgContext& context);
|
||||
void free(WgContext& context, WgRenderTarget* renderTarget);
|
||||
|
||||
void initialize(WgContext& context, uint32_t width, uint32_t height);
|
||||
void release(WgContext& context);
|
||||
|
|
93
src/renderer/wg_engine/tvgWgRenderTask.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2025 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgWgRenderTask.h"
|
||||
#include <iostream>
|
||||
|
||||
//***********************************************************************
|
||||
// WgPaintTask
|
||||
//***********************************************************************
|
||||
|
||||
void WgPaintTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
if (renderData->type() == tvg::Type::Shape)
|
||||
compositor.renderShape(context, (WgRenderDataShape*)renderData, blendMethod);
|
||||
if (renderData->type() == tvg::Type::Picture)
|
||||
compositor.renderImage(context, (WgRenderDataPicture*)renderData, blendMethod);
|
||||
else assert(true);
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
// WgSceneTask
|
||||
//***********************************************************************
|
||||
|
||||
void WgSceneTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
// begin the render pass for the current scene and clear the target content
|
||||
WGPUColor color{};
|
||||
if ((compose->method == MaskMethod::None) && (compose->blend != BlendMethod::Normal)) color = { 1.0, 1.0, 1.0, 0.0 };
|
||||
compositor.beginRenderPass(encoder, renderTarget, true, color);
|
||||
// run all childs (scenes and shapes)
|
||||
runChildren(context, compositor, encoder);
|
||||
// we must to end current render pass for current scene
|
||||
compositor.endRenderPass();
|
||||
// we must to apply effect for current scene
|
||||
if (effect)
|
||||
runEffect(context, compositor, encoder);
|
||||
// there's no point in continuing if the scene has no destination target (e.g., the root scene)
|
||||
if (!renderTargetDst) return;
|
||||
// apply scene blending
|
||||
if (compose->method == MaskMethod::None) {
|
||||
compositor.beginRenderPass(encoder, renderTargetDst, false);
|
||||
compositor.renderScene(context, renderTarget, compose);
|
||||
// apply scene composition (for scenes, that have a handle to mask)
|
||||
} else if (renderTargetMsk) {
|
||||
compositor.beginRenderPass(encoder, renderTargetDst, false);
|
||||
compositor.composeScene(context, renderTarget, renderTargetMsk, compose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgSceneTask::runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
ARRAY_FOREACH(task, children) {
|
||||
WgRenderTask* renderTask = *task;
|
||||
// we need to restore current render pass without clear
|
||||
compositor.beginRenderPass(encoder, renderTarget, false);
|
||||
// run children (shape or scene)
|
||||
renderTask->run(context, compositor, encoder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgSceneTask::runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
assert(effect);
|
||||
switch (effect->type) {
|
||||
case SceneEffect::GaussianBlur: compositor.gaussianBlur(context, renderTarget, (RenderEffectGaussianBlur*)effect, compose); break;
|
||||
case SceneEffect::DropShadow: compositor.dropShadow(context, renderTarget, (RenderEffectDropShadow*)effect, compose); break;
|
||||
case SceneEffect::Fill: compositor.fillEffect(context, renderTarget, (RenderEffectFill*)effect, compose); break;
|
||||
case SceneEffect::Tint: compositor.tintEffect(context, renderTarget, (RenderEffectTint*)effect, compose); break;
|
||||
case SceneEffect::Tritone : compositor.tritoneEffect(context, renderTarget, (RenderEffectTritone*)effect, compose); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
72
src/renderer/wg_engine/tvgWgRenderTask.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2025 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_WG_RENDER_TASK_H_
|
||||
#define _TVG_WG_RENDER_TASK_H_
|
||||
|
||||
#include "tvgWgCompositor.h"
|
||||
|
||||
// base class for any renderable objects
|
||||
struct WgRenderTask {
|
||||
virtual ~WgRenderTask() {}
|
||||
virtual void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) = 0;
|
||||
};
|
||||
|
||||
// task for sinlge shape rendering
|
||||
struct WgPaintTask: public WgRenderTask {
|
||||
// shape render properties
|
||||
WgRenderDataPaint* renderData{};
|
||||
BlendMethod blendMethod{};
|
||||
|
||||
WgPaintTask(WgRenderDataPaint* renderData, BlendMethod blendMethod) :
|
||||
renderData(renderData), blendMethod(blendMethod) {}
|
||||
// apply shape execution, including custom blending and clipping
|
||||
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
|
||||
};
|
||||
|
||||
// task for scene rendering with blending, composition and effect
|
||||
struct WgSceneTask: public WgRenderTask {
|
||||
public:
|
||||
// parent scene (nullptr for root scene)
|
||||
WgSceneTask* parent{};
|
||||
// childs can be shapes or scenes tesks
|
||||
Array<WgRenderTask*> children;
|
||||
// scene blend/compose targets
|
||||
WgRenderTarget* renderTarget{};
|
||||
WgRenderTarget* renderTargetMsk{};
|
||||
WgRenderTarget* renderTargetDst{};
|
||||
// scene blend/compose properties
|
||||
WgCompose* compose{};
|
||||
// scene effect properties
|
||||
const RenderEffect* effect{};
|
||||
|
||||
WgSceneTask(WgRenderTarget* renderTarget, WgCompose* compose, WgSceneTask* parent) :
|
||||
parent(parent), renderTarget(renderTarget), compose(compose) {}
|
||||
// run all, including all shapes drawing, blending, composition and effect
|
||||
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
|
||||
private:
|
||||
void runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
|
||||
void runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_RENDER_TASK_H_
|
||||
|
|
@ -34,10 +34,8 @@ static atomic<int32_t> rendererCnt{-1};
|
|||
|
||||
void WgRenderer::release()
|
||||
{
|
||||
// check for general context availability
|
||||
if (!mContext.queue) return;
|
||||
|
||||
// dispose stored objects
|
||||
disposeObjects();
|
||||
|
||||
// clear render data paint pools
|
||||
|
@ -45,15 +43,14 @@ void WgRenderer::release()
|
|||
mRenderDataPicturePool.release(mContext);
|
||||
mRenderDataViewportPool.release(mContext);
|
||||
mRenderDataEffectParamsPool.release(mContext);
|
||||
WgMeshDataPool::gMeshDataPool->release(mContext);
|
||||
|
||||
// clear render storage pool
|
||||
mRenderStoragePool.release(mContext);
|
||||
// clear render pool
|
||||
mRenderTargetPool.release(mContext);
|
||||
|
||||
// clear rendering tree stacks
|
||||
mCompositorStack.clear();
|
||||
mRenderStorageStack.clear();
|
||||
mRenderStorageRoot.release(mContext);
|
||||
mCompositorList.clear();
|
||||
mRenderTargetStack.clear();
|
||||
mRenderTargetRoot.release(mContext);
|
||||
|
||||
// release context handles
|
||||
mCompositor.release(mContext);
|
||||
|
@ -102,7 +99,6 @@ void WgRenderer::clearTargets()
|
|||
|
||||
bool WgRenderer::surfaceConfigure(WGPUSurface surface, WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
// store target surface properties
|
||||
this->surface = surface;
|
||||
if (width == 0 || height == 0 || !surface) return false;
|
||||
|
||||
|
@ -135,33 +131,29 @@ bool WgRenderer::surfaceConfigure(WGPUSurface surface, WgContext& context, uint3
|
|||
|
||||
RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
|
||||
{
|
||||
// get or create render data shape
|
||||
auto renderDataShape = (WgRenderDataShape*)data;
|
||||
if (!renderDataShape)
|
||||
renderDataShape = mRenderDataShapePool.allocate(mContext);
|
||||
auto renderDataShape = data ? (WgRenderDataShape*)data : mRenderDataShapePool.allocate(mContext);
|
||||
|
||||
// update geometry
|
||||
if ((!data) || (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke))) {
|
||||
renderDataShape->updateMeshes(mContext, rshape, transform, mBufferPool.pool);
|
||||
renderDataShape->updateMeshes(rshape, transform, mBufferPool.pool);
|
||||
}
|
||||
|
||||
// update paint settings
|
||||
if ((!data) || (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend))) {
|
||||
renderDataShape->update(mContext, transform, mTargetSurface.cs, opacity);
|
||||
renderDataShape->renderSettingsShape.update(mContext, transform, mTargetSurface.cs, opacity);
|
||||
renderDataShape->renderSettingsStroke.update(mContext, transform, mTargetSurface.cs, opacity);
|
||||
renderDataShape->fillRule = rshape.rule;
|
||||
}
|
||||
|
||||
// setup fill settings
|
||||
renderDataShape->viewport = mViewport;
|
||||
renderDataShape->opacity = opacity;
|
||||
if (flags & RenderUpdateFlag::Gradient && rshape.fill) renderDataShape->renderSettingsShape.updateFill(mContext, rshape.fill);
|
||||
else if (flags & RenderUpdateFlag::Color) renderDataShape->renderSettingsShape.updateColor(mContext, rshape.color);
|
||||
if (flags & RenderUpdateFlag::Gradient && rshape.fill) renderDataShape->renderSettingsShape.update(mContext, rshape.fill);
|
||||
else if (flags & RenderUpdateFlag::Color) renderDataShape->renderSettingsShape.update(mContext, rshape.color);
|
||||
if (rshape.stroke) {
|
||||
if (flags & RenderUpdateFlag::GradientStroke && rshape.stroke->fill) renderDataShape->renderSettingsStroke.updateFill(mContext, rshape.stroke->fill);
|
||||
else if (flags & RenderUpdateFlag::Stroke) renderDataShape->renderSettingsStroke.updateColor(mContext, rshape.stroke->color);
|
||||
if (flags & RenderUpdateFlag::GradientStroke && rshape.stroke->fill) renderDataShape->renderSettingsStroke.update(mContext, rshape.stroke->fill);
|
||||
else if (flags & RenderUpdateFlag::Stroke) renderDataShape->renderSettingsStroke.update(mContext, rshape.stroke->color);
|
||||
}
|
||||
|
||||
// store clips data
|
||||
renderDataShape->updateClips(clips);
|
||||
|
||||
return renderDataShape;
|
||||
|
@ -170,16 +162,12 @@ RenderData WgRenderer::prepare(const RenderShape& rshape, RenderData data, const
|
|||
|
||||
RenderData WgRenderer::prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
{
|
||||
// get or create render data shape
|
||||
auto renderDataPicture = (WgRenderDataPicture*)data;
|
||||
if (!renderDataPicture)
|
||||
renderDataPicture = mRenderDataPicturePool.allocate(mContext);
|
||||
auto renderDataPicture = data ? (WgRenderDataPicture*)data : mRenderDataPicturePool.allocate(mContext);
|
||||
|
||||
// update paint settings
|
||||
renderDataPicture->viewport = mViewport;
|
||||
renderDataPicture->opacity = opacity;
|
||||
if (flags & (RenderUpdateFlag::Transform | RenderUpdateFlag::Blend)) {
|
||||
renderDataPicture->update(mContext, transform, surface->cs, opacity);
|
||||
renderDataPicture->renderSettings.update(mContext, transform, surface->cs, opacity);
|
||||
}
|
||||
|
||||
// update image data
|
||||
|
@ -187,58 +175,87 @@ RenderData WgRenderer::prepare(RenderSurface* surface, RenderData data, const Ma
|
|||
renderDataPicture->updateSurface(mContext, surface);
|
||||
}
|
||||
|
||||
// store clips data
|
||||
renderDataPicture->updateClips(clips);
|
||||
|
||||
return renderDataPicture;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::preRender()
|
||||
{
|
||||
// push rot render storage to the render tree stack
|
||||
assert(mRenderStorageStack.count == 0);
|
||||
mRenderStorageStack.push(&mRenderStorageRoot);
|
||||
// create command encoder for drawing
|
||||
WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||
mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
|
||||
// start root render pass
|
||||
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true);
|
||||
if (mContext.invalid()) return false;
|
||||
|
||||
mCompositor.reset(mContext);
|
||||
|
||||
assert(mRenderTargetStack.count == 0);
|
||||
mRenderTargetStack.push(&mRenderTargetRoot);
|
||||
|
||||
// create root compose settings
|
||||
WgCompose* compose = new WgCompose();
|
||||
compose->aabb = { { 0, 0 }, { (int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h } };
|
||||
compose->blend = BlendMethod::Normal;
|
||||
compose->method = MaskMethod::None;
|
||||
compose->opacity = 255;
|
||||
mCompositorList.push(compose);
|
||||
|
||||
// create root scene
|
||||
WgSceneTask* sceneTask = new WgSceneTask(&mRenderTargetRoot, compose, nullptr);
|
||||
mRenderTaskList.push(sceneTask);
|
||||
mSceneTaskStack.push(sceneTask);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::renderShape(RenderData data)
|
||||
{
|
||||
// temporary simple render data to the current render target
|
||||
mCompositor.renderShape(mContext, (WgRenderDataShape*)data, mBlendMethod);
|
||||
WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod);
|
||||
WgSceneTask* sceneTask = mSceneTaskStack.last();
|
||||
sceneTask->children.push(paintTask);
|
||||
mRenderTaskList.push(paintTask);
|
||||
mCompositor.requestShape((WgRenderDataShape*)data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::renderImage(RenderData data)
|
||||
{
|
||||
// temporary simple render data to the current render target
|
||||
mCompositor.renderImage(mContext, (WgRenderDataPicture*)data, mBlendMethod);
|
||||
WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod);
|
||||
WgSceneTask* sceneTask = mSceneTaskStack.last();
|
||||
sceneTask->children.push(paintTask);
|
||||
mRenderTaskList.push(paintTask);
|
||||
mCompositor.requestImage((WgRenderDataPicture*)data);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::postRender()
|
||||
{
|
||||
// end root render pass
|
||||
mCompositor.endRenderPass();
|
||||
// release command encoder
|
||||
const WGPUCommandBufferDescriptor commandBufferDesc{};
|
||||
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(mCommandEncoder, &commandBufferDesc);
|
||||
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer);
|
||||
wgpuCommandBufferRelease(commandsBuffer);
|
||||
wgpuCommandEncoderRelease(mCommandEncoder);
|
||||
// pop root render storage to the render tree stack
|
||||
mRenderStorageStack.pop();
|
||||
assert(mRenderStorageStack.count == 0);
|
||||
// clear viewport list and store allocated handles to pool
|
||||
ARRAY_FOREACH(p, mRenderDataViewportList)
|
||||
// flush stage data to gpu
|
||||
mCompositor.flush(mContext);
|
||||
|
||||
// create command encoder for drawing
|
||||
WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder();
|
||||
|
||||
// run rendering (all the fun is here)
|
||||
WgSceneTask* sceneTaskRoot = mSceneTaskStack.last();
|
||||
sceneTaskRoot->run(mContext, mCompositor, commandEncoder);
|
||||
|
||||
// execute and release command encoder
|
||||
mContext.submitCommandEncoder(commandEncoder);
|
||||
mContext.releaseCommandEncoder(commandEncoder);
|
||||
|
||||
// clear the render tasks tree
|
||||
mSceneTaskStack.pop();
|
||||
assert(mSceneTaskStack.count == 0);
|
||||
mRenderTargetStack.pop();
|
||||
assert(mRenderTargetStack.count == 0);
|
||||
ARRAY_FOREACH(p, mRenderTaskList) { delete (*p); };
|
||||
mRenderTaskList.clear();
|
||||
ARRAY_FOREACH(p, mCompositorList) { delete (*p); };
|
||||
mCompositorList.clear();
|
||||
ARRAY_FOREACH(p, mRenderDataViewportList) {
|
||||
mRenderDataViewportPool.free(mContext, *p);
|
||||
}
|
||||
mRenderDataViewportList.clear();
|
||||
return true;
|
||||
}
|
||||
|
@ -257,14 +274,9 @@ RenderRegion WgRenderer::region(RenderData data)
|
|||
if (renderData->type() == Type::Shape) {
|
||||
auto& v1 = renderData->aabb.min;
|
||||
auto& v2 = renderData->aabb.max;
|
||||
RenderRegion renderRegion;
|
||||
renderRegion.x = static_cast<int32_t>(nearbyint(v1.x));
|
||||
renderRegion.y = static_cast<int32_t>(nearbyint(v1.y));
|
||||
renderRegion.w = static_cast<int32_t>(nearbyint(v2.x)) - renderRegion.x;
|
||||
renderRegion.h = static_cast<int32_t>(nearbyint(v2.y)) - renderRegion.y;
|
||||
return renderRegion;
|
||||
return {{int32_t(nearbyint(v1.x)), int32_t(nearbyint(v1.y))}, {int32_t(nearbyint(v2.x)), int32_t(nearbyint(v2.y))}};
|
||||
}
|
||||
return { 0, 0, (int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h };
|
||||
return {{0, 0}, {(int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h}};
|
||||
}
|
||||
|
||||
|
||||
|
@ -301,6 +313,8 @@ const RenderSurface* WgRenderer::mainSurface()
|
|||
|
||||
bool WgRenderer::clear()
|
||||
{
|
||||
if (mContext.invalid()) return false;
|
||||
|
||||
//TODO: clear the current target buffer only if clear() is called
|
||||
return true;
|
||||
}
|
||||
|
@ -308,6 +322,8 @@ bool WgRenderer::clear()
|
|||
|
||||
bool WgRenderer::sync()
|
||||
{
|
||||
if (mContext.invalid()) return false;
|
||||
|
||||
disposeObjects();
|
||||
|
||||
// if texture buffer used
|
||||
|
@ -317,57 +333,45 @@ bool WgRenderer::sync()
|
|||
wgpuSurfaceGetCurrentTexture(surface, &surfaceTexture);
|
||||
dstTexture = surfaceTexture.texture;
|
||||
}
|
||||
// there is no external dest buffer
|
||||
|
||||
if (!dstTexture) return false;
|
||||
|
||||
// get external dest buffer
|
||||
// insure that surface and offscreen target have the same size
|
||||
if ((wgpuTextureGetWidth(dstTexture) == mRenderTargetRoot.width) &&
|
||||
(wgpuTextureGetHeight(dstTexture) == mRenderTargetRoot.height)) {
|
||||
WGPUTextureView dstTextureView = mContext.createTextureView(dstTexture);
|
||||
|
||||
// create command encoder
|
||||
const WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||
WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
|
||||
|
||||
WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder();
|
||||
// show root offscreen buffer
|
||||
mCompositor.blit(mContext, commandEncoder, &mRenderStorageRoot, dstTextureView);
|
||||
|
||||
// release command encoder
|
||||
const WGPUCommandBufferDescriptor commandBufferDesc{};
|
||||
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
|
||||
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer);
|
||||
wgpuCommandBufferRelease(commandsBuffer);
|
||||
wgpuCommandEncoderRelease(commandEncoder);
|
||||
|
||||
// release dest buffer view
|
||||
mCompositor.blit(mContext, commandEncoder, &mRenderTargetRoot, dstTextureView);
|
||||
mContext.submitCommandEncoder(commandEncoder);
|
||||
mContext.releaseCommandEncoder(commandEncoder);
|
||||
mContext.releaseTextureView(dstTextureView);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// render target handle
|
||||
bool WgRenderer::target(WGPUDevice device, WGPUInstance instance, void* target, uint32_t width, uint32_t height, int type)
|
||||
{
|
||||
// release all existing handles
|
||||
if (!instance || !device || !target) {
|
||||
// release all handles
|
||||
release();
|
||||
return true;
|
||||
}
|
||||
|
||||
// can not initialize renderer, give up
|
||||
if (!instance || !device || !target || !width || !height)
|
||||
return false;
|
||||
if (!width || !height) return false;
|
||||
|
||||
// device or instance was changed, need to recreate all instances
|
||||
if ((mContext.device != device) || (mContext.instance != instance)) {
|
||||
// release all handles
|
||||
release();
|
||||
|
||||
// initialize base rendering handles
|
||||
mContext.initialize(instance, device);
|
||||
|
||||
// initialize render tree instances
|
||||
mRenderStoragePool.initialize(mContext, width, height);
|
||||
mRenderStorageRoot.initialize(mContext, width, height);
|
||||
mRenderTargetPool.initialize(mContext, width, height);
|
||||
mRenderTargetRoot.initialize(mContext, width, height);
|
||||
mCompositor.initialize(mContext, width, height);
|
||||
|
||||
// store target properties
|
||||
|
@ -386,12 +390,12 @@ bool WgRenderer::target(WGPUDevice device, WGPUInstance instance, void* target,
|
|||
// update render targets dimentions
|
||||
if ((mTargetSurface.w != width) || (mTargetSurface.h != height) || (type == 0 ? (surface != (WGPUSurface)target) : (targetTexture != (WGPUTexture)target))) {
|
||||
// release render tagets
|
||||
mRenderStoragePool.release(mContext);
|
||||
mRenderStorageRoot.release(mContext);
|
||||
mRenderTargetPool.release(mContext);
|
||||
mRenderTargetRoot.release(mContext);
|
||||
clearTargets();
|
||||
|
||||
mRenderStoragePool.initialize(mContext, width, height);
|
||||
mRenderStorageRoot.initialize(mContext, width, height);
|
||||
mRenderTargetPool.initialize(mContext, width, height);
|
||||
mRenderTargetRoot.initialize(mContext, width, height);
|
||||
mCompositor.resize(mContext, width, height);
|
||||
|
||||
// store target properties
|
||||
|
@ -446,7 +450,7 @@ RenderCompositor* WgRenderer::target(const RenderRegion& region, TVG_UNUSED Colo
|
|||
compose->rdViewport->update(mContext, region);
|
||||
mRenderDataViewportList.push(compose->rdViewport);
|
||||
}
|
||||
mCompositorStack.push(compose);
|
||||
mCompositorList.push(compose);
|
||||
return compose;
|
||||
}
|
||||
|
||||
|
@ -458,145 +462,101 @@ bool WgRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_
|
|||
compose->method = method;
|
||||
compose->opacity = opacity;
|
||||
compose->blend = mBlendMethod;
|
||||
// end current render pass
|
||||
mCompositor.endRenderPass();
|
||||
// allocate new render storage and push to the render tree stack
|
||||
WgRenderStorage* storage = mRenderStoragePool.allocate(mContext);
|
||||
mRenderStorageStack.push(storage);
|
||||
// begin newly added render pass
|
||||
WGPUColor color{};
|
||||
if ((compose->method == MaskMethod::None) && (compose->blend != BlendMethod::Normal)) color = { 1.0, 1.0, 1.0, 0.0 };
|
||||
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true, color);
|
||||
WgSceneTask* sceneTaskCurrent = mSceneTaskStack.last();
|
||||
// allocate new render target and push to the render tree stack
|
||||
WgRenderTarget* renderTarget = mRenderTargetPool.allocate(mContext);
|
||||
mRenderTargetStack.push(renderTarget);
|
||||
// create and setup new scene task
|
||||
WgSceneTask* sceneTask = new WgSceneTask(renderTarget, compose, sceneTaskCurrent);
|
||||
sceneTaskCurrent->children.push(sceneTask);
|
||||
mRenderTaskList.push(sceneTask);
|
||||
mSceneTaskStack.push(sceneTask);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::endComposite(RenderCompositor* cmp)
|
||||
{
|
||||
// get current composition settings
|
||||
WgCompose* comp = (WgCompose*)cmp;
|
||||
// we must to end current render pass to run blend/composition mechanics
|
||||
mCompositor.endRenderPass();
|
||||
// finish scene blending
|
||||
if (comp->method == MaskMethod::None) {
|
||||
// get source and destination render storages
|
||||
WgRenderStorage* src = mRenderStorageStack.last();
|
||||
mRenderStorageStack.pop();
|
||||
WgRenderStorage* dst = mRenderStorageStack.last();
|
||||
// begin previous render pass
|
||||
mCompositor.beginRenderPass(mCommandEncoder, dst, false);
|
||||
// apply composition
|
||||
mCompositor.renderScene(mContext, src, comp);
|
||||
if (cmp->method == MaskMethod::None) {
|
||||
// get source and destination render targets
|
||||
WgRenderTarget* src = mRenderTargetStack.last();
|
||||
mRenderTargetStack.pop();
|
||||
|
||||
WgSceneTask* srcScene = mSceneTaskStack.last();
|
||||
mSceneTaskStack.pop();
|
||||
// setup render target compose destitations
|
||||
srcScene->renderTargetDst = mSceneTaskStack.last()->renderTarget;
|
||||
srcScene->renderTargetMsk = nullptr;
|
||||
// back render targets to the pool
|
||||
mRenderStoragePool.free(mContext, src);
|
||||
} else { // finish composition
|
||||
// get source, mask and destination render storages
|
||||
WgRenderStorage* src = mRenderStorageStack.last();
|
||||
mRenderStorageStack.pop();
|
||||
WgRenderStorage* msk = mRenderStorageStack.last();
|
||||
mRenderStorageStack.pop();
|
||||
WgRenderStorage* dst = mRenderStorageStack.last();
|
||||
// begin previous render pass
|
||||
mCompositor.beginRenderPass(mCommandEncoder, dst, false);
|
||||
// apply composition
|
||||
mCompositor.composeScene(mContext, src, msk, comp);
|
||||
mRenderTargetPool.free(mContext, src);
|
||||
} else { // finish scene composition
|
||||
// get source, mask and destination render targets
|
||||
WgRenderTarget* src = mRenderTargetStack.last();
|
||||
mRenderTargetStack.pop();
|
||||
WgRenderTarget* msk = mRenderTargetStack.last();
|
||||
mRenderTargetStack.pop();
|
||||
// get source and mask scenes
|
||||
WgSceneTask* srcScene = mSceneTaskStack.last();
|
||||
mSceneTaskStack.pop();
|
||||
WgSceneTask* mskScene = mSceneTaskStack.last();
|
||||
mSceneTaskStack.pop();
|
||||
// setup render target compose destitations
|
||||
srcScene->renderTargetDst = mSceneTaskStack.last()->renderTarget;
|
||||
srcScene->renderTargetMsk = mskScene->renderTarget;
|
||||
// back render targets to the pool
|
||||
mRenderStoragePool.free(mContext, src);
|
||||
mRenderStoragePool.free(mContext, msk);
|
||||
mRenderTargetPool.free(mContext, src);
|
||||
mRenderTargetPool.free(mContext, msk);
|
||||
}
|
||||
|
||||
// delete current compositor settings
|
||||
delete mCompositorStack.last();
|
||||
mCompositorStack.pop();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void WgRenderer::prepare(RenderEffect* effect, const Matrix& transform)
|
||||
{
|
||||
// prepare gaussian blur data
|
||||
if (!effect->rd) effect->rd = mRenderDataEffectParamsPool.allocate(mContext);
|
||||
auto renderData = (WgRenderDataEffectParams*)effect->rd;
|
||||
|
||||
if (effect->type == SceneEffect::GaussianBlur) {
|
||||
auto renderEffect = (RenderEffectGaussianBlur*)effect;
|
||||
auto renderData = (WgRenderDataEffectParams*)renderEffect->rd;
|
||||
if (!renderData) {
|
||||
renderData = mRenderDataEffectParamsPool.allocate(mContext);
|
||||
renderEffect->rd = renderData;
|
||||
renderData->update(mContext, (RenderEffectGaussianBlur*)effect, transform);
|
||||
} else if (effect->type == SceneEffect::DropShadow) {
|
||||
renderData->update(mContext, (RenderEffectDropShadow*)effect, transform);
|
||||
} else if (effect->type == SceneEffect::Fill) {
|
||||
renderData->update(mContext, (RenderEffectFill*)effect);
|
||||
} else if (effect->type == SceneEffect::Tint) {
|
||||
renderData->update(mContext, (RenderEffectTint*)effect);
|
||||
} else if (effect->type == SceneEffect::Tritone) {
|
||||
renderData->update(mContext, (RenderEffectTritone*)effect);
|
||||
} else {
|
||||
TVGERR("WG_ENGINE", "Missing effect type? = %d", (int) effect->type);
|
||||
return;
|
||||
}
|
||||
renderData->update(mContext, renderEffect, transform);
|
||||
|
||||
effect->valid = true;
|
||||
} else
|
||||
// prepare drop shadow data
|
||||
if (effect->type == SceneEffect::DropShadow) {
|
||||
auto renderEffect = (RenderEffectDropShadow*)effect;
|
||||
auto renderData = (WgRenderDataEffectParams*)renderEffect->rd;
|
||||
if (!renderData) {
|
||||
renderData = mRenderDataEffectParamsPool.allocate(mContext);
|
||||
renderEffect->rd = renderData;
|
||||
}
|
||||
renderData->update(mContext, renderEffect, transform);
|
||||
effect->valid = true;
|
||||
} else
|
||||
// prepare fill
|
||||
if (effect->type == SceneEffect::Fill) {
|
||||
auto renderEffect = (RenderEffectFill*)effect;
|
||||
auto renderData = (WgRenderDataEffectParams*)renderEffect->rd;
|
||||
if (!renderData) {
|
||||
renderData = mRenderDataEffectParamsPool.allocate(mContext);
|
||||
renderEffect->rd = renderData;
|
||||
}
|
||||
renderData->update(mContext, renderEffect);
|
||||
effect->valid = true;
|
||||
} else
|
||||
// prepare tint
|
||||
if (effect->type == SceneEffect::Tint) {
|
||||
auto renderEffect = (RenderEffectTint*)effect;
|
||||
auto renderData = (WgRenderDataEffectParams*)renderEffect->rd;
|
||||
if (!renderData) {
|
||||
renderData = mRenderDataEffectParamsPool.allocate(mContext);
|
||||
renderEffect->rd = renderData;
|
||||
}
|
||||
renderData->update(mContext, renderEffect);
|
||||
effect->valid = true;
|
||||
} else
|
||||
// prepare tritone
|
||||
if (effect->type == SceneEffect::Tritone) {
|
||||
auto renderEffect = (RenderEffectTritone*)effect;
|
||||
auto renderData = (WgRenderDataEffectParams*)renderEffect->rd;
|
||||
if (!renderData) {
|
||||
renderData = mRenderDataEffectParamsPool.allocate(mContext);
|
||||
renderEffect->rd = renderData;
|
||||
}
|
||||
renderData->update(mContext, renderEffect);
|
||||
effect->valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WgRenderer::region(RenderEffect* effect)
|
||||
{
|
||||
// update gaussian blur region
|
||||
if (effect->type == SceneEffect::GaussianBlur) {
|
||||
auto gaussian = (RenderEffectGaussianBlur*)effect;
|
||||
auto renderData = (WgRenderDataEffectParams*)gaussian->rd;
|
||||
if (gaussian->direction != 2) {
|
||||
gaussian->extend.x = -renderData->extend;
|
||||
gaussian->extend.w = +renderData->extend * 2;
|
||||
gaussian->extend.min.x = -renderData->extend;
|
||||
gaussian->extend.max.x = +renderData->extend;
|
||||
}
|
||||
if (gaussian->direction != 1) {
|
||||
gaussian->extend.y = -renderData->extend;
|
||||
gaussian->extend.h = +renderData->extend * 2;
|
||||
gaussian->extend.min.y = -renderData->extend;
|
||||
gaussian->extend.max.y = +renderData->extend;
|
||||
}
|
||||
return true;
|
||||
} else
|
||||
// update drop shadow region
|
||||
if (effect->type == SceneEffect::DropShadow) {
|
||||
} else if (effect->type == SceneEffect::DropShadow) {
|
||||
auto dropShadow = (RenderEffectDropShadow*)effect;
|
||||
auto renderData = (WgRenderDataEffectParams*)dropShadow->rd;
|
||||
dropShadow->extend.x = -(renderData->extend + std::abs(renderData->offset.x));
|
||||
dropShadow->extend.w = +(renderData->extend + std::abs(renderData->offset.x)) * 2;
|
||||
dropShadow->extend.y = -(renderData->extend + std::abs(renderData->offset.y));
|
||||
dropShadow->extend.h = +(renderData->extend + std::abs(renderData->offset.y)) * 2;
|
||||
dropShadow->extend.min.x = -(renderData->extend + std::abs(renderData->offset.x));
|
||||
dropShadow->extend.max.x = +(renderData->extend + std::abs(renderData->offset.x));
|
||||
dropShadow->extend.min.y = -(renderData->extend + std::abs(renderData->offset.y));
|
||||
dropShadow->extend.max.y = +(renderData->extend + std::abs(renderData->offset.y));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -605,20 +565,8 @@ bool WgRenderer::region(RenderEffect* effect)
|
|||
|
||||
bool WgRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, TVG_UNUSED bool direct)
|
||||
{
|
||||
// we must to end current render pass to resolve ms texture before effect
|
||||
mCompositor.endRenderPass();
|
||||
WgCompose* comp = (WgCompose*)cmp;
|
||||
WgRenderStorage* dst = mRenderStorageStack.last();
|
||||
|
||||
switch (effect->type) {
|
||||
case SceneEffect::GaussianBlur: return mCompositor.gaussianBlur(mContext, dst, (RenderEffectGaussianBlur*)effect, comp);
|
||||
case SceneEffect::DropShadow: return mCompositor.dropShadow(mContext, dst, (RenderEffectDropShadow*)effect, comp);
|
||||
case SceneEffect::Fill: return mCompositor.fillEffect(mContext, dst, (RenderEffectFill*)effect, comp);
|
||||
case SceneEffect::Tint: return mCompositor.tintEffect(mContext, dst, (RenderEffectTint*)effect, comp);
|
||||
case SceneEffect::Tritone : return mCompositor.tritoneEffect(mContext, dst, (RenderEffectTritone*)effect, comp);
|
||||
default: return false;
|
||||
}
|
||||
return false;
|
||||
mSceneTaskStack.last()->effect = effect;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -632,6 +580,8 @@ void WgRenderer::dispose(RenderEffect* effect)
|
|||
|
||||
bool WgRenderer::preUpdate()
|
||||
{
|
||||
if (mContext.invalid()) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
#ifndef _TVG_WG_RENDERER_H_
|
||||
#define _TVG_WG_RENDERER_H_
|
||||
|
||||
#include "tvgWgCompositor.h"
|
||||
#include "tvgWgRenderTask.h"
|
||||
|
||||
class WgRenderer : public RenderMethod
|
||||
{
|
||||
|
@ -72,13 +72,15 @@ private:
|
|||
bool surfaceConfigure(WGPUSurface surface, WgContext& context, uint32_t width, uint32_t height);
|
||||
|
||||
// render tree stacks
|
||||
WgRenderStorage mRenderStorageRoot;
|
||||
Array<WgCompose*> mCompositorStack;
|
||||
Array<WgRenderStorage*> mRenderStorageStack;
|
||||
WgRenderTarget mRenderTargetRoot;
|
||||
Array<WgCompose*> mCompositorList;
|
||||
Array<WgRenderTarget*> mRenderTargetStack;
|
||||
Array<WgRenderDataViewport*> mRenderDataViewportList;
|
||||
Array<WgSceneTask*> mSceneTaskStack;
|
||||
Array<WgRenderTask*> mRenderTaskList;
|
||||
|
||||
// render storage pool
|
||||
WgRenderStoragePool mRenderStoragePool;
|
||||
// render target pool
|
||||
WgRenderTargetPool mRenderTargetPool;
|
||||
|
||||
// render data paint pools
|
||||
WgRenderDataShapePool mRenderDataShapePool;
|
||||
|
@ -100,7 +102,6 @@ private:
|
|||
Key mDisposeKey{};
|
||||
|
||||
// gpu handles
|
||||
WGPUCommandEncoder mCommandEncoder{};
|
||||
WGPUTexture targetTexture{}; // external handle
|
||||
WGPUSurfaceTexture surfaceTexture{};
|
||||
WGPUSurface surface{}; // external handle
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
#include "tvgWgShaderSrc.h"
|
||||
#include <string>
|
||||
|
||||
#define WG_SHADER_SOURCE(...) #__VA_ARGS__
|
||||
|
||||
//************************************************************************
|
||||
// graphics shader source: stencil
|
||||
//************************************************************************
|
||||
|
@ -32,14 +30,15 @@
|
|||
const char* cShaderSrc_Stencil = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f };
|
||||
struct PaintSettings { transform: mat4x4f };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
@ -56,15 +55,16 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
const char* cShaderSrc_Depth = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f };
|
||||
struct PaintSettings { transform: mat4x4f };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
@group(2) @binding(0) var<uniform> uDepth : f32;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position.z = uDepth;
|
||||
return out;
|
||||
}
|
||||
|
@ -82,23 +82,22 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
const char* cShaderSrc_Solid = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(2) @binding(0) var<uniform> uSolidColor : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
||||
let Sc = uSolidColor;
|
||||
let So = uBlendSettings.a;
|
||||
let Sc = uPaintSettings.color;
|
||||
let So = uPaintSettings.options.a;
|
||||
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
|
||||
}
|
||||
)";
|
||||
|
@ -110,34 +109,32 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
const char* cShaderSrc_Linear = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f };
|
||||
struct GradSettings { settings: vec4f, focal: vec4f };
|
||||
struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
|
||||
|
||||
// uniforms
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
@group(2) @binding(0) var uSamplerGrad : sampler;
|
||||
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
|
||||
@group(2) @binding(2) var<uniform> uSettingGrad : GradSettings;
|
||||
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
||||
let pos = in.vGradCoord.xy;
|
||||
let st = uSettingGrad.settings.xy;
|
||||
let ed = uSettingGrad.settings.zw;
|
||||
let st = uPaintSettings.gradient.coords.xy;
|
||||
let ed = uPaintSettings.gradient.coords.zw;
|
||||
let ba = ed - st;
|
||||
let t = dot(pos - st, ba) / dot(ba, ba);
|
||||
let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5));
|
||||
let So = uBlendSettings.a;
|
||||
let So = uPaintSettings.options.a;
|
||||
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
|
||||
}
|
||||
)";
|
||||
|
@ -149,37 +146,35 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
const char* cShaderSrc_Radial = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f };
|
||||
struct GradSettings { settings: vec4f, focal: vec4f };
|
||||
struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
@group(2) @binding(0) var uSamplerGrad : sampler;
|
||||
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
|
||||
@group(2) @binding(2) var<uniform> uSettingGrad : GradSettings;
|
||||
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
||||
// orignal data
|
||||
let d0 = in.vGradCoord.xy - uSettingGrad.settings.xy;
|
||||
let d1 = uSettingGrad.settings.xy - uSettingGrad.focal.xy;
|
||||
let r0 = uSettingGrad.settings.z;
|
||||
let rd = uSettingGrad.focal.z - uSettingGrad.settings.z;
|
||||
let d0 = in.vGradCoord.xy - uPaintSettings.gradient.coords.xy;
|
||||
let d1 = uPaintSettings.gradient.coords.xy - uPaintSettings.gradient.focal.xy;
|
||||
let r0 = uPaintSettings.gradient.coords.z;
|
||||
let rd = uPaintSettings.gradient.focal.z - uPaintSettings.gradient.coords.z;
|
||||
let a = 1.0*dot(d1, d1) - 1.0*rd*rd;
|
||||
let b = 2.0*dot(d0, d1) - 2.0*r0*rd;
|
||||
let c = 1.0*dot(d0, d0) - 1.0*r0*r0;
|
||||
let t = (-b + sqrt(b*b - 4*a*c))/(2*a);
|
||||
let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(1.0 - t, 0.5));
|
||||
let So = uBlendSettings.a;
|
||||
let So = uPaintSettings.options.a;
|
||||
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
|
||||
}
|
||||
)";
|
||||
|
@ -191,17 +186,17 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
const char* cShaderSrc_Image = R"(
|
||||
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord: vec2f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
@group(2) @binding(0) var uSampler : sampler;
|
||||
@group(2) @binding(1) var uTextureView : texture_2d<f32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vTexCoord = in.texCoord;
|
||||
return out;
|
||||
}
|
||||
|
@ -209,7 +204,7 @@ fn vs_main(in: VertexInput) -> VertexOutput {
|
|||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
||||
var Sc: vec4f = textureSample(uTextureView, uSampler, in.vTexCoord.xy);
|
||||
let So: f32 = uBlendSettings.a;
|
||||
let So: f32 = uPaintSettings.options.a;
|
||||
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
|
||||
};
|
||||
)";
|
||||
|
@ -248,18 +243,18 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
|
|||
const char* cShaderSrc_Solid_Blend = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f, @location(1) vScrCoord: vec2f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(2) @binding(0) var<uniform> uSolidColor : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
// @group(2) - empty
|
||||
@group(3) @binding(0) var uSamplerDst : sampler;
|
||||
@group(3) @binding(1) var uTextureDst : texture_2d<f32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = pos;
|
||||
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
|
||||
return out;
|
||||
|
@ -268,13 +263,13 @@ fn vs_main(in: VertexInput) -> VertexOutput {
|
|||
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
|
||||
fn getFragData(in: VertexOutput) -> FragData {
|
||||
// get source data
|
||||
let colorSrc = uSolidColor;
|
||||
let colorSrc = uPaintSettings.color;
|
||||
let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy);
|
||||
// fill fragment data
|
||||
var data: FragData;
|
||||
data.Sc = colorSrc.rgb;
|
||||
data.Sa = colorSrc.a;
|
||||
data.So = uBlendSettings.a;
|
||||
data.So = uPaintSettings.options.a;
|
||||
data.Dc = colorDst.rgb;
|
||||
data.Da = colorDst.a;
|
||||
data.Sc = data.Sa * data.So * data.Sc;
|
||||
|
@ -288,24 +283,22 @@ fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
|
|||
const char* cShaderSrc_Linear_Blend = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f };
|
||||
struct GradSettings { settings: vec4f, focal: vec4f };
|
||||
struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
@group(2) @binding(0) var uSamplerGrad : sampler;
|
||||
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
|
||||
@group(2) @binding(2) var<uniform> uSettingGrad : vec4f;
|
||||
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
|
||||
@group(3) @binding(0) var uSamplerDst : sampler;
|
||||
@group(3) @binding(1) var uTextureDst : texture_2d<f32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = pos;
|
||||
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
|
||||
return out;
|
||||
}
|
||||
|
@ -314,8 +307,8 @@ struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
|
|||
fn getFragData(in: VertexOutput) -> FragData {
|
||||
// get source data
|
||||
let pos = in.vGradCoord.xy;
|
||||
let st = uSettingGrad.xy;
|
||||
let ed = uSettingGrad.zw;
|
||||
let st = uPaintSettings.gradient.coords.xy;
|
||||
let ed = uPaintSettings.gradient.coords.zw;
|
||||
let ba = ed - st;
|
||||
let t = dot(pos - st, ba) / dot(ba, ba);
|
||||
let colorSrc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5));
|
||||
|
@ -324,7 +317,7 @@ fn getFragData(in: VertexOutput) -> FragData {
|
|||
var data: FragData;
|
||||
data.Sc = colorSrc.rgb;
|
||||
data.Sa = colorSrc.a;
|
||||
data.So = uBlendSettings.a;
|
||||
data.So = uPaintSettings.options.a;
|
||||
data.Dc = colorDst.rgb;
|
||||
data.Da = colorDst.a;
|
||||
data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So);
|
||||
|
@ -338,34 +331,32 @@ fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
|
|||
const char* cShaderSrc_Radial_Blend = R"(
|
||||
struct VertexInput { @location(0) position: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f };
|
||||
struct GradSettings { settings: vec4f, focal: vec4f };
|
||||
struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
@group(2) @binding(0) var uSamplerGrad : sampler;
|
||||
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
|
||||
@group(2) @binding(2) var<uniform> uSettingGrad : GradSettings;
|
||||
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
|
||||
@group(3) @binding(0) var uSamplerDst : sampler;
|
||||
@group(3) @binding(1) var uTextureDst : texture_2d<f32>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = pos;
|
||||
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
|
||||
return out;
|
||||
}
|
||||
|
||||
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
|
||||
fn getFragData(in: VertexOutput) -> FragData {
|
||||
let d0 = in.vGradCoord.xy - uSettingGrad.settings.xy;
|
||||
let d1 = uSettingGrad.settings.xy - uSettingGrad.focal.xy;
|
||||
let r0 = uSettingGrad.settings.z;
|
||||
let rd = uSettingGrad.focal.z - uSettingGrad.settings.z;
|
||||
let d0 = in.vGradCoord.xy - uPaintSettings.gradient.coords.xy;
|
||||
let d1 = uPaintSettings.gradient.coords.xy - uPaintSettings.gradient.focal.xy;
|
||||
let r0 = uPaintSettings.gradient.coords.z;
|
||||
let rd = uPaintSettings.gradient.focal.z - uPaintSettings.gradient.coords.z;
|
||||
let a = 1.0*dot(d1, d1) - 1.0*rd*rd;
|
||||
let b = 2.0*dot(d0, d1) - 2.0*r0*rd;
|
||||
let c = 1.0*dot(d0, d0) - 1.0*r0*r0;
|
||||
|
@ -376,7 +367,7 @@ fn getFragData(in: VertexOutput) -> FragData {
|
|||
var data: FragData;
|
||||
data.Sc = colorSrc.rgb;
|
||||
data.Sa = colorSrc.a;
|
||||
data.So = uBlendSettings.a;
|
||||
data.So = uPaintSettings.options.a;
|
||||
data.Dc = colorDst.rgb;
|
||||
data.Da = colorDst.a;
|
||||
data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So);
|
||||
|
@ -390,10 +381,10 @@ fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
|
|||
const char* cShaderSrc_Image_Blend = R"(
|
||||
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
|
||||
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord : vec2f, @location(1) vScrCoord: vec2f };
|
||||
struct PaintSettings { transform: mat4x4f, options: vec4f };
|
||||
|
||||
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
|
||||
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f;
|
||||
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
|
||||
@group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
|
||||
@group(2) @binding(0) var uSamplerSrc : sampler;
|
||||
@group(2) @binding(1) var uTextureSrc : texture_2d<f32>;
|
||||
@group(3) @binding(0) var uSamplerDst : sampler;
|
||||
|
@ -402,7 +393,7 @@ struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord
|
|||
@vertex
|
||||
fn vs_main(in: VertexInput) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0);
|
||||
let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
|
||||
out.position = pos;
|
||||
out.vTexCoord = in.texCoord;
|
||||
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
|
||||
|
@ -418,7 +409,7 @@ fn getFragData(in: VertexOutput) -> FragData {
|
|||
var data: FragData;
|
||||
data.Sc = colorSrc.rgb;
|
||||
data.Sa = colorSrc.a;
|
||||
data.So = uBlendSettings.a;
|
||||
data.So = uPaintSettings.options.a;
|
||||
data.Dc = colorDst.rgb;
|
||||
data.Da = colorDst.a;
|
||||
data.Sc = data.Sc * data.So;
|
||||
|
|
|
@ -118,42 +118,41 @@ void WgShaderTypeVec4f::update(const RenderColor& c)
|
|||
|
||||
void WgShaderTypeVec4f::update(const RenderRegion& r)
|
||||
{
|
||||
vec[0] = r.x; // left
|
||||
vec[1] = r.y; // top
|
||||
vec[2] = r.x + r.w - 1; // right
|
||||
vec[3] = r.y + r.h - 1; // bottom
|
||||
vec[0] = r.min.x;
|
||||
vec[1] = r.min.y;
|
||||
vec[2] = r.max.x - 1;
|
||||
vec[3] = r.max.y - 1;
|
||||
}
|
||||
|
||||
//************************************************************************
|
||||
// WgShaderTypeGradient
|
||||
// WgShaderTypeGradSettings
|
||||
//************************************************************************
|
||||
|
||||
void WgShaderTypeGradient::update(const LinearGradient* linearGradient)
|
||||
void WgShaderTypeGradSettings::update(const Fill* fill)
|
||||
{
|
||||
// update gradient data
|
||||
assert(fill);
|
||||
// update transform matrix
|
||||
Matrix invTransform;
|
||||
if (inverse(&fill->transform(), &invTransform))
|
||||
transform.update(invTransform);
|
||||
else transform.identity();
|
||||
// update gradient base points
|
||||
if (fill->type() == Type::LinearGradient)
|
||||
((LinearGradient*)fill)->linear(&coords.vec[0], &coords.vec[1], &coords.vec[2], &coords.vec[3]);
|
||||
else if (fill->type() == Type::RadialGradient)
|
||||
((RadialGradient*)fill)->radial(&coords.vec[0], &coords.vec[1], &coords.vec[2], &focal.vec[0], &focal.vec[1], &focal.vec[2]);
|
||||
}
|
||||
|
||||
//************************************************************************
|
||||
// WgShaderTypeGradientData
|
||||
//************************************************************************
|
||||
|
||||
void WgShaderTypeGradientData::update(const Fill* fill)
|
||||
{
|
||||
if (!fill) return;
|
||||
const Fill::ColorStop* stops = nullptr;
|
||||
auto stopCnt = linearGradient->colorStops(&stops);
|
||||
updateTexData(stops, stopCnt);
|
||||
// update base points
|
||||
linearGradient->linear(&settings[0], &settings[1], &settings[2], &settings[3]);
|
||||
};
|
||||
|
||||
|
||||
void WgShaderTypeGradient::update(const RadialGradient* radialGradient)
|
||||
{
|
||||
// update gradient data
|
||||
const Fill::ColorStop* stops = nullptr;
|
||||
auto stopCnt = radialGradient->colorStops(&stops);
|
||||
updateTexData(stops, stopCnt);
|
||||
// update base points
|
||||
radialGradient->radial(&settings[0], &settings[1], &settings[2], &settings[4], &settings[5], &settings[6]);
|
||||
};
|
||||
|
||||
|
||||
void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t stopCnt)
|
||||
{
|
||||
auto stopCnt = fill->colorStops(&stops);
|
||||
if (stopCnt == 0) return;
|
||||
|
||||
static Array<Fill::ColorStop> sstops(stopCnt);
|
||||
sstops.clear();
|
||||
sstops.push(stops[0]);
|
||||
|
@ -167,10 +166,10 @@ void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t
|
|||
uint32_t range_s = 0;
|
||||
uint32_t range_e = uint32_t(sstops[0].offset * (WG_TEXTURE_GRADIENT_SIZE-1));
|
||||
for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) {
|
||||
texData[ti * 4 + 0] = sstops[0].r;
|
||||
texData[ti * 4 + 1] = sstops[0].g;
|
||||
texData[ti * 4 + 2] = sstops[0].b;
|
||||
texData[ti * 4 + 3] = sstops[0].a;
|
||||
data[ti * 4 + 0] = sstops[0].r;
|
||||
data[ti * 4 + 1] = sstops[0].g;
|
||||
data[ti * 4 + 2] = sstops[0].b;
|
||||
data[ti * 4 + 3] = sstops[0].a;
|
||||
}
|
||||
// body
|
||||
for (uint32_t di = 1; di < sstops.count; di++) {
|
||||
|
@ -179,10 +178,10 @@ void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t
|
|||
float delta = 1.0f/(range_e - range_s);
|
||||
for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) {
|
||||
float t = (ti - range_s) * delta;
|
||||
texData[ti * 4 + 0] = tvg::lerp(sstops[di-1].r, sstops[di].r, t);
|
||||
texData[ti * 4 + 1] = tvg::lerp(sstops[di-1].g, sstops[di].g, t);
|
||||
texData[ti * 4 + 2] = tvg::lerp(sstops[di-1].b, sstops[di].b, t);
|
||||
texData[ti * 4 + 3] = tvg::lerp(sstops[di-1].a, sstops[di].a, t);
|
||||
data[ti * 4 + 0] = tvg::lerp(sstops[di-1].r, sstops[di].r, t);
|
||||
data[ti * 4 + 1] = tvg::lerp(sstops[di-1].g, sstops[di].g, t);
|
||||
data[ti * 4 + 2] = tvg::lerp(sstops[di-1].b, sstops[di].b, t);
|
||||
data[ti * 4 + 3] = tvg::lerp(sstops[di-1].a, sstops[di].a, t);
|
||||
}
|
||||
}
|
||||
// tail
|
||||
|
@ -190,10 +189,10 @@ void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t
|
|||
range_s = uint32_t(colorStopLast.offset * (WG_TEXTURE_GRADIENT_SIZE-1));
|
||||
range_e = WG_TEXTURE_GRADIENT_SIZE;
|
||||
for (uint32_t ti = range_s; ti < range_e; ti++) {
|
||||
texData[ti * 4 + 0] = colorStopLast.r;
|
||||
texData[ti * 4 + 1] = colorStopLast.g;
|
||||
texData[ti * 4 + 2] = colorStopLast.b;
|
||||
texData[ti * 4 + 3] = colorStopLast.a;
|
||||
data[ti * 4 + 0] = colorStopLast.r;
|
||||
data[ti * 4 + 1] = colorStopLast.g;
|
||||
data[ti * 4 + 2] = colorStopLast.b;
|
||||
data[ti * 4 + 3] = colorStopLast.a;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,16 +55,44 @@ struct WgShaderTypeVec4f
|
|||
void update(const RenderRegion& r);
|
||||
};
|
||||
|
||||
// sampler, texture, vec4f
|
||||
#define WG_TEXTURE_GRADIENT_SIZE 512
|
||||
struct WgShaderTypeGradient
|
||||
// WGSL: struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
|
||||
struct WgShaderTypeGradSettings
|
||||
{
|
||||
float settings[4+4]{}; // WGSL: struct GradSettings { settings: vec4f, focal: vec4f; transform: mat4f };
|
||||
uint8_t texData[WG_TEXTURE_GRADIENT_SIZE * 4];
|
||||
// gradient transform matrix
|
||||
WgShaderTypeMat4x4f transform;
|
||||
// linear: [0] - x1, [1] - y1, [2] - x2, [3] - y2
|
||||
// radial: [0] - cx, [1] - cy, [2] - cr
|
||||
WgShaderTypeVec4f coords;
|
||||
// radial: [0] - fx, [1] - fy, [2] - fr
|
||||
WgShaderTypeVec4f focal;
|
||||
|
||||
void update(const LinearGradient* linearGradient);
|
||||
void update(const RadialGradient* radialGradient);
|
||||
void updateTexData(const Fill::ColorStop* stops, uint32_t stopCnt);
|
||||
void update(const Fill* fill);
|
||||
};
|
||||
|
||||
// WGSL: struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
|
||||
struct WgShaderTypePaintSettings
|
||||
{
|
||||
// paint transform matrix (must be at offset 0)
|
||||
WgShaderTypeMat4x4f transform;
|
||||
// [0] - color space, [3] - opacity
|
||||
WgShaderTypeVec4f options;
|
||||
// solid color
|
||||
WgShaderTypeVec4f color;
|
||||
// gradient settings (linear/radial)
|
||||
WgShaderTypeGradSettings gradient;
|
||||
// align to 256 bytes (see webgpu spec: minUniformBufferOffsetAlignment)
|
||||
uint8_t _padding[256 - sizeof(transform) - sizeof(options) - sizeof(color) - sizeof(gradient)];
|
||||
};
|
||||
// see webgpu spec: 3.6.2. Limits - minUniformBufferOffsetAlignment (256)
|
||||
static_assert(sizeof(WgShaderTypePaintSettings) == 256, "Uniform shader data type size must be aligned to 256 bytes");
|
||||
|
||||
// gradient color map
|
||||
#define WG_TEXTURE_GRADIENT_SIZE 512
|
||||
struct WgShaderTypeGradientData
|
||||
{
|
||||
uint8_t data[WG_TEXTURE_GRADIENT_SIZE * 4];
|
||||
|
||||
void update(const Fill* fill);
|
||||
};
|
||||
|
||||
// gaussian params: sigma, scale, extend
|
||||
|
|
|
@ -121,6 +121,9 @@ TEST_CASE("Bounding Box", "[tvgPaint]")
|
|||
Initializer::init(0);
|
||||
|
||||
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
|
||||
uint32_t buffer[100*100];
|
||||
canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888);
|
||||
|
||||
auto shape = Shape::gen();
|
||||
canvas->push(shape);
|
||||
canvas->sync();
|
||||
|
@ -220,7 +223,9 @@ TEST_CASE("Composition", "[tvgPaint]")
|
|||
|
||||
//Clipping
|
||||
auto comp = Shape::gen();
|
||||
REQUIRE(shape->clip() == nullptr);
|
||||
REQUIRE(shape->clip(comp) == Result::Success);
|
||||
REQUIRE(shape->clip() == comp);
|
||||
|
||||
//AlphaMask
|
||||
comp = Shape::gen();
|
||||
|
|
|
@ -80,7 +80,8 @@ TEST_CASE("Scene Clear And Reuse Shape", "[tvgScene]")
|
|||
REQUIRE(Initializer::init(0) == Result::Success);
|
||||
{
|
||||
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
|
||||
REQUIRE(canvas);
|
||||
uint32_t buffer[100*100];
|
||||
canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888);
|
||||
|
||||
auto scene = Scene::gen();
|
||||
REQUIRE(scene);
|
||||
|
|
|
@ -120,6 +120,8 @@ TEST_CASE("Text Basic", "[tvgText]")
|
|||
Initializer::init(0);
|
||||
|
||||
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
|
||||
uint32_t buffer[100*100];
|
||||
canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888);
|
||||
|
||||
auto text = Text::gen();
|
||||
REQUIRE(text);
|
||||
|
@ -144,6 +146,8 @@ TEST_CASE("Text with composite glyphs", "[tvgText]")
|
|||
Initializer::init(0);
|
||||
|
||||
auto canvas = unique_ptr<SwCanvas>(SwCanvas::gen());
|
||||
uint32_t buffer[100*100];
|
||||
canvas->target(buffer, 100, 100, 100, ColorSpace::ABGR8888);
|
||||
|
||||
auto text = Text::gen();
|
||||
REQUIRE(text);
|
||||
|
|
|
@ -54,7 +54,7 @@ private:
|
|||
|
||||
void helpMsg()
|
||||
{
|
||||
cout << "Usage: \n tvg-lotie2gif [Lottie file] or [Lottie folder] [-r resolution] [-f fps] [-b background color]\n\nExamples: \n $ tvg-lotie2gif input.json\n $ tvg-lotie2gif input.json -r 600x600\n $ tvg-lotie2gif input.json -f 30\n $ tvg-lotie2gif input.json -r 600x600 -f 30\n $ tvg-lotie2gif lottiefolder\n $ tvg-lotie2gif lottiefolder -r 600x600 -f 30 -b fa7410\n\n";
|
||||
cout << "Usage: \n tvg-lottie2gif [Lottie file] or [Lottie folder] [-r resolution] [-f fps] [-b background color]\n\nExamples: \n $ tvg-lottie2gif input.json\n $ tvg-lottie2gif input.json -r 600x600\n $ tvg-lottie2gif input.json -f 30\n $ tvg-lottie2gif input.json -r 600x600 -f 30\n $ tvg-lottie2gif lottiefolder\n $ tvg-lottie2gif lottiefolder -r 600x600 -f 30 -b fa7410\n\n";
|
||||
}
|
||||
|
||||
bool validate(string& lottieName)
|
||||
|
|