Compare commits

...

19 commits

Author SHA1 Message Date
Jinny You
dfc7d268a1 infra(ios): Set cpu family to aarch64 for meson compatibility
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
Use meson's official cpu_family name 'aarch64' instead of 'arm64' for ios build.
2025-06-08 11:54:11 +09:00
Hermet Park
eb76769dcf jpg: removed setjmp usage which is not portable with rust 2025-06-08 11:54:08 +09:00
Hermet Park
0fa5d41c8d doc: replaced svg sample shot
Some checks failed
Android / build_x86_64 (push) Has been cancelled
Android / build_aarch64 (push) Has been cancelled
iOS / build_x86_64 (push) Has been cancelled
iOS / build_arm64 (push) Has been cancelled
macOS / build (push) Has been cancelled
macOS / compact_test (push) Has been cancelled
macOS / unit_test (push) Has been cancelled
Ubuntu / build (push) Has been cancelled
Ubuntu / compact_test (push) Has been cancelled
Ubuntu / unit_test (push) Has been cancelled
Windows / build (push) Has been cancelled
Windows / compact_test (push) Has been cancelled
Windows / unit_test (push) Has been cancelled
issue: https://github.com/thorvg/thorvg/issues/3499
2025-06-03 11:32:07 +09:00
Hermet Park
92070fe0de examples: replaced the Lenna with free images
https://www.pexels.com/photo/lovebirds-cuddling-on-a-wooden-branch-30518529/
https://www.pexels.com/@hardeep/

issue: https://github.com/thorvg/thorvg/issues/3499
2025-06-03 11:26:00 +09:00
Hermet Park
6fd7b87754 sw_engine: clean code++
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
2025-06-03 00:58:29 +09:00
Hermet Park
eeebfbb654 sw_engine: hotfix++
resolved a memory violation introduced by:
e2909dd6a4
2025-06-02 22:40:43 +09:00
Hermet Park
dc59440744
Update CODEOWNERS
Some checks failed
Android / build_x86_64 (push) Has been cancelled
Android / build_aarch64 (push) Has been cancelled
iOS / build_x86_64 (push) Has been cancelled
iOS / build_arm64 (push) Has been cancelled
macOS / build (push) Has been cancelled
macOS / compact_test (push) Has been cancelled
macOS / unit_test (push) Has been cancelled
Ubuntu / build (push) Has been cancelled
Ubuntu / compact_test (push) Has been cancelled
Ubuntu / unit_test (push) Has been cancelled
Windows / build (push) Has been cancelled
Windows / compact_test (push) Has been cancelled
Windows / unit_test (push) Has been cancelled
Removed inactive maintaining parts.
2025-05-31 01:24:36 +09:00
Sergii Liebodkin
24509b0e41 wg_engine: geometry stage buffers implementation
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
Implemented task-based rendering and geometry stage buffers:
1. Get information about current frame objects
2. Accumulate geometry data into a stage buffer during frame rendering
3. Flush it to the GPU in single call
4. Run rendering process in post render stage

https://github.com/thorvg/thorvg/issues/3489
https://github.com/thorvg/thorvg/issues/3455
2025-05-30 02:21:31 +09:00
Hermet Park
1f53f2d72f capi: code refactoring
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
begin with a positive path for optimal instruction flow
at the machine code level.
2025-05-29 15:30:59 +09:00
Hermet Park
2eb2b83bb0 api: revise the clip() apis.
- Paint explicity allows a shape as a clipper
- Added clipper getter apis

API Updates
 + Shape* Paint::clip()
 * Result Paint::clip(Paint* clipper) -> Result Paint::clip(Shape* clipper)

CAPI Updates
 + Tvg_Paint* tvg_paint_get_clip(const Tvg_Paint* paint)
 * Tvg_Result tvg_paint_clip(Tvg_Paint* paint, Tvg_Paint* clipper)
   -> Tvg_Result tvg_paint_set_clip(Tvg_Paint* paint, Tvg_Paint* clipper);

Please note that clipper type can be changed again to tvg::Path
in the upcoming update.
2025-05-29 14:02:32 +09:00
Hermet Park
e2909dd6a4 sw_engine: replace RLE memory with common array
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
This commit has two purposes:

- refactoring to introduce y indexing method for the upcoming partial rendering.
- replaces the RLE-specific memory allocation with a shared array structure,
eliminating potential memory overflows during RLE clipping.
2025-05-28 23:17:18 +09:00
Sergii Liebodkin
d0be8cd2bd gl_engine: fix compilation warning with RenderRegion data type 2025-05-28 19:20:47 +09:00
Hermet Park
dc8c5bce50 sw_engine: code refactoring
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
just renamed internal variables (region -> bbox)
for the sake of a shorter name, no logical changes.
2025-05-28 11:41:52 +09:00
Hermet Park
cc72eda465 sw_engine: unify RenderRegion and SwBBox
refactored for smoother data flow through the rendering pipeline.
2025-05-28 11:41:52 +09:00
Hermet Park
8a35f02105 renderer/engines: redesigned RenderRegion property layout
redefiend properties so that min/max are prioritized,
as they are accessed more frequently than pos/size
during rendering calculations.

also introduced miscellaneous functions to improve usability.
2025-05-28 11:41:52 +09:00
Hermet Park
04b7bb4f25 renderer: allow empty content during canvas rendering
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
This allow more generous handling of failure cases for invalid content,
thorvg can draw other successful contents.

Now, only raster engines can return fail cases in the picture.
2025-05-28 00:48:48 +09:00
Hermet Park
b221eed7fa renderer: chores 2025-05-27 17:58:10 +09:00
Mira Grudzinska
211fee73e2 lottie: fix precomposition with masking
Some checks failed
Android / build_x86_64 (push) Has been cancelled
Android / build_aarch64 (push) Has been cancelled
iOS / build_x86_64 (push) Has been cancelled
iOS / build_arm64 (push) Has been cancelled
macOS / build (push) Has been cancelled
macOS / compact_test (push) Has been cancelled
macOS / unit_test (push) Has been cancelled
Ubuntu / build (push) Has been cancelled
Ubuntu / compact_test (push) Has been cancelled
Ubuntu / unit_test (push) Has been cancelled
Windows / build (push) Has been cancelled
Windows / compact_test (push) Has been cancelled
Windows / unit_test (push) Has been cancelled
A precomposition layer is clipped to its viewport.
If the same layer also had a mask that was optimized
using clipping, this clip was unintentionally overridden
by the viewport clipping. This conflict is now fixed.
2025-05-26 19:26:45 +09:00
Sergii Liebodkin
55847bdcb3 gl_engine: fix memoty leak on target resize
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
We must to remove offscreen render buffers during removing render target

https://github.com/thorvg/thorvg/issues/3210
2025-05-25 23:04:12 +09:00
65 changed files with 2125 additions and 1819 deletions

View file

@ -4,14 +4,8 @@
* @hermet * @hermet
/src/renderer/sw_engine @mgrudzinska /src/renderer/sw_engine @mgrudzinska
/src/renderer/gl_engine @RuiwenTang @SergeyLebedkin /src/renderer/gl_engine @SergeyLebedkin @RuiwenTang
/src/renderer/wg_engine @SergeyLebedkin /src/renderer/wg_engine @SergeyLebedkin
/src/loaders/external_webp @JSUYA /src/loaders/svg @mgrudzinska @JSUYA
/src/loaders/raw @JSUYA
/src/loaders/svg @JSUYA @mgrudzinska
/src/loaders/webp @JSUYA
/src/loaders/lottie @mgrudzinska /src/loaders/lottie @mgrudzinska
/src/bindings/capi @mgrudzinska
/src/bindings/wasm @tinyjin /src/bindings/wasm @tinyjin
/src/savers/gif @JSUYA
/src/tools/svg2png @JSUYA

View file

@ -295,7 +295,7 @@ ThorVG facilitates [SVG Tiny Specification](https://www.w3.org/TR/SVGTiny12/) re
The figure below highlights ThorVG's SVG rendering capabilities: The figure below highlights ThorVG's SVG rendering capabilities:
<p align="center"> <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> </p>
The following code snippet shows how to draw SVG image using ThorVG: The following code snippet shows how to draw SVG image using ThorVG:

View file

@ -17,6 +17,6 @@ cpp_link_args = ['-miphoneos-version-min=11.0']
system = 'darwin' system = 'darwin'
subsystem = 'ios' subsystem = 'ios'
kernel = 'xnu' kernel = 'xnu'
cpu_family = 'arm64' cpu_family = 'aarch64'
cpu = 'arm64' cpu = 'aarch64'
endian = 'little' endian = 'little'

View file

@ -65,7 +65,7 @@ struct UserExample : tvgexam::Example
auto picture = tvg::Picture::gen(); auto picture = tvg::Picture::gen();
if (!tvgexam::verify(picture->load(data, size, "png", "", true))) return false; if (!tvgexam::verify(picture->load(data, size, "png", "", true))) return false;
free(data); free(data);
picture->translate(400, 0); picture->translate(380, 0);
picture->scale(0.8); picture->scale(0.8);
canvas->push(picture); canvas->push(picture);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 KiB

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

After

Width:  |  Height:  |  Size: 58 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 533 KiB

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 617 KiB

After

Width:  |  Height:  |  Size: 192 KiB

View file

@ -65,6 +65,7 @@ namespace tvg
class RenderMethod; class RenderMethod;
class Animation; class Animation;
class Shape;
/** /**
* @defgroup ThorVG ThorVG * @defgroup ThorVG ThorVG
@ -396,13 +397,13 @@ public:
* *
* @param[in] clipper The shape object as the clipper. * @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 @p clipper has already belonged to another paint.
* @retval Result::InsufficientCondition if the target has already belonged to another paint. *
* @see Paint::clip()
* *
* @note @p clipper only supports the Shape type.
* @since 1.0 * @since 1.0
*/ */
Result clip(Paint* clipper) noexcept; Result clip(Shape* clipper) noexcept;
/** /**
* @brief Sets the blending method for the paint object. * @brief Sets the blending method for the paint object.
@ -478,6 +479,19 @@ public:
*/ */
MaskMethod mask(const Paint** target) const noexcept; 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. * @brief Increment the reference count for the Paint instance.
* *

BIN
res/example_svg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 818 KiB

View file

@ -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); 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. * @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. * 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_INSUFFICIENT_CONDITION if the target has already belonged to another paint.
* @retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape. * @retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape.
* *
* @see tvg_paint_get_clip()
* @since 1.0 * @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. * @brief Retrieves the parent paint object.

File diff suppressed because it is too large Load diff

View file

@ -102,6 +102,17 @@ struct Array
count = 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 const T* begin() const
{ {
return data; return data;
@ -137,6 +148,12 @@ struct Array
return data[count - 1]; return data[count - 1];
} }
T& next()
{
if (full()) grow(count + 1);
return data[count++];
}
T& first() T& first()
{ {
return data[0]; return data[0];
@ -164,6 +181,11 @@ struct Array
return count == 0; return count == 0;
} }
bool full()
{
return count == reserved;
}
template<class COMPARE> void sort() template<class COMPARE> void sort()
{ {
qsort<COMPARE>(data, 0, (int32_t)(count - 1)); qsort<COMPARE>(data, 0, (int32_t)(count - 1));

View file

@ -33,21 +33,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <setjmp.h>
#include <stdint.h> #include <stdint.h>
#include "tvgCommon.h" #include "tvgCommon.h"
#include "tvgJpgd.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 */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
@ -171,7 +161,7 @@ private:
jpeg_decoder(const jpeg_decoder &); jpeg_decoder(const jpeg_decoder &);
jpeg_decoder &operator =(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 struct huff_tables
{ {
@ -198,7 +188,6 @@ private:
char m_data[1]; char m_data[1];
}; };
jmp_buf m_jmp_state;
mem_block *m_pMem_blocks; mem_block *m_pMem_blocks;
int m_image_x_size; int m_image_x_size;
int m_image_y_size; int m_image_y_size;
@ -274,21 +263,21 @@ private:
int m_total_bytes_read; int m_total_bytes_read;
void free_all_blocks(); 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 *alloc(size_t n, bool zero = false);
void word_clear(void *p, uint16_t c, uint32_t n); void word_clear(void *p, uint16_t c, uint32_t n);
void prep_in_buffer(); bool prep_in_buffer();
void read_dht_marker(); bool read_dht_marker();
void read_dqt_marker(); bool read_dqt_marker();
void read_sof_marker(); bool read_sof_marker();
void skip_variable_marker(); bool skip_variable_marker();
void read_dri_marker(); bool read_dri_marker();
void read_sos_marker(); bool read_sos_marker();
int next_marker(); int next_marker();
int process_markers(); int process_markers();
void locate_soi_marker(); bool locate_soi_marker();
void locate_sof_marker(); bool locate_sof_marker();
int locate_sos_marker(); bool locate_sos_marker();
void init(jpeg_decoder_stream * pStream); void init(jpeg_decoder_stream * pStream);
void create_look_ups(); void create_look_ups();
void fix_in_buffer(); 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); 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); inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
void load_next_row(); void load_next_row();
void decode_next_row(); bool decode_next_row();
void make_huff_table(int index, huff_tables *pH); void make_huff_table(int index, huff_tables *pH);
void check_quant_tables(); bool check_quant_tables();
void check_huff_tables(); bool check_huff_tables();
void calc_mcu_block_order(); void calc_mcu_block_order();
int init_scan(); int init_scan();
void init_frame(); bool init_frame();
void process_restart(); bool process_restart();
void decode_scan(pDecode_block_func decode_block_func); bool decode_scan(pDecode_block_func decode_block_func);
void init_progressive(); bool init_progressive();
void init_sequential(); bool init_sequential();
void decode_start(); bool decode_start();
void decode_init(jpeg_decoder_stream * pStream); void decode_init(jpeg_decoder_stream * pStream);
void H2V2Convert(); void H2V2Convert();
void H2V1Convert(); void H2V1Convert();
@ -326,10 +315,10 @@ private:
inline int huff_decode(huff_tables *pH); inline int huff_decode(huff_tables *pH);
inline int huff_decode(huff_tables *pH, int& extrabits); inline int huff_decode(huff_tables *pH, int& extrabits);
static inline uint8_t clamp(int i); 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 bool 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 bool 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 bool 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_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? // Any bytes remaining in buffer?
if (!m_in_buf_left) { if (!m_in_buf_left) {
// Try to get more bytes. // Try to get more bytes.
prep_in_buffer(); if (!prep_in_buffer()) return 0xFF;
// Still nothing to get? // Still nothing to get?
if (!m_in_buf_left) { if (!m_in_buf_left) {
// Pad the end of the stream with 0xFF 0xD9 (EOI marker) // 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) inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag)
{ {
if (!m_in_buf_left) { if (!m_in_buf_left) {
prep_in_buffer(); if (!prep_in_buffer()) return 0xFF;
if (!m_in_buf_left) { if (!m_in_buf_left) {
*pPadding_flag = true; *pPadding_flag = true;
int t = m_tem_flag; int t = m_tem_flag;
@ -1093,13 +1082,15 @@ void jpeg_decoder::free_all_blocks()
} }
// This method handles all errors. It will never return. bool jpeg_decoder::stop_decoding(jpgd_status status)
// It could easily be changed to use C++ exceptions.
JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status)
{ {
#if 0 //for debugging
m_error_code = status; m_error_code = status;
#else
m_error_code = JPGD_FAILED;
#endif
free_all_blocks(); 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) { if (!rv) {
int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047); int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
mem_block *b = tvg::malloc<mem_block*>(sizeof(mem_block) + capacity); auto b = tvg::malloc<mem_block*>(sizeof(mem_block) + capacity);
if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
b->m_pNext = m_pMem_blocks; m_pMem_blocks = b; b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
b->m_used_count = nSize; b->m_used_count = nSize;
b->m_size = capacity; 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. // Refill the input buffer.
// This method will sit in a loop until (A) the buffer is full or (B) // 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. // 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_in_buf_left = 0;
m_pIn_buf_ofs = m_in_buf; m_pIn_buf_ofs = m_in_buf;
if (m_eof_flag) return true;
if (m_eof_flag) return;
do { 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); 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) stop_decoding(JPGD_STREAM_READ); if (bytes_read == -1) return stop_decoding(JPGD_STREAM_READ);
m_in_buf_left += bytes_read; m_in_buf_left += bytes_read;
} while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag)); } 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). // 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.) // (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); word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
return true;
} }
// Read a Huffman code table. // Read a Huffman code table.
void jpeg_decoder::read_dht_marker() bool jpeg_decoder::read_dht_marker()
{ {
int i, index, count; int i, index, count;
uint8_t huff_num[17]; uint8_t huff_num[17];
uint8_t huff_val[256]; uint8_t huff_val[256];
uint32_t num_left = get_bits(16); 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; num_left -= 2;
while (num_left) { while (num_left) {
@ -1184,19 +1174,19 @@ void jpeg_decoder::read_dht_marker()
count += huff_num[i]; 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++) for (i = 0; i < count; i++)
huff_val[i] = static_cast<uint8_t>(get_bits(8)); huff_val[i] = static_cast<uint8_t>(get_bits(8));
i = 1 + 16 + count; 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; 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); 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_num[index]) m_huff_num[index] = (uint8_t *)alloc(17);
if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256); 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_num[index], huff_num, 17);
memcpy(m_huff_val[index], huff_val, 256); memcpy(m_huff_val[index], huff_val, 256);
} }
return true;
} }
// Read a quantization table. // Read a quantization table.
void jpeg_decoder::read_dqt_marker() bool jpeg_decoder::read_dqt_marker()
{ {
int n, i, prec; int n, i, prec;
uint32_t temp; uint32_t temp;
uint32_t num_left = get_bits(16); 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; num_left -= 2;
while (num_left) { while (num_left) {
@ -1222,7 +1213,7 @@ void jpeg_decoder::read_dqt_marker()
prec = n >> 4; prec = n >> 4;
n &= 0x0F; 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)); 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; i = 64 + 1;
if (prec) i += 64; 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; num_left -= i;
} }
return true;
} }
// Read the start of frame (SOF) marker. // Read the start of frame (SOF) marker.
void jpeg_decoder::read_sof_marker() bool jpeg_decoder::read_sof_marker()
{ {
int i; int i;
uint32_t num_left = get_bits(16); 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); 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); 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); 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++) { for (i = 0; i < m_comps_in_frame; i++) {
m_comp_ident[i] = get_bits(8); 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_v_samp[i] = get_bits(4);
m_comp_quant[i] = get_bits(8); m_comp_quant[i] = get_bits(8);
} }
return true;
} }
// Used to skip unrecognized markers. // Used to skip unrecognized markers.
void jpeg_decoder::skip_variable_marker() bool jpeg_decoder::skip_variable_marker()
{ {
uint32_t num_left = get_bits(16); 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; num_left -= 2;
while (num_left) { while (num_left) {
get_bits(8); get_bits(8);
num_left--; num_left--;
} }
return true;
} }
// Read a define restart interval (DRI) marker. // 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); m_restart_interval = get_bits(16);
return true;
} }
// Read a start of scan (SOS) marker. // Read a start of scan (SOS) marker.
void jpeg_decoder::read_sos_marker() bool jpeg_decoder::read_sos_marker()
{ {
int i, ci, c, cc; int i, ci, c, cc;
uint32_t num_left = get_bits(16); uint32_t num_left = get_bits(16);
@ -1300,7 +1295,7 @@ void jpeg_decoder::read_sos_marker()
m_comps_in_scan = n; m_comps_in_scan = n;
num_left -= 3; 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++) { for (i = 0; i < n; i++) {
cc = get_bits(8); cc = get_bits(8);
@ -1310,7 +1305,7 @@ void jpeg_decoder::read_sos_marker()
for (ci = 0; ci < m_comps_in_frame; ci++) for (ci = 0; ci < m_comps_in_frame; ci++)
if (cc == m_comp_ident[ci]) break; 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_list[i] = ci;
m_comp_dc_tab[ci] = (c >> 4) & 15; m_comp_dc_tab[ci] = (c >> 4) & 15;
@ -1331,6 +1326,7 @@ void jpeg_decoder::read_sos_marker()
get_bits(8); get_bits(8);
num_left--; num_left--;
} }
return true;
} }
@ -1380,21 +1376,21 @@ int jpeg_decoder::process_markers()
case M_EOI: case M_EOI:
case M_SOS: return c; case M_SOS: return c;
case M_DHT: { case M_DHT: {
read_dht_marker(); if (read_dht_marker()) break;
break; else return M_EOI;
} }
// No arithmetic support - dumb patents! // No arithmetic support - dumb patents!
case M_DAC: { case M_DAC: {
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT); stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
break; return M_EOI;
} }
case M_DQT: { case M_DQT: {
read_dqt_marker(); if (read_dqt_marker()) break;
break; else return M_EOI;
} }
case M_DRI: { case M_DRI: {
read_dri_marker(); if (read_dri_marker()) break;
break; else return M_EOI;
} }
//case M_APP0: /* no need to read the JFIF marker */ //case M_APP0: /* no need to read the JFIF marker */
case M_JPG: case M_JPG:
@ -1408,11 +1404,11 @@ int jpeg_decoder::process_markers()
case M_RST7: case M_RST7:
case M_TEM: { case M_TEM: {
stop_decoding(JPGD_UNEXPECTED_MARKER); stop_decoding(JPGD_UNEXPECTED_MARKER);
break; return M_EOI;
} }
default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */ default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
skip_variable_marker(); if (skip_variable_marker()) break;
break; else return M_EOI;
} }
} }
} }
@ -1422,71 +1418,62 @@ int jpeg_decoder::process_markers()
// Finds the start of image (SOI) marker. // Finds the start of image (SOI) marker.
// This code is rather defensive: it only checks the first 512 bytes to avoid // This code is rather defensive: it only checks the first 512 bytes to avoid
// false positives. // false positives.
void jpeg_decoder::locate_soi_marker() bool jpeg_decoder::locate_soi_marker()
{ {
uint32_t lastchar = get_bits(8); uint32_t lastchar = get_bits(8);
uint32_t thischar = get_bits(8); uint32_t thischar = get_bits(8);
/* ok if it's a normal JPEG file without a special header */ /* 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; uint32_t bytesleft = 4096; //512;
while (true) { while (true) {
if (--bytesleft == 0) stop_decoding(JPGD_NOT_JPEG); if (--bytesleft == 0) return stop_decoding(JPGD_NOT_JPEG);
lastchar = thischar; lastchar = thischar;
thischar = get_bits(8); thischar = get_bits(8);
if (lastchar == 0xFF) { if (lastchar == 0xFF) {
if (thischar == M_SOI) break; 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. // 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; 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. // Find a start of frame (SOF) marker.
void jpeg_decoder::locate_sof_marker() bool jpeg_decoder::locate_sof_marker()
{ {
locate_soi_marker(); if (!locate_soi_marker()) return false;
int c = process_markers(); switch (process_markers()) {
switch (c) {
case M_SOF2: { case M_SOF2: {
m_progressive_flag = true; m_progressive_flag = true;
read_sof_marker(); return read_sof_marker();
break;
} }
case M_SOF0: /* baseline DCT */ case M_SOF0: /* baseline DCT */
case M_SOF1: { /* extended sequential DCT */ case M_SOF1: { /* extended sequential DCT */
read_sof_marker(); return read_sof_marker();
break;
}
case M_SOF9: { /* Arithmetic coding */
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
break;
}
default: {
stop_decoding(JPGD_UNSUPPORTED_MARKER);
break;
} }
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. // 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; if (c == M_EOI) return false;
else if (c != M_SOS) stop_decoding(JPGD_UNEXPECTED_MARKER); else if (c != M_SOS) return stop_decoding(JPGD_UNEXPECTED_MARKER);
read_sos_marker(); return read_sos_marker();
return true;
} }
@ -1828,7 +1815,7 @@ void jpeg_decoder::load_next_row()
// Restart interval processing. // Restart interval processing.
void jpeg_decoder::process_restart() bool jpeg_decoder::process_restart()
{ {
int i; int i;
int c = 0; int c = 0;
@ -1842,15 +1829,15 @@ void jpeg_decoder::process_restart()
for (i = 1536; i > 0; i--) { for (i = 1536; i > 0; i--) {
if (get_char() == 0xFF) break; 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--) { for ( ; i > 0; i--) {
if ((c = get_char()) != 0xFF) break; 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. // 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. // Reset each component's DC prediction values.
memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t)); 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; m_bits_left = 16;
get_bits_no_markers(16); get_bits_no_markers(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. // 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++) { 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; jpgd_block_t* p = m_pMCU_coefficients;
@ -1903,7 +1894,7 @@ void jpeg_decoder::decode_next_row()
if (s) { if (s) {
if (r) { 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) { if (k < prev_num_set) {
int n = JPGD_MIN(r, prev_num_set - k); int n = JPGD_MIN(r, prev_num_set - k);
int kt = 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]; p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
} else { } else {
if (r == 15) { 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) { if (k < prev_num_set) {
int n = JPGD_MIN(16, prev_num_set - k); int n = JPGD_MIN(16, prev_num_set - k);
int kt = k; int kt = k;
@ -1942,6 +1933,7 @@ void jpeg_decoder::decode_next_row()
else transform_mcu(mcu_row); else transform_mcu(mcu_row);
m_restarts_left--; 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_error_code) || (!m_ready_flag)) return JPGD_FAILED;
if (m_total_lines_left == 0) return JPGD_DONE; if (m_total_lines_left == 0) return JPGD_DONE;
if (m_mcu_lines_left == 0) { if (m_mcu_lines_left == 0) {
if (setjmp(m_jmp_state)) return JPGD_FAILED;
if (m_progressive_flag) load_next_row(); 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. // Find the EOI marker if that was the last row.
if (m_total_lines_left <= m_max_mcu_y_size) find_eoi(); if (m_total_lines_left <= m_max_mcu_y_size) find_eoi();
m_mcu_lines_left = m_max_mcu_y_size; 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. // 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++) { 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. // 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++) { 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_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]]] == nullptr)) 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++) { 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]); make_huff_table(i, m_pHuff_tabs[i]);
} }
} }
return true;
} }
@ -2418,7 +2411,7 @@ int jpeg_decoder::init_scan()
calc_mcu_block_order(); calc_mcu_block_order();
check_huff_tables(); 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)); 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 // Starts a frame. Determines if the number of components or sampling factors
// are supported. // are supported.
void jpeg_decoder::init_frame() bool jpeg_decoder::init_frame()
{ {
int i; int i;
if (m_comps_in_frame == 1) { 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_scan_type = JPGD_GRAYSCALE;
m_max_blocks_per_mcu = 1; m_max_blocks_per_mcu = 1;
m_max_mcu_x_size = 8; m_max_mcu_x_size = 8;
m_max_mcu_y_size = 8; m_max_mcu_y_size = 8;
} else if (m_comps_in_frame == 3) { } 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))) 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); return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
}
if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) { if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) {
m_scan_type = JPGD_YH1V1; m_scan_type = JPGD_YH1V1;
@ -2469,8 +2463,8 @@ void jpeg_decoder::init_frame()
m_max_blocks_per_mcu = 6; m_max_blocks_per_mcu = 6;
m_max_mcu_x_size = 16; m_max_mcu_x_size = 16;
m_max_mcu_y_size = 16; m_max_mcu_y_size = 16;
} else stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS); } else return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
} else stop_decoding(JPGD_UNSUPPORTED_COLORSPACE); } 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_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; 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; m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
// Should never happen // 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 // 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)); 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_total_lines_left = m_image_y_size;
m_mcu_lines_left = 0; m_mcu_lines_left = 0;
create_look_ups(); 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 // The following methods decode the various types of m_blocks encountered
// in progressively encoded images. // 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; int s, r;
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y); 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]); 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); 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)) { 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); 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); 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; int k, s, r;
if (pD->m_eob_run) { if (pD->m_eob_run) {
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); 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; r = s >> 4;
s &= 15; s &= 15;
if (s) { 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); r = pD->get_bits_no_markers(s);
s = JPGD_HUFF_EXTEND(r, 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); p[g_ZAG[k]] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
} else { } else {
if (r == 15) { 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 { } else {
pD->m_eob_run = 1 << r; pD->m_eob_run = 1 << r;
if (r) pD->m_eob_run += pD->get_bits_no_markers(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 s, k, r;
int p1 = 1 << pD->m_successive_low; 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; r = s >> 4;
s &= 15; s &= 15;
if (s) { 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; if (pD->get_bits_no_markers(1)) s = p1;
else s = m1; else s = m1;
} else { } else {
@ -2667,11 +2667,12 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
} }
pD->m_eob_run--; pD->m_eob_run--;
} }
return true;
} }
// Decode a scan in a progressively encoded image. // 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 mcu_row, mcu_col, mcu_block;
int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS]; 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++) { for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0; 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++) { for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
component_id = m_mcu_org[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]++; if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
else { else {
@ -2714,15 +2717,16 @@ void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
} }
} }
} }
return true;
} }
// Decode a progressively encoded image. // Decode a progressively encoded image.
void jpeg_decoder::init_progressive() bool jpeg_decoder::init_progressive()
{ {
int i; 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. // Allocate the coefficient buffers.
for (i = 0; i < m_comps_in_frame; i++) { 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); dc_only_scan = (m_spectral_start == 0);
refinement_scan = (m_successive_high != 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 (dc_only_scan) {
if (m_spectral_end) stop_decoding(JPGD_BAD_SOS_SPECTRAL); if (m_spectral_end) return stop_decoding(JPGD_BAD_SOS_SPECTRAL);
} else if (m_comps_in_scan != 1) { /* AC scans can only contain one component */ } else if (m_comps_in_scan != 1) return stop_decoding(JPGD_BAD_SOS_SPECTRAL); // AC scans can only contain one component
stop_decoding(JPGD_BAD_SOS_SPECTRAL);
}
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 (dc_only_scan) {
if (refinement_scan) decode_block_func = decode_block_dc_refine; 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; if (refinement_scan) decode_block_func = decode_block_ac_refine;
else decode_block_func = decode_block_ac_first; 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; m_bits_left = 16;
get_bits(16); get_bits(16);
get_bits(16); get_bits(16);
@ -2769,20 +2771,23 @@ void jpeg_decoder::init_progressive()
} }
calc_mcu_block_order(); 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 (!init_frame()) return false;
if (m_progressive_flag) init_progressive(); if (m_progressive_flag) return init_progressive();
else init_sequential(); return init_sequential();
} }
@ -2795,7 +2800,6 @@ void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream) jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
{ {
if (setjmp(m_jmp_state)) return;
decode_init(pStream); decode_init(pStream);
} }
@ -2804,9 +2808,7 @@ int jpeg_decoder::begin_decoding()
{ {
if (m_ready_flag) return JPGD_SUCCESS; if (m_ready_flag) return JPGD_SUCCESS;
if (m_error_code) return JPGD_FAILED; if (m_error_code) return JPGD_FAILED;
if (setjmp(m_jmp_state)) return JPGD_FAILED; if (!decode_start()) return JPGD_FAILED;
decode_start();
m_ready_flag = true; m_ready_flag = true;
return JPGD_SUCCESS; return JPGD_SUCCESS;
@ -2970,7 +2972,7 @@ unsigned char* jpgdDecompress(jpeg_decoder* decoder)
if (!pImage_data) return nullptr; if (!pImage_data) return nullptr;
for (int y = 0; y < image_height; y++) { for (int y = 0; y < image_height; y++) {
const uint8_t* pScan_line; const uint8_t* pScan_line = nullptr;
uint32_t scan_line_len; uint32_t scan_line_len;
if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) { if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) {
tvg::free(pImage_data); tvg::free(pImage_data);

View file

@ -1174,8 +1174,8 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
{ {
if (layer->masks.count == 0) return; if (layer->masks.count == 0) return;
//Introduce an intermediate scene for embracing the matte + masking //Introduce an intermediate scene for embracing matte + masking or precomp clipping + masking replaced by clipping
if (layer->matteTarget) { if (layer->matteTarget || layer->type == LottieLayer::Precomp) {
auto scene = Scene::gen(); auto scene = Scene::gen();
scene->push(layer->scene); scene->push(layer->scene);
layer->scene = scene; layer->scene = scene;

View file

@ -57,55 +57,52 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
bool GlGeometry::tesselate(const RenderSurface* image, 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.clear();
fill.index.reserve(6);
float left = 0.f; fill.vertex.reserve(5 * 4);
float top = 0.f; fill.index.reserve(6);
float right = image->w;
float bottom = image->h;
// left top point auto left = 0.f;
fill.vertex.push(left); auto top = 0.f;
fill.vertex.push(top); auto right = float(image->w);
auto bottom = float(image->h);
fill.vertex.push(0.f); // left top point
fill.vertex.push(1.f); fill.vertex.push(left);
// left bottom point fill.vertex.push(top);
fill.vertex.push(left);
fill.vertex.push(bottom);
fill.vertex.push(0.f); fill.vertex.push(0.f);
fill.vertex.push(0.f); fill.vertex.push(1.f);
// right top point // left bottom point
fill.vertex.push(right); fill.vertex.push(left);
fill.vertex.push(top); fill.vertex.push(bottom);
fill.vertex.push(1.f); fill.vertex.push(0.f);
fill.vertex.push(1.f); fill.vertex.push(0.f);
// right bottom point // right top point
fill.vertex.push(right); fill.vertex.push(right);
fill.vertex.push(bottom); fill.vertex.push(top);
fill.vertex.push(1.f); fill.vertex.push(1.f);
fill.vertex.push(0.f); fill.vertex.push(1.f);
// right bottom point
fill.vertex.push(right);
fill.vertex.push(bottom);
fill.index.push(0); fill.vertex.push(1.f);
fill.index.push(1); fill.vertex.push(0.f);
fill.index.push(2);
fill.index.push(2); fill.index.push(0);
fill.index.push(1); fill.index.push(1);
fill.index.push(3); fill.index.push(2);
bounds.x = 0; fill.index.push(2);
bounds.y = 0; fill.index.push(1);
bounds.w = image->w; fill.index.push(3);
bounds.h = image->h;
} bounds = {{0, 0}, {int32_t(image->w), int32_t(image->h)}};
return true; return true;
} }
@ -155,31 +152,20 @@ GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag)
RenderRegion GlGeometry::getBounds() const RenderRegion GlGeometry::getBounds() const
{ {
if (tvg::identity(&matrix)) { if (tvg::identity(&matrix)) return bounds;
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)};
lt *= matrix; auto lt = Point{float(bounds.min.x), float(bounds.min.y)} * matrix;
lb *= matrix; auto lb = Point{float(bounds.min.x), float(bounds.max.y)} * matrix;
rt *= matrix; auto rt = Point{float(bounds.max.x), float(bounds.min.y)} * matrix;
rb *= 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)); auto 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)); auto 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)); auto 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 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;
}
} }

View file

@ -52,17 +52,17 @@ void GlRenderPass::addRenderTask(GlRenderTask* task)
void GlRenderPass::getMatrix(float *dst, const Matrix &matrix) const void GlRenderPass::getMatrix(float *dst, const Matrix &matrix) const
{ {
const auto& vp = getViewport();
Matrix postMatrix{}; Matrix postMatrix{};
tvg::identity(&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; auto m = postMatrix * matrix;
float modelMatrix[16]; float modelMatrix[16];
GET_MATRIX44(m, modelMatrix); GET_MATRIX44(m, modelMatrix);
MVP_MATRIX(vp.w, vp.h); MVP_MATRIX(vp.w(), vp.h());
MULTIPLY_MATRIX(mvp, modelMatrix, dst); MULTIPLY_MATRIX(mvp, modelMatrix, dst);
} }

View file

@ -62,10 +62,7 @@ public:
} }
auto task = new T(program, targetFbo, mFbo, std::move(mTasks)); auto task = new T(program, targetFbo, mFbo, std::move(mTasks));
task->setRenderSize(mFbo->getViewport().w(), mFbo->getViewport().h());
const auto& vp = mFbo->getViewport();
task->setRenderSize(static_cast<uint32_t>(vp.w), static_cast<uint32_t>(vp.h));
return task; return task;
} }

View file

@ -22,25 +22,18 @@
#include "tvgGlRenderTarget.h" #include "tvgGlRenderTarget.h"
GlRenderTarget::GlRenderTarget(uint32_t width, uint32_t height): mWidth(width), mHeight(height) {} GlRenderTarget::GlRenderTarget() {}
GlRenderTarget::~GlRenderTarget() GlRenderTarget::~GlRenderTarget()
{ {
if (mFbo == 0) return; reset();
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));
}
} }
void GlRenderTarget::init(GLint resolveId) void GlRenderTarget::init(uint32_t width, uint32_t height, GLint resolveId)
{ {
if (mFbo != GL_INVALID_VALUE || 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. //TODO: fbo is used. maybe we can consider the direct rendering with resolveId as well.
GL_CHECK(glGenFramebuffers(1, &mFbo)); GL_CHECK(glGenFramebuffers(1, &mFbo));
@ -82,6 +75,18 @@ void GlRenderTarget::init(GLint resolveId)
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, 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(uint32_t maxWidth, uint32_t maxHeight): mMaxWidth(maxWidth), mMaxHeight(maxHeight), mPool() {}
GlRenderTargetPool::~GlRenderTargetPool() GlRenderTargetPool::~GlRenderTargetPool()
@ -102,31 +107,28 @@ uint32_t alignPow2(uint32_t value)
GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLuint resolveId) GlRenderTarget* GlRenderTargetPool::getRenderTarget(const RenderRegion& vp, GLuint resolveId)
{ {
uint32_t width = static_cast<uint32_t>(vp.w); auto width = vp.w();
uint32_t height = static_cast<uint32_t>(vp.h); auto height = vp.h();
// pow2 align width and height // pow2 align width and height
if (width >= mMaxWidth) width = mMaxWidth; if (width >= mMaxWidth) width = mMaxWidth;
else width = alignPow2(width); else width = alignPow2(width);
if (width >= mMaxWidth) width = mMaxWidth; if (width >= mMaxWidth) width = mMaxWidth;
if (height >= mMaxHeight) height = mMaxHeight; if (height >= mMaxHeight) height = mMaxHeight;
else height = alignPow2(height); else height = alignPow2(height);
if (height >= mMaxHeight) height = mMaxHeight; if (height >= mMaxHeight) height = mMaxHeight;
for (uint32_t i = 0; i < mPool.count; i++) { for (uint32_t i = 0; i < mPool.count; i++) {
auto rt = mPool[i]; auto rt = mPool[i];
if (rt->getWidth() == width && rt->getHeight() == height) { if (rt->getWidth() == width && rt->getHeight() == height) {
rt->setViewport(vp); rt->setViewport(vp);
return rt; return rt;
} }
} }
auto rt = new GlRenderTarget(width, height); auto rt = new GlRenderTarget();
rt->init(resolveId); rt->init(width, height, resolveId);
rt->setViewport(vp); rt->setViewport(vp);
mPool.push(rt); mPool.push(rt);
return rt; return rt;

View file

@ -28,11 +28,11 @@
class GlRenderTarget class GlRenderTarget
{ {
public: public:
GlRenderTarget() = default; GlRenderTarget();
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 getFboId() { return mFbo; }
GLuint getResolveFboId() { return mResolveFbo; } GLuint getResolveFboId() { return mResolveFbo; }

View file

@ -24,6 +24,9 @@
#include "tvgGlProgram.h" #include "tvgGlProgram.h"
#include "tvgGlRenderPass.h" #include "tvgGlRenderPass.h"
/************************************************************************/
/* GlRenderTask Class Implementation */
/************************************************************************/
GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program) GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(program)
{ {
@ -33,6 +36,7 @@ GlRenderTask::GlRenderTask(GlProgram* program, GlRenderTask* other): mProgram(pr
mIndexCount = other->mIndexCount; mIndexCount = other->mIndexCount;
} }
void GlRenderTask::run() void GlRenderTask::run()
{ {
// bind shader // bind shader
@ -45,7 +49,7 @@ void GlRenderTask::run()
} }
// setup scissor rect // 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 // setup attribute layout
for (uint32_t i = 0; i < mVertexLayout.count; i++) { for (uint32_t i = 0; i < mVertexLayout.count; i++) {
@ -81,36 +85,44 @@ void GlRenderTask::run()
} }
} }
void GlRenderTask::addVertexLayout(const GlVertexLayout &layout) void GlRenderTask::addVertexLayout(const GlVertexLayout &layout)
{ {
mVertexLayout.push(layout); mVertexLayout.push(layout);
} }
void GlRenderTask::addBindResource(const GlBindingResource &binding) void GlRenderTask::addBindResource(const GlBindingResource &binding)
{ {
mBindingResources.push(binding); mBindingResources.push(binding);
} }
void GlRenderTask::setDrawRange(uint32_t offset, uint32_t count) void GlRenderTask::setDrawRange(uint32_t offset, uint32_t count)
{ {
mIndexOffset = offset; mIndexOffset = offset;
mIndexCount = count; mIndexCount = count;
} }
void GlRenderTask::setViewport(const RenderRegion &viewport) void GlRenderTask::setViewport(const RenderRegion &viewport)
{ {
mViewport = viewport; mViewport = viewport;
if (mViewport.w < 0) { if (mViewport.max.x < mViewport.min.x) mViewport.max.x = mViewport.min.x;
mViewport.w = 0; if (mViewport.max.y < mViewport.min.y) mViewport.max.y = mViewport.min.y;
}
if (mViewport.h < 0) {
mViewport.h = 0;
}
} }
/************************************************************************/
/* GlStencilCoverTask Class Implementation */
/************************************************************************/
GlStencilCoverTask::GlStencilCoverTask(GlRenderTask* stencil, GlRenderTask* cover, GlStencilMode mode) 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() GlStencilCoverTask::~GlStencilCoverTask()
{ {
@ -118,6 +130,7 @@ GlStencilCoverTask::~GlStencilCoverTask()
delete mCoverTask; delete mCoverTask;
} }
void GlStencilCoverTask::run() void GlStencilCoverTask::run()
{ {
GL_CHECK(glEnable(GL_STENCIL_TEST)); GL_CHECK(glEnable(GL_STENCIL_TEST));
@ -151,12 +164,18 @@ void GlStencilCoverTask::run()
GL_CHECK(glDisable(GL_STENCIL_TEST)); GL_CHECK(glDisable(GL_STENCIL_TEST));
} }
void GlStencilCoverTask::normalizeDrawDepth(int32_t maxDepth) void GlStencilCoverTask::normalizeDrawDepth(int32_t maxDepth)
{ {
mCoverTask->normalizeDrawDepth(maxDepth); mCoverTask->normalizeDrawDepth(maxDepth);
mStencilTask->normalizeDrawDepth(maxDepth); mStencilTask->normalizeDrawDepth(maxDepth);
} }
/************************************************************************/
/* GlComposeTask Class Implementation */
/************************************************************************/
GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks) GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks)
:GlRenderTask(program) ,mTargetFbo(target), mFbo(fbo), mTasks() :GlRenderTask(program) ,mTargetFbo(target), mFbo(fbo), mTasks()
{ {
@ -164,12 +183,14 @@ GlComposeTask::GlComposeTask(GlProgram* program, GLuint target, GlRenderTarget*
tasks.clear(); tasks.clear();
} }
GlComposeTask::~GlComposeTask() GlComposeTask::~GlComposeTask()
{ {
ARRAY_FOREACH(p, mTasks) delete(*p); ARRAY_FOREACH(p, mTasks) delete(*p);
mTasks.clear(); mTasks.clear();
} }
void GlComposeTask::run() void GlComposeTask::run()
{ {
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getSelfFbo())); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getSelfFbo()));
@ -209,29 +230,43 @@ void GlComposeTask::run()
onResolve(); 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_READ_FRAMEBUFFER, getSelfFbo()));
GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getResolveFboId())); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, getResolveFboId()));
GL_CHECK(glBlitFramebuffer(0, 0, mRenderWidth, mRenderHeight, 0, 0, mRenderWidth, mRenderHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST)); 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) GlBlitTask::GlBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks)
: GlComposeTask(program, target, fbo, std::move(tasks)), mColorTex(fbo->getColorTexture()) : GlComposeTask(program, target, fbo, std::move(tasks)), mColorTex(fbo->getColorTexture())
{ {
} }
void GlBlitTask::run() void GlBlitTask::run()
{ {
GlComposeTask::run(); GlComposeTask::run();
GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo())); 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) { if (mClearBuffer) {
GL_CHECK(glClearColor(0, 0, 0, 0)); GL_CHECK(glClearColor(0, 0, 0, 0));
@ -246,16 +281,24 @@ void GlBlitTask::run()
GlRenderTask::run(); GlRenderTask::run();
} }
/************************************************************************/
/* GlDrawBlitTask Class Implementation */
/************************************************************************/
GlDrawBlitTask::GlDrawBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks) GlDrawBlitTask::GlDrawBlitTask(GlProgram* program, GLuint target, GlRenderTarget* fbo, Array<GlRenderTask*>&& tasks)
: GlComposeTask(program, target, fbo, std::move(tasks)) : GlComposeTask(program, target, fbo, std::move(tasks))
{ {
} }
GlDrawBlitTask::~GlDrawBlitTask() GlDrawBlitTask::~GlDrawBlitTask()
{ {
if (mPrevTask) delete mPrevTask; if (mPrevTask) delete mPrevTask;
} }
void GlDrawBlitTask::run() void GlDrawBlitTask::run()
{ {
if (mPrevTask) mPrevTask->run(); if (mPrevTask) mPrevTask->run();
@ -269,15 +312,22 @@ void GlDrawBlitTask::run()
GlRenderTask::run(); GlRenderTask::run();
} }
/************************************************************************/
/* GlClipTask Class Implementation */
/************************************************************************/
GlClipTask::GlClipTask(GlRenderTask* clip, GlRenderTask* mask) GlClipTask::GlClipTask(GlRenderTask* clip, GlRenderTask* mask)
:GlRenderTask(nullptr), mClipTask(clip), mMaskTask(mask) {} :GlRenderTask(nullptr), mClipTask(clip), mMaskTask(mask) {}
GlClipTask::~GlClipTask() GlClipTask::~GlClipTask()
{ {
delete mClipTask; delete mClipTask;
delete mMaskTask; delete mMaskTask;
} }
void GlClipTask::run() void GlClipTask::run()
{ {
GL_CHECK(glEnable(GL_STENCIL_TEST)); GL_CHECK(glEnable(GL_STENCIL_TEST));
@ -304,14 +354,22 @@ void GlClipTask::run()
GL_CHECK(glDisable(GL_STENCIL_TEST)); GL_CHECK(glDisable(GL_STENCIL_TEST));
} }
void GlClipTask::normalizeDrawDepth(int32_t maxDepth) void GlClipTask::normalizeDrawDepth(int32_t maxDepth)
{ {
mClipTask->normalizeDrawDepth(maxDepth); mClipTask->normalizeDrawDepth(maxDepth);
mMaskTask->normalizeDrawDepth(maxDepth); mMaskTask->normalizeDrawDepth(maxDepth);
} }
/************************************************************************/
/* GlSimpleBlendTask Class Implementation */
/************************************************************************/
GlSimpleBlendTask::GlSimpleBlendTask(BlendMethod method, GlProgram* program) GlSimpleBlendTask::GlSimpleBlendTask(BlendMethod method, GlProgram* program)
: GlRenderTask(program), mBlendMethod(method) {} : GlRenderTask(program), mBlendMethod(method)
{
}
void GlSimpleBlendTask::run() void GlSimpleBlendTask::run()
{ {
@ -332,8 +390,17 @@ void GlSimpleBlendTask::run()
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
} }
/************************************************************************/
/* GlComplexBlendTask Class Implementation */
/************************************************************************/
GlComplexBlendTask::GlComplexBlendTask(GlProgram* program, GlRenderTarget* dstFbo, GlRenderTarget* dstCopyFbo, GlRenderTask* stencilTask, GlComposeTask* composeTask) 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() GlComplexBlendTask::~GlComplexBlendTask()
{ {
@ -341,6 +408,7 @@ GlComplexBlendTask::~GlComplexBlendTask()
delete mComposeTask; delete mComposeTask;
} }
void GlComplexBlendTask::run() void GlComplexBlendTask::run()
{ {
mComposeTask->run(); mComposeTask->run();
@ -349,12 +417,11 @@ void GlComplexBlendTask::run()
GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId())); GL_CHECK(glBindFramebuffer(GL_READ_FRAMEBUFFER, mDstFbo->getFboId()));
GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->getResolveFboId())); GL_CHECK(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, mDstCopyFbo->getResolveFboId()));
GL_CHECK(glViewport(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)); GL_CHECK(glScissor(0, 0, mDstFbo->getViewport().w(), mDstFbo->getViewport().h()));
const auto& vp = getViewport(); const auto& vp = getViewport();
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(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(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->getFboId())); GL_CHECK(glBindFramebuffer(GL_FRAMEBUFFER, mDstFbo->getFboId()));
@ -381,12 +448,17 @@ void GlComplexBlendTask::run()
GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); GL_CHECK(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
} }
void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth) void GlComplexBlendTask::normalizeDrawDepth(int32_t maxDepth)
{ {
mStencilTask->normalizeDrawDepth(maxDepth); mStencilTask->normalizeDrawDepth(maxDepth);
GlRenderTask::normalizeDrawDepth(maxDepth); GlRenderTask::normalizeDrawDepth(maxDepth);
} }
/************************************************************************/
/* GlGaussianBlurTask Class Implementation */
/************************************************************************/
void GlGaussianBlurTask::run() void GlGaussianBlurTask::run()
{ {
const auto vp = getViewport(); const auto vp = getViewport();
@ -439,6 +511,9 @@ void GlGaussianBlurTask::run()
GL_CHECK(glEnable(GL_BLEND)); GL_CHECK(glEnable(GL_BLEND));
} }
/************************************************************************/
/* GlEffectDropShadowTask Class Implementation */
/************************************************************************/
void GlEffectDropShadowTask::run() void GlEffectDropShadowTask::run()
{ {
@ -492,6 +567,9 @@ void GlEffectDropShadowTask::run()
GL_CHECK(glEnable(GL_BLEND)); GL_CHECK(glEnable(GL_BLEND));
} }
/************************************************************************/
/* GlEffectColorTransformTask Class Implementation */
/************************************************************************/
void GlEffectColorTransformTask::run() void GlEffectColorTransformTask::run()
{ {

View file

@ -130,12 +130,10 @@ public:
protected: protected:
GLuint getTargetFbo() { return mTargetFbo; } GLuint getTargetFbo() { return mTargetFbo; }
GLuint getSelfFbo(); GLuint getSelfFbo();
GLuint getResolveFboId(); GLuint getResolveFboId();
void onResolve(); void onResolve();
private: private:
GLuint mTargetFbo; GLuint mTargetFbo;
GlRenderTarget* mFbo; GlRenderTarget* mFbo;

View file

@ -52,6 +52,8 @@ void GlRenderer::flush()
{ {
clearDisposes(); clearDisposes();
mRootTarget.reset();
ARRAY_FOREACH(p, mComposePool) delete(*p); ARRAY_FOREACH(p, mComposePool) delete(*p);
mComposePool.clear(); mComposePool.clear();
@ -181,10 +183,10 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
bbox.intersect(vp); bbox.intersect(vp);
} }
auto x = bbox.x - vp.x; auto x = bbox.sx() - vp.sx();
auto y = bbox.y - vp.y; auto y = bbox.sy() - vp.sy();
auto w = bbox.w; auto w = bbox.sw();
auto h = bbox.h; auto h = bbox.sh();
GlRenderTask* task = nullptr; GlRenderTask* task = nullptr;
if (mBlendMethod != BlendMethod::Normal && !complexBlend) task = new GlSimpleBlendTask(mBlendMethod, mPrograms[RT_Color]); 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; 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; GlRenderTask* stencilTask = nullptr;
@ -266,7 +269,6 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
{ {
auto vp = currentPass()->getViewport(); auto vp = currentPass()->getViewport();
auto bbox = sdata.geometry.viewport; auto bbox = sdata.geometry.viewport;
bbox.intersect(vp); bbox.intersect(vp);
const Fill::ColorStop* stops = nullptr; const Fill::ColorStop* stops = nullptr;
@ -275,13 +277,9 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
GlRenderTask* task = nullptr; GlRenderTask* task = nullptr;
if (fill->type() == Type::LinearGradient) { if (fill->type() == Type::LinearGradient) task = new GlRenderTask(mPrograms[RT_LinGradient]);
task = new GlRenderTask(mPrograms[RT_LinGradient]); else if (fill->type() == Type::RadialGradient) task = new GlRenderTask(mPrograms[RT_RadGradient]);
} else if (fill->type() == Type::RadialGradient) { else return;
task = new GlRenderTask(mPrograms[RT_RadGradient]);
} else {
return;
}
task->setDrawDepth(depth); task->setDrawDepth(depth);
@ -291,13 +289,11 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
} }
auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds()); auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds());
if (complexBlend) vp = currentPass()->getViewport(); if (complexBlend) vp = currentPass()->getViewport();
auto x = bbox.x - vp.x; auto x = bbox.sx() - vp.sx();
auto y = bbox.y - vp.y; auto y = vp.sh() - (bbox.sy() - vp.sy()) - bbox.sh();
task->setViewport({{x, y}, {x + bbox.sw(), y + bbox.sh()}});
task->setViewport({x, vp.h - y - bbox.h, bbox.w, bbox.h});
GlRenderTask* stencilTask = nullptr; GlRenderTask* stencilTask = nullptr;
GlStencilMode stencilMode = sdata.geometry.getStencilMode(flag); 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) { for (uint32_t i = 0; i < clips.count; ++i) {
auto sdata = static_cast<GlShape*>(clips[i]); auto sdata = static_cast<GlShape*>(clips[i]);
auto clipTask = new GlRenderTask(mPrograms[RT_Stencil]); auto clipTask = new GlRenderTask(mPrograms[RT_Stencil]);
clipTask->setDrawDepth(clipDepths[i]); clipTask->setDrawDepth(clipDepths[i]);
auto flag = (sdata->geometry.stroke.vertex.count > 0) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path; auto flag = (sdata->geometry.stroke.vertex.count > 0) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path;
sdata->geometry.draw(clipTask, &mGpuBuffer, flag); sdata->geometry.draw(clipTask, &mGpuBuffer, flag);
auto bbox = sdata->geometry.viewport; auto bbox = sdata->geometry.viewport;
bbox.intersect(vp); bbox.intersect(vp);
auto x = bbox.x - vp.x; auto x = bbox.sx() - vp.sx();
auto y = bbox.y - vp.y; auto y = vp.sh() - (bbox.sy() - vp.sy()) - bbox.sh();
clipTask->setViewport({{x, y}, {x + bbox.sw(), y + bbox.sh()}});
clipTask->setViewport({x, vp.h - y - bbox.h, bbox.w, bbox.h});
float matrix44[16]; float matrix44[16];
currentPass()->getMatrix(matrix44, sdata->geometry.matrix); 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->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), identityVertexOffset});
maskTask->addBindResource(GlBindingResource{0, loc, mGpuBuffer.getBufferId(), mat4Offset, 16 * sizeof(float), }); maskTask->addBindResource(GlBindingResource{0, loc, mGpuBuffer.getBufferId(), mat4Offset, 16 * sizeof(float), });
maskTask->setDrawRange(identityIndexOffset, 6); 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)); currentPass()->addRenderTask(new GlClipTask(clipTask, maskTask));
} }
@ -541,11 +533,10 @@ GlRenderPass* GlRenderer::currentPass()
bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds) 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); bounds.intersect(vp);
if (bounds.invalid()) return false;
if (bounds.w == 0 || bounds.h == 0) return false;
if (mBlendMethod == BlendMethod::Normal || mBlendMethod == BlendMethod::Add || mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) return false; if (mBlendMethod == BlendMethod::Normal || mBlendMethod == BlendMethod::Add || mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) return false;
@ -571,37 +562,26 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h)); if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h));
auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp); auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp);
{ auto x = vp.sx();
const auto& passVp = currentPass()->getViewport(); auto y = currentPass()->getViewport().sh() - vp.sy() - vp.sh();
stencilTask->setViewport({{x, y}, {x + vp.sw(), y + vp.sh()}});
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});
}
stencilTask->setDrawDepth(currentPass()->nextDrawDepth()); stencilTask->setDrawDepth(currentPass()->nextDrawDepth());
{ // set view matrix
// set view matrix float matrix44[16];
float matrix44[16]; currentPass()->getMatrix(matrix44, matrix);
currentPass()->getMatrix(matrix44, matrix); uint32_t viewOffset = mGpuBuffer.push(matrix44, 16 * sizeof(float), true);
uint32_t viewOffset = mGpuBuffer.push(matrix44, 16 * sizeof(float), true); stencilTask->addBindResource(GlBindingResource{
stencilTask->addBindResource(GlBindingResource{ 0,
0, stencilTask->getProgram()->getUniformBlockIndex("Matrix"),
stencilTask->getProgram()->getUniformBlockIndex("Matrix"), mGpuBuffer.getBufferId(),
mGpuBuffer.getBufferId(), viewOffset,
viewOffset, 16 * sizeof(float),
16 * sizeof(float), });
});
}
auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask); auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask);
prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight()); prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight());
task->setDrawDepth(currentPass()->nextDrawDepth()); task->setDrawDepth(currentPass()->nextDrawDepth());
// src and dst texture // src and dst texture
@ -632,8 +612,7 @@ GlProgram* GlRenderer::getBlendProgram()
void GlRenderer::prepareBlitTask(GlBlitTask* task) void GlRenderer::prepareBlitTask(GlBlitTask* task)
{ {
RenderRegion region{0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)}; prepareCmpTask(task, {{0, 0}, {int32_t(surface.w), int32_t(surface.h)}}, surface.w, surface.h);
prepareCmpTask(task, region, surface.w, surface.h);
task->addBindResource(GlBindingResource{0, task->getColorTexture(), task->getProgram()->getUniformLocation("uSrcTexture")}); 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; auto taskVp = vp;
taskVp.intersect(passVp); taskVp.intersect(passVp);
auto x = taskVp.x - passVp.x; auto x = taskVp.sx() - passVp.sx();
auto y = taskVp.y - passVp.y; auto y = taskVp.sy() - passVp.sy();
auto w = taskVp.w; auto w = taskVp.sw();
auto h = taskVp.h; auto h = taskVp.sh();
float rw = static_cast<float>(passVp.w); float rw = static_cast<float>(passVp.w());
float rh = static_cast<float>(passVp.h); float rh = static_cast<float>(passVp.h());
float l = static_cast<float>(x); float l = static_cast<float>(x);
float t = static_cast<float>(rh - y); 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{0, 2, 4 * sizeof(float), vertexOffset});
task->addVertexLayout(GlVertexLayout{1, 2, 4 * sizeof(float), vertexOffset + 2 * sizeof(float)}); task->addVertexLayout(GlVertexLayout{1, 2, 4 * sizeof(float), vertexOffset + 2 * sizeof(float)});
task->setDrawRange(indexOffset, indices.count); task->setDrawRange(indexOffset, indices.count);
y = (passVp.sh() - y - h);
task->setViewport({x, static_cast<int32_t>((passVp.h - y - h)), w, h}); task->setViewport({{x, y}, {x + w, y + h}});
} }
@ -741,11 +719,11 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
if (program && !selfPass->isEmpty() && !maskPass->isEmpty()) { if (program && !selfPass->isEmpty() && !maskPass->isEmpty()) {
auto prev_task = maskPass->endRenderPass<GlComposeTask>(nullptr, currentPass()->getFboId()); auto prev_task = maskPass->endRenderPass<GlComposeTask>(nullptr, currentPass()->getFboId());
prev_task->setDrawDepth(currentPass()->nextDrawDepth()); 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); prev_task->setViewport(glCmp->bbox);
auto compose_task = selfPass->endRenderPass<GlDrawBlitTask>(program, currentPass()->getFboId()); 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); compose_task->setPrevTask(prev_task);
prepareCmpTask(compose_task, glCmp->bbox, selfPass->getFboWidth(), selfPass->getFboHeight()); 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->addBindResource(GlBindingResource{1, maskPass->getTextureId(), program->getUniformLocation("uMaskTexture")});
compose_task->setDrawDepth(currentPass()->nextDrawDepth()); 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); currentPass()->addRenderTask(compose_task);
} }
@ -767,7 +745,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
if (!renderPass->isEmpty()) { if (!renderPass->isEmpty()) {
auto task = renderPass->endRenderPass<GlDrawBlitTask>(mPrograms[RT_Image], currentPass()->getFboId()); 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()); prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight());
task->setDrawDepth(currentPass()->nextDrawDepth()); task->setDrawDepth(currentPass()->nextDrawDepth());
@ -795,7 +773,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
// texture id // texture id
task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uTexture")}); 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)); currentPass()->addRenderTask(std::move(task));
} }
delete(renderPass); delete(renderPass);
@ -834,9 +812,8 @@ bool GlRenderer::target(void* context, int32_t id, uint32_t w, uint32_t h)
currentContext(); currentContext();
mRootTarget = GlRenderTarget(surface.w, surface.h); mRootTarget.setViewport({{0, 0}, {int32_t(surface.w), int32_t(surface.h)}});
mRootTarget.setViewport({0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)}); mRootTarget.init(surface.w, surface.h, mTargetFboId);
mRootTarget.init(mTargetFboId);
return true; return true;
} }
@ -863,7 +840,7 @@ bool GlRenderer::sync()
prepareBlitTask(task); prepareBlitTask(task);
task->mClearBuffer = mClearBuffer; 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()) { if (mGpuBuffer.flushToGPU()) {
mGpuBuffer.bind(); mGpuBuffer.bind();
@ -884,7 +861,7 @@ bool GlRenderer::sync()
RenderRegion GlRenderer::region(RenderData data) RenderRegion GlRenderer::region(RenderData data)
{ {
if (currentPass()->isEmpty()) return {0, 0, 0, 0}; if (currentPass()->isEmpty()) return {};
auto shape = reinterpret_cast<GlShape*>(data); auto shape = reinterpret_cast<GlShape*>(data);
auto bounds = shape->geometry.getBounds(); auto bounds = shape->geometry.getBounds();
@ -934,20 +911,11 @@ bool GlRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_
cmp->opacity = opacity; cmp->opacity = opacity;
uint32_t index = mRenderPassStack.count - 1; 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); auto glCmp = static_cast<GlCompositor*>(cmp);
if (glCmp->bbox.valid()) mRenderPassStack.push(new GlRenderPass(mComposePool[index]->getRenderTarget(glCmp->bbox)));
if (glCmp->bbox.w > 0 && glCmp->bbox.h > 0) { else mRenderPassStack.push(new GlRenderPass(nullptr));
auto renderTarget = mComposePool[index]->getRenderTarget(glCmp->bbox);
mRenderPassStack.push(new GlRenderPass(renderTarget));
} else {
// empty render pass
mRenderPassStack.push(new GlRenderPass(nullptr));
}
return true; return true;
} }
@ -1068,12 +1036,12 @@ bool GlRenderer::effectGaussianBlurRegion(RenderEffectGaussianBlur* effect)
{ {
auto gaussianBlur = (GlGaussianBlur*)effect->rd; auto gaussianBlur = (GlGaussianBlur*)effect->rd;
if (effect->direction != 2) { if (effect->direction != 2) {
effect->extend.x = -gaussianBlur->extend; effect->extend.min.x = -gaussianBlur->extend;
effect->extend.w = +gaussianBlur->extend * 2; effect->extend.max.x = +gaussianBlur->extend;
} }
if (effect->direction != 1) { if (effect->direction != 1) {
effect->extend.y = -gaussianBlur->extend; effect->extend.min.y = -gaussianBlur->extend;
effect->extend.h = +gaussianBlur->extend * 2; effect->extend.max.y = +gaussianBlur->extend;
} }
return true; return true;
}; };
@ -1082,10 +1050,10 @@ bool GlRenderer::effectGaussianBlurRegion(RenderEffectGaussianBlur* effect)
bool GlRenderer::effectDropShadowRegion(RenderEffectDropShadow* effect) bool GlRenderer::effectDropShadowRegion(RenderEffectDropShadow* effect)
{ {
auto gaussianBlur = (GlDropShadow*)effect->rd; auto gaussianBlur = (GlDropShadow*)effect->rd;
effect->extend.x = -gaussianBlur->extend; effect->extend.min.x = -gaussianBlur->extend;
effect->extend.w = +gaussianBlur->extend * 2; effect->extend.max.x = +gaussianBlur->extend;
effect->extend.y = -gaussianBlur->extend; effect->extend.min.y = -gaussianBlur->extend;
effect->extend.h = +gaussianBlur->extend * 2; effect->extend.max.y = +gaussianBlur->extend;
return true; return true;
}; };
@ -1135,7 +1103,6 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
auto voffset = mGpuBuffer.push((void*)vdata, sizeof(vdata)); auto voffset = mGpuBuffer.push((void*)vdata, sizeof(vdata));
auto ioffset = mGpuBuffer.pushIndex((void*)idata, sizeof(idata)); auto ioffset = mGpuBuffer.pushIndex((void*)idata, sizeof(idata));
// effect gaussian blur
if (effect->type == SceneEffect::GaussianBlur) { if (effect->type == SceneEffect::GaussianBlur) {
// get gaussian programs // get gaussian programs
GlProgram* programHorz = mPrograms[RT_GaussianHorz]; GlProgram* programHorz = mPrograms[RT_GaussianHorz];
@ -1151,7 +1118,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
// create gaussian blur tasks // create gaussian blur tasks
auto gaussianTask = new GlGaussianBlurTask(dstFbo, dstCopyFbo0, dstCopyFbo1); auto gaussianTask = new GlGaussianBlurTask(dstFbo, dstCopyFbo0, dstCopyFbo1);
gaussianTask->effect = (RenderEffectGaussianBlur*)effect; 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 // horizontal blur task and geometry
gaussianTask->horzTask = new GlRenderTask(programHorz); gaussianTask->horzTask = new GlRenderTask(programHorz);
gaussianTask->horzTask->addBindResource(GlBindingResource{0, programHorz->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), blurOffset, sizeof(GlGaussianBlur)}); gaussianTask->horzTask->addBindResource(GlBindingResource{0, programHorz->getUniformBlockIndex("Gaussian"), mGpuBuffer.getBufferId(), blurOffset, sizeof(GlGaussianBlur)});
@ -1164,8 +1131,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
gaussianTask->vertTask->setDrawRange(ioffset, 6); gaussianTask->vertTask->setDrawRange(ioffset, 6);
// add task to render pipeline // add task to render pipeline
pass->addRenderTask(gaussianTask); pass->addRenderTask(gaussianTask);
} // effect drop shadow } else if (effect->type == SceneEffect::DropShadow) {
else if (effect->type == SceneEffect::DropShadow) {
// get programs // get programs
GlProgram* program = mPrograms[RT_DropShadow]; GlProgram* program = mPrograms[RT_DropShadow];
GlProgram* programHorz = mPrograms[RT_GaussianHorz]; GlProgram* programHorz = mPrograms[RT_GaussianHorz];
@ -1181,7 +1147,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
// create gaussian blur tasks // create gaussian blur tasks
auto task = new GlEffectDropShadowTask(program, dstFbo, dstCopyFbo0, dstCopyFbo1); auto task = new GlEffectDropShadowTask(program, dstFbo, dstCopyFbo0, dstCopyFbo1);
task->effect = (RenderEffectDropShadow*)effect; 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->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("DropShadow"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlDropShadow)});
task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset});
task->setDrawRange(ioffset, 6); task->setDrawRange(ioffset, 6);
@ -1197,8 +1163,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
task->vertTask->setDrawRange(ioffset, 6); task->vertTask->setDrawRange(ioffset, 6);
// add task to render pipeline // add task to render pipeline
pass->addRenderTask(task); 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{}; GlProgram* program{};
if (effect->type == SceneEffect::Fill) program = mPrograms[RT_EffectFill]; if (effect->type == SceneEffect::Fill) program = mPrograms[RT_EffectFill];
else if (effect->type == SceneEffect::Tint) program = mPrograms[RT_EffectTint]; else if (effect->type == SceneEffect::Tint) program = mPrograms[RT_EffectTint];
@ -1212,7 +1177,7 @@ bool GlRenderer::render(TVG_UNUSED RenderCompositor* cmp, const RenderEffect* ef
auto paramsOffset = mGpuBuffer.push(params, sizeof(GlEffectParams), true); auto paramsOffset = mGpuBuffer.push(params, sizeof(GlEffectParams), true);
// create and setup task // create and setup task
auto task = new GlEffectColorTransformTask(program, dstFbo, dstCopyFbo); 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->addBindResource(GlBindingResource{0, program->getUniformBlockIndex("Params"), mGpuBuffer.getBufferId(), paramsOffset, sizeof(GlEffectParams)});
task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset}); task->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), voffset});
task->setDrawRange(ioffset, 6); task->setDrawRange(ioffset, 6);
@ -1255,25 +1220,18 @@ bool GlRenderer::blend(BlendMethod method)
bool GlRenderer::renderImage(void* data) bool GlRenderer::renderImage(void* data)
{ {
auto sdata = static_cast<GlShape*>(data); auto sdata = static_cast<GlShape*>(data);
if (!sdata) return false; 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 vp = currentPass()->getViewport();
auto bbox = sdata->geometry.viewport; auto bbox = sdata->geometry.viewport;
bbox.intersect(vp); bbox.intersect(vp);
if (bbox.invalid()) return true;
if (bbox.w <= 0 || bbox.h <= 0) return true; auto x = bbox.sx() - vp.sx();
auto y = bbox.sy() - vp.sy();
auto x = bbox.x - vp.x; auto drawDepth = currentPass()->nextDrawDepth();
auto y = bbox.y - vp.y;
int32_t drawDepth = currentPass()->nextDrawDepth();
if (!sdata->clips.empty()) drawClip(sdata->clips); if (!sdata->clips.empty()) drawClip(sdata->clips);
@ -1286,7 +1244,6 @@ bool GlRenderer::renderImage(void* data)
} }
bool complexBlend = beginComplexBlending(bbox, sdata->geometry.getBounds()); bool complexBlend = beginComplexBlending(bbox, sdata->geometry.getBounds());
if (complexBlend) vp = currentPass()->getViewport(); if (complexBlend) vp = currentPass()->getViewport();
// matrix buffer // matrix buffer
@ -1315,7 +1272,11 @@ bool GlRenderer::renderImage(void* data)
// texture id // texture id
task->addBindResource(GlBindingResource{0, sdata->texId, task->getProgram()->getUniformLocation("uTexture")}); 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); currentPass()->addRenderTask(task);
@ -1331,33 +1292,25 @@ bool GlRenderer::renderImage(void* data)
bool GlRenderer::renderShape(RenderData data) bool GlRenderer::renderShape(RenderData data)
{ {
auto sdata = static_cast<GlShape*>(data);
if (currentPass()->isEmpty()) return true; if (currentPass()->isEmpty()) return true;
auto sdata = static_cast<GlShape*>(data);
if (sdata->updateFlag == RenderUpdateFlag::None) return true; if (sdata->updateFlag == RenderUpdateFlag::None) return true;
const auto& vp = currentPass()->getViewport();
auto bbox = sdata->geometry.viewport; auto bbox = sdata->geometry.viewport;
bbox.intersect(vp); bbox.intersect(currentPass()->getViewport());
if (bbox.invalid()) return true;
if (bbox.w <= 0 || bbox.h <= 0) return true;
int32_t drawDepth1 = 0, drawDepth2 = 0; int32_t drawDepth1 = 0, drawDepth2 = 0;
size_t flags = static_cast<size_t>(sdata->updateFlag); if (sdata->updateFlag == RenderUpdateFlag::None) return false;
if (sdata->updateFlag & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Color)) drawDepth1 = currentPass()->nextDrawDepth();
if (flags == 0) return false; if (sdata->updateFlag & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke)) drawDepth2 = currentPass()->nextDrawDepth();
if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Color)) drawDepth1 = currentPass()->nextDrawDepth();
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::GradientStroke)) drawDepth2 = currentPass()->nextDrawDepth();
if (!sdata->clips.empty()) drawClip(sdata->clips); if (!sdata->clips.empty()) drawClip(sdata->clips);
auto processFill = [&]() { auto processFill = [&]() {
if (flags & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient)) { if (sdata->updateFlag & (RenderUpdateFlag::Color | RenderUpdateFlag::Gradient)) {
if (const auto& gradient = sdata->rshape->fill) { if (const auto& gradient = sdata->rshape->fill) {
drawPrimitive(*sdata, gradient, RenderUpdateFlag::Gradient, drawDepth1); drawPrimitive(*sdata, gradient, RenderUpdateFlag::Gradient, drawDepth1);
} else if (sdata->rshape->color.a > 0) { } else if (sdata->rshape->color.a > 0) {
@ -1368,7 +1321,7 @@ bool GlRenderer::renderShape(RenderData data)
auto processStroke = [&]() { auto processStroke = [&]() {
if (!sdata->rshape->stroke) return; 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()) { if (const auto& gradient = sdata->rshape->strokeFill()) {
drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke, drawDepth2); drawPrimitive(*sdata, gradient, RenderUpdateFlag::GradientStroke, drawDepth2);
} else if (sdata->rshape->stroke->color.a > 0) { } else if (sdata->rshape->stroke->color.a > 0) {

View file

@ -1516,12 +1516,7 @@ void Stroker::stroke(const RenderShape *rshape, const RenderPath& path)
RenderRegion Stroker::bounds() const RenderRegion Stroker::bounds() const
{ {
return RenderRegion { return {{int32_t(floor(mLeftTop.x)), int32_t(floor(mLeftTop.y))}, {int32_t(ceil(mRightBottom.x)), int32_t(ceil(mRightBottom.y))}};
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))),
};
} }
@ -2197,26 +2192,15 @@ void BWTessellator::tessellate(const RenderPath& path, const Matrix& matrix)
RenderRegion BWTessellator::bounds() const RenderRegion BWTessellator::bounds() const
{ {
return RenderRegion { 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))}};
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))),
};
} }
uint32_t BWTessellator::pushVertex(float x, float y) uint32_t BWTessellator::pushVertex(float x, float y)
{ {
auto index = _pushVertex(mBuffer->vertex, x, y); auto index = _pushVertex(mBuffer->vertex, x, y);
if (index == 0) bbox.max = bbox.min = {x, y};
if (index == 0) { 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)}};
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)};
}
return index; return index;
} }

View file

@ -156,7 +156,7 @@ private:
void pushTriangle(uint32_t a, uint32_t b, uint32_t c); void pushTriangle(uint32_t a, uint32_t b, uint32_t c);
GlGeometryBuffer* mBuffer; GlGeometryBuffer* mBuffer;
BBox bbox = {{}, {}}; BBox bbox = {};
}; };
} // namespace tvg } // namespace tvg

View file

@ -121,19 +121,20 @@ struct SwSpan
struct SwRle struct SwRle
{ {
SwSpan *spans; Array<SwSpan> spans;
uint32_t alloc;
uint32_t size;
};
struct SwBBox bool invalid() const
{
SwPoint min, max;
void reset()
{ {
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 struct SwFill
@ -210,14 +211,14 @@ struct SwDashStroke
struct SwShape struct SwShape
{ {
SwOutline* outline = nullptr; SwOutline* outline = nullptr;
SwStroke* stroke = nullptr; SwStroke* stroke = nullptr;
SwFill* fill = nullptr; SwFill* fill = nullptr;
SwRle* rle = nullptr; SwRle* rle = nullptr;
SwRle* strokeRle = 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? bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
}; };
struct SwImage struct SwImage
@ -279,7 +280,7 @@ struct SwCompositor : RenderCompositor
SwSurface* recoverSfc; //Recover surface when composition is started SwSurface* recoverSfc; //Recover surface when composition is started
SwCompositor* recoverCmp; //Recover compositor when composition is done SwCompositor* recoverCmp; //Recover compositor when composition is done
SwImage image; SwImage image;
SwBBox bbox; RenderRegion bbox;
bool valid; bool valid;
}; };
@ -499,16 +500,15 @@ SwFixed mathLength(const SwPoint& pt);
int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut); int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
SwFixed mathMean(SwFixed angle1, SwFixed angle2); SwFixed mathMean(SwFixed angle1, SwFixed angle2);
SwPoint mathTransform(const Point* to, const Matrix& transform); SwPoint mathTransform(const Point* to, const Matrix& transform);
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack); bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack);
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
void shapeReset(SwShape* shape); 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 shapePrepared(const SwShape* shape);
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias); bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid); void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform); 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 shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape); void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable); 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); SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke); void strokeFree(SwStroke* stroke);
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);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias); bool imageGenRle(SwImage* image, const RenderRegion& bbox, bool antiAlias);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid); void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image); void imageReset(SwImage* image);
void imageFree(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, 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. 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(SwRle* rle, const SwOutline* outline, const RenderRegion& bbox, bool antiAlias);
SwRle* rleRender(const SwBBox* bbox); SwRle* rleRender(const RenderRegion* bbox);
void rleFree(SwRle* rle); void rleFree(SwRle* rle);
void rleReset(SwRle* rle); void rleReset(SwRle* rle);
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2); void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
bool rleClip(SwRle* rle, const SwRle* clip); 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); SwMpool* mpoolInit(uint32_t threads);
bool mpoolTerm(SwMpool* mpool); bool mpoolTerm(SwMpool* mpool);
@ -568,7 +568,7 @@ void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface); bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c); bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c);
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity); bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c); bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity); 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); bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
@ -576,7 +576,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 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 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 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 rasterUnpremultiply(RenderSurface* surface);
void rasterPremultiply(RenderSurface* surface); void rasterPremultiply(RenderSurface* surface);
bool rasterConvertCS(RenderSurface* surface, ColorSpace to); bool rasterConvertCS(RenderSurface* surface, ColorSpace to);

View file

@ -72,7 +72,7 @@ static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool,
/* External Class Implementation */ /* 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); 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; 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; return false;
} }

View file

@ -271,27 +271,12 @@ SwPoint mathTransform(const Point* to, const Matrix& transform)
} }
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee) bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack)
{
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)
{ {
if (!outline) return false; if (!outline) return false;
if (outline->pts.empty() || outline->cntrs.empty()) { if (outline->pts.empty() || outline->cntrs.empty()) {
renderRegion.reset(); renderBox.reset();
return false; return false;
} }
@ -310,16 +295,13 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
} }
if (fastTrack) { if (fastTrack) {
renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f)); renderBox.min = {int32_t(round(xMin / 64.0f)), int32_t(round(yMin / 64.0f))};
renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f)); renderBox.max = {int32_t(round(xMax / 64.0f)), int32_t(round(yMax / 64.0f))};
renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
} else { } else {
renderRegion.min.x = xMin >> 6; renderBox.min = {xMin >> 6, yMin >> 6};
renderRegion.max.x = (xMax + 63) >> 6; renderBox.max = {(xMax + 63) >> 6, (yMax + 63) >> 6};
renderRegion.min.y = yMin >> 6;
renderRegion.max.y = (yMax + 63) >> 6;
} }
return mathClipBBox(clipRegion, renderRegion); renderBox.intersect(clipBox);
return renderBox.valid();
} }

View file

@ -61,7 +61,7 @@ static inline int _gaussianRemap(int end, int idx)
//TODO: SIMD OPTIMIZATION? //TODO: SIMD OPTIMIZATION?
template<int border = 0> 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) { if (flipped) {
src += (bbox.min.x * stride + bbox.min.y) << 2; 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) bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params)
{ {
//bbox region expansion for feathering //region expansion for feathering
auto& region = params->extend; auto& bbox = params->extend;
auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends; auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends;
if (params->direction != 2) { if (params->direction != 2) {
region.x = -extra; bbox.min.x = -extra;
region.w = extra * 2; bbox.max.x = extra;
} }
if (params->direction != 1) { if (params->direction != 1) {
region.y = -extra; bbox.min.y = -extra;
region.h = extra * 2; bbox.max.y = extra;
} }
return true; return true;
@ -230,7 +230,7 @@ struct SwDropShadow : SwGaussianBlur
//TODO: SIMD OPTIMIZATION? //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) { if (flipped) {
src += (bbox.min.x * stride + bbox.min.y); 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); src += (bbox.min.y * sstride + bbox.min.x);
dst += (region.min.y * dstride + region.min.x); dst += (bbox.min.y * dstride + bbox.min.x);
auto w = region.max.x - region.min.x; auto w = bbox.max.x - bbox.min.x;
auto h = region.max.y - region.min.y; auto h = bbox.max.y - bbox.min.y;
auto translucent = (direct || opacity < 255); auto translucent = (direct || opacity < 255);
//shift offset //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; 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); else dst += (offset.y * dstride);
for (auto y = 0; y < h; ++y) { 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) bool effectDropShadowRegion(RenderEffectDropShadow* params)
{ {
//bbox region expansion for feathering //region expansion for feathering
auto& region = params->extend; auto& bbox = params->extend;
auto& offset = static_cast<SwDropShadow*>(params->rd)->offset; auto& offset = static_cast<SwDropShadow*>(params->rd)->offset;
auto extra = static_cast<SwDropShadow*>(params->rd)->extends; auto extra = static_cast<SwDropShadow*>(params->rd)->extends;
region.x = -extra; bbox.min = {-extra, -extra};
region.w = extra * 2; bbox.max = {extra, extra};
region.y = -extra;
region.h = extra * 2;
region.x = std::min(region.x + (int32_t)offset.x, region.x); if (offset.x < 0) bbox.min.x += (int32_t) offset.x;
region.y = std::min(region.y + (int32_t)offset.y, region.y); else bbox.max.x += offset.x;
region.w += abs(offset.x);
region.h += abs(offset.y); if (offset.y < 0) bbox.min.y += (int32_t) offset.y;
else bbox.max.y += offset.y;
return true; return true;
} }

File diff suppressed because it is too large Load diff

View file

@ -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 h = bbox.h();
auto w = static_cast<uint32_t>(region.max.x - region.min.x); auto w = bbox.w();
//32bits channels //32bits channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); 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; uint32_t ialpha = 255 - c.a;
@ -145,7 +145,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, c
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize); 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; auto ialpha = ~c.a;
for (uint32_t y = 0; y < h; ++y) { for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride]; auto dst = &buffer[y * surface->stride];
@ -160,20 +160,18 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, c
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c) static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
{ {
auto span = rle->spans;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); auto color = surface->join(c.r, c.g, c.b, c.a);
uint32_t src; 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]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color; else src = color;
auto ialpha = IA(src); auto ialpha = IA(src);
//1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required) //1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
auto notAligned = ((uintptr_t)dst & 0xf) / 4; auto notAligned = ((uintptr_t)dst & 0xf) / 4;
@ -213,7 +211,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize); TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
uint8_t src; 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]; auto dst = &surface->buf8[span->y * surface->stride + span->x];
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
else src = c.a; else src = c.a;

View file

@ -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) static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
{ {
auto span = rle->spans;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); auto color = surface->join(c.r, c.g, c.b, c.a);
uint32_t src; 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]; auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage); if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color; else src = color;
@ -112,7 +110,7 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, c
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
uint8_t src; 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]; auto dst = &surface->buf8[span->y * surface->stride + span->x];
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
else src = 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 //32bits channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); 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 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]; 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); *dst = color + ALPHA_BLEND(*dst, ialpha);
} }
} }
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } 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; 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]; 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); *dst = c.a + MULTIPLY(*dst, ialpha);
} }
} }

View file

@ -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) static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
{ {
auto span = rle->spans;
//32bit channels //32bit channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); 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; uint8x8_t *vDst = nullptr;
uint16_t align; 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); if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color; else src = color;
@ -132,7 +130,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize); TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
uint8_t src; 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]; auto dst = &surface->buf8[span->y * surface->stride + span->x];
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a); if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
else src = 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 h = bbox.h();
auto w = static_cast<uint32_t>(region.max.x - region.min.x); auto w = bbox.w();
//32bits channels //32bits channels
if (surface->channelSize == sizeof(uint32_t)) { if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a); 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 ialpha = 255 - c.a;
auto vColor = vdup_n_u32(color); auto vColor = vdup_n_u32(color);
@ -185,7 +183,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
//8bit grayscale //8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) { } else if (surface->channelSize == sizeof(uint8_t)) {
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize); 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; auto ialpha = ~c.a;
for (uint32_t y = 0; y < h; ++y) { for (uint32_t y = 0; y < h; ++y) {
auto dst = &buffer[y * surface->stride]; auto dst = &buffer[y * surface->stride];

View file

@ -52,33 +52,33 @@ static float xa, xb, ua, va;
//Y Range exception handling //Y Range exception handling
static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd) static bool _arrange(const SwImage* image, const RenderRegion* bbox, int& yStart, int& yEnd)
{ {
int32_t regionTop, regionBottom; int32_t bboxTop, bboxBottom;
if (region) { if (bbox) {
regionTop = region->min.y; bboxTop = bbox->min.y;
regionBottom = region->max.y; bboxBottom = bbox->max.y;
} else { } else {
regionTop = image->rle->spans->y; bboxTop = image->rle->spans.first().y;
regionBottom = image->rle->spans[image->rle->size - 1].y; bboxBottom = image->rle->spans.last().y;
} }
if (yStart < regionTop) yStart = regionTop; if (yStart < bboxTop) yStart = bboxTop;
if (yEnd > regionBottom) yEnd = regionBottom; if (yEnd > bboxBottom) yEnd = bboxBottom;
return yEnd > yStart; return yEnd > yStart;
} }
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()"); TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
return false; 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 _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
@ -94,16 +94,16 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
uint32_t* buf; uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based. SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, region, yStart, yEnd)) return; if (!_arrange(image, bbox, yStart, yEnd)) return;
//Loop through all lines in the segment //Loop through all lines in the segment
uint32_t spanIdx = 0; uint32_t spanIdx = 0;
if (region) { if (bbox) {
minx = region->min.x; minx = bbox->min.x;
maxx = region->max.x; maxx = bbox->max.x;
} else { } else {
span = image->rle->spans; span = image->rle->data();
while (span->y < yStart) { while (span->y < yStart) {
++span; ++span;
++spanIdx; ++spanIdx;
@ -116,11 +116,11 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
x1 = (int32_t)_xa; x1 = (int32_t)_xa;
x2 = (int32_t)_xb; x2 = (int32_t)_xb;
if (!region) { if (!bbox) {
minx = INT32_MAX; minx = INT32_MAX;
maxx = 0; maxx = 0;
//one single row, could be consisted of multiple spans. //one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size) { while (span->y == y && spanIdx < image->rle->size()) {
if (minx > span->x) minx = span->x; if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len; if (maxx < span->x + span->len) maxx = span->x + span->len;
++span; ++span;
@ -195,7 +195,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!region && spanIdx >= image->rle->size) break; if (!bbox && spanIdx >= image->rle->size()) break;
++y; ++y;
} }
@ -206,7 +206,7 @@ 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 _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
@ -227,16 +227,16 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr; auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
uint8_t* cmp = nullptr; uint8_t* cmp = nullptr;
if (!_arrange(image, region, yStart, yEnd)) return; if (!_arrange(image, bbox, yStart, yEnd)) return;
//Loop through all lines in the segment //Loop through all lines in the segment
uint32_t spanIdx = 0; uint32_t spanIdx = 0;
if (region) { if (bbox) {
minx = region->min.x; minx = bbox->min.x;
maxx = region->max.x; maxx = bbox->max.x;
} else { } else {
span = image->rle->spans; span = image->rle->data();
while (span->y < yStart) { while (span->y < yStart) {
++span; ++span;
++spanIdx; ++spanIdx;
@ -249,11 +249,11 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
x1 = (int32_t)_xa; x1 = (int32_t)_xa;
x2 = (int32_t)_xb; x2 = (int32_t)_xb;
if (!region) { if (!bbox) {
minx = INT32_MAX; minx = INT32_MAX;
maxx = 0; maxx = 0;
//one single row, could be consisted of multiple spans. //one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size) { while (span->y == y && spanIdx < image->rle->size()) {
if (minx > span->x) minx = span->x; if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len; if (maxx < span->x + span->len) maxx = span->x + span->len;
++span; ++span;
@ -387,7 +387,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!region && spanIdx >= image->rle->size) break; if (!bbox && spanIdx >= image->rle->size()) break;
++y; ++y;
} }
@ -399,7 +399,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
/* This mapping algorithm is based on Mikael Kalms's. */ /* 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 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}; float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@ -460,7 +460,7 @@ 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[0], y[1])) side = x[0] > x[1];
if (tvg::equal(y[1], y[2])) side = x[2] > 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 bboxTop = bbox ? bbox->min.y : image->rle->data()->y; //Normal Image or Rle Image?
auto compositing = _compositing(surface); //Composition required auto compositing = _compositing(surface); //Composition required
auto blending = _blending(surface); //Blending required auto blending = _blending(surface); //Blending required
@ -479,7 +479,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible //Draw upper segment if possibly visible
if (yi[0] < yi[1]) { if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; off_y = y[0] < bboxTop ? (bboxTop - y[0]) : 0;
xa += (off_y * dxdya); xa += (off_y * dxdya);
ua += (off_y * dudya); ua += (off_y * dudya);
va += (off_y * dvdya); va += (off_y * dvdya);
@ -489,18 +489,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
xb = x[0] + dy * dxdyb + (off_y * dxdyb); xb = x[0] + dy * dxdyb + (off_y * dxdyb);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 1);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else { } 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; upper = true;
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
if (yi[1] < yi[2]) { if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; off_y = y[1] < bboxTop ? (bboxTop - y[1]) : 0;
if (!upper) { if (!upper) {
xa += (off_y * dxdya); xa += (off_y * dxdya);
ua += (off_y * dudya); ua += (off_y * dudya);
@ -510,12 +510,12 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
dxdyb = dxdy[2]; dxdyb = dxdy[2];
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 2);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else { } 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 //Longer edge is on the right side
@ -527,7 +527,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible //Draw upper segment if possibly visible
if (yi[0] < yi[1]) { if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; off_y = y[0] < bboxTop ? (bboxTop - y[0]) : 0;
xb += (off_y *dxdyb); xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping // Set slopes along left edge and perform subpixel pre-stepping
@ -540,18 +540,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
va = v[0] + dy * dvdya + (off_y * dvdya); va = v[0] + dy * dvdya + (off_y * dvdya);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 3);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else { } 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; upper = true;
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
if (yi[1] < yi[2]) { if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; off_y = y[1] < bboxTop ? (bboxTop - y[1]) : 0;
if (!upper) xb += (off_y *dxdyb); if (!upper) xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping // Set slopes along left edge and perform subpixel pre-stepping
@ -564,24 +564,24 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
va = v[1] + dy * dvdya + (off_y * dvdya); va = v[1] + dy * dvdya + (off_y * dvdya);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 4);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else { } 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(float ymin, float ymax, const SwImage* image, const RenderRegion* bbox)
{ {
auto yStart = static_cast<int>(ymin); auto yStart = static_cast<int>(ymin);
auto yEnd = static_cast<int>(ymax); auto yEnd = static_cast<int>(ymax);
if (!_arrange(image, region, yStart, yEnd)) return nullptr; if (!_arrange(image, bbox, yStart, yEnd)) return nullptr;
auto aaSpans = tvg::malloc<AASpans*>(sizeof(AASpans)); auto aaSpans = tvg::malloc<AASpans*>(sizeof(AASpans));
aaSpans->yStart = yStart; aaSpans->yStart = yStart;
@ -863,7 +863,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
| / | | / |
3 -- 2 3 -- 2
*/ */
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity) static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const RenderRegion* bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
@ -871,7 +871,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
} }
//Exceptions: No dedicated drawing area? //Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true; if ((!image->rle && !bbox) || (image->rle && image->rle->size() == 0)) return true;
/* Prepare vertices. /* Prepare vertices.
shift XY coordinates to match the sub-pixeling technique. */ shift XY coordinates to match the sub-pixeling technique. */
@ -888,7 +888,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
} }
auto aaSpans = _AASpans(ys, ye, image, region); auto aaSpans = _AASpans(ys, ye, image, bbox);
if (!aaSpans) return true; if (!aaSpans) return true;
Polygon polygon; Polygon polygon;
@ -898,14 +898,14 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
polygon.vertex[1] = vertices[1]; polygon.vertex[1] = vertices[1];
polygon.vertex[2] = vertices[3]; polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); _rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
//Draw the second polygon //Draw the second polygon
polygon.vertex[0] = vertices[1]; polygon.vertex[0] = vertices[1];
polygon.vertex[1] = vertices[2]; polygon.vertex[1] = vertices[2];
polygon.vertex[2] = vertices[3]; polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); _rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
#if 0 #if 0
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) { if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {

View file

@ -40,7 +40,7 @@ struct SwTask : Task
{ {
SwSurface* surface = nullptr; SwSurface* surface = nullptr;
SwMpool* mpool = nullptr; SwMpool* mpool = nullptr;
SwBBox bbox; //Rendering Region RenderRegion bbox; //Rendering Region
Matrix transform; Matrix transform;
Array<RenderData> clips; Array<RenderData> clips;
RenderUpdateFlag flags = RenderUpdateFlag::None; RenderUpdateFlag flags = RenderUpdateFlag::None;
@ -48,22 +48,11 @@ struct SwTask : Task
bool pushed = false; //Pushed into task list? bool pushed = false; //Pushed into task list?
bool disposed = false; //Disposed task? bool disposed = false; //Disposed task?
RenderRegion bounds() const RenderRegion& bounds()
{ {
//Can we skip the synchronization? //Can we skip the synchronization?
done(); done();
return bbox;
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;
} }
virtual void dispose() = 0; virtual void dispose() = 0;
@ -117,7 +106,7 @@ struct SwShapeTask : SwTask
} }
auto strokeWidth = validStrokeWidth(clipper); auto strokeWidth = validStrokeWidth(clipper);
SwBBox renderRegion{}; RenderRegion renderBox{};
auto updateShape = flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip); auto updateShape = flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip);
auto updateFill = false; auto updateFill = false;
@ -126,11 +115,11 @@ struct SwShapeTask : SwTask
updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill); updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill);
if (updateShape) shapeReset(&shape); if (updateShape) shapeReset(&shape);
if (updateFill || clipper) { 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; if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
} else { } else {
updateFill = false; updateFill = false;
renderRegion.reset(); renderBox.reset();
} }
} }
} }
@ -146,7 +135,7 @@ struct SwShapeTask : SwTask
if (updateShape || flags & RenderUpdateFlag::Stroke) { if (updateShape || flags & RenderUpdateFlag::Stroke) {
if (strokeWidth > 0.0f) { if (strokeWidth > 0.0f) {
shapeResetStroke(&shape, rshape, transform); 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()) { if (auto fill = rshape->strokeFill()) {
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
if (ctable) shapeResetStrokeFill(&shape); if (ctable) shapeResetStrokeFill(&shape);
@ -168,7 +157,7 @@ struct SwShapeTask : SwTask
if (!clipShapeRle && !clipStrokeRle) goto err; if (!clipShapeRle && !clipStrokeRle) goto err;
} }
bbox = renderRegion; //sync bbox = renderBox; //sync
return; return;
@ -199,7 +188,7 @@ struct SwImageTask : SwTask
void run(unsigned tid) override void run(unsigned tid) override
{ {
auto clipRegion = bbox; auto clipBox = bbox;
//Convert colorspace if it's not aligned. //Convert colorspace if it's not aligned.
rasterConvertCS(source, surface->cs); rasterConvertCS(source, surface->cs);
@ -216,7 +205,7 @@ struct SwImageTask : SwTask
imageReset(&image); imageReset(&image);
if (!image.data || image.w == 0 || image.h == 0) goto end; 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 (clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end; if (!imageGenRle(&image, bbox, false)) goto end;
@ -570,38 +559,19 @@ SwSurface* SwRenderer::request(int channelSize, bool square)
RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags)
{ {
auto x = region.x; auto bbox = RenderRegion::intersect(region, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}});
auto y = region.y; if (bbox.invalid()) return nullptr;
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 cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing)); 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->recoverSfc = surface;
cmp->compositor->recoverCmp = surface->compositor; cmp->compositor->recoverCmp = surface->compositor;
cmp->compositor->valid = false; cmp->compositor->valid = false;
cmp->compositor->bbox.min.x = x; cmp->compositor->bbox = bbox;
cmp->compositor->bbox.min.y = y;
cmp->compositor->bbox.max.x = x + w;
cmp->compositor->bbox.max.y = y + h;
/* TODO: Currently, only blending might work. /* TODO: Currently, only blending might work.
Blending and composition must be handled together. */ Blending and composition must be handled together. */
auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000; 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 //Switch render target
surface = cmp; surface = cmp;
@ -731,16 +701,13 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
static_cast<SwTask*>(*p)->done(); static_cast<SwTask*>(*p)->done();
} }
task->clips = clips;
task->transform = transform;
task->opacity = opacity;
task->surface = surface; task->surface = surface;
task->mpool = mpool; 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; 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) { if (!task->pushed) {
task->pushed = true; task->pushed = true;

View file

@ -324,27 +324,18 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
if (!rw.antiAlias) coverage = 255; if (!rw.antiAlias) coverage = 255;
//see whether we can add this span to the current list //see whether we can add this span to the current list
if (rle->size > 0) { if (!rle->spans.empty()) {
auto span = rle->spans + rle->size - 1; auto& span = rle->spans.last();
if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) { if ((span.coverage == coverage) && (span.y == y) && (span.x + span.len == x)) {
//Clip x range //Clip x range
SwCoord xOver = 0; SwCoord xOver = 0;
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x); if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
span->len += (aCount + xOver); span.len += (aCount + xOver);
return; 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 //Clip x range
SwCoord xOver = 0; SwCoord xOver = 0;
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x); 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; if (aCount + xOver <= 0) return;
//add a span to the current list //add a span to the current list
auto span = rle->spans + rle->size; rle->spans.next() = {(uint16_t)x, (uint16_t)y, uint16_t(aCount + xOver), (uint8_t)coverage};
span->x = x;
span->y = y;
span->len = (aCount + xOver);
span->coverage = coverage;
rle->size++;
} }
@ -737,114 +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 && temp->y == clipSpans->y) {
if (outSpansCnt == 0) {
TVGERR("SW_ENGINE", "span buffer is over.");
break;
}
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 */ /* 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; if (!outline) return nullptr;
@ -865,8 +748,8 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
rw.area = 0; rw.area = 0;
rw.cover = 0; rw.cover = 0;
rw.invalid = true; rw.invalid = true;
rw.cellMin = renderRegion.min; rw.cellMin = {bbox.min.x, bbox.min.y};
rw.cellMax = renderRegion.max; rw.cellMax = {bbox.max.x, bbox.max.y};
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x; rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y; rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.outline = const_cast<SwOutline*>(outline); rw.outline = const_cast<SwOutline*>(outline);
@ -874,8 +757,9 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
rw.bandShoot = 0; rw.bandShoot = 0;
rw.antiAlias = antiAlias; rw.antiAlias = antiAlias;
if (!rle) rw.rle = tvg::calloc<SwRle*>(1, sizeof(SwRle)); if (!rle) rw.rle = new SwRle;
else rw.rle = rle; else rw.rle = rle;
rw.rle->spans.reserve(256);
//Generate RLE //Generate RLE
Band bands[BAND_SIZE]; Band bands[BAND_SIZE];
@ -938,7 +822,10 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
/* This is too complex for a single scanline; there must /* This is too complex for a single scanline; there must
be some problems */ be some problems */
if (middle == bottom) goto error; if (middle == bottom) {
rleFree(rw.rle);
return nullptr;
}
if (bottom - top >= rw.bandSize) ++rw.bandShoot; if (bottom - top >= rw.bandSize) ++rw.bandShoot;
@ -949,34 +836,26 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
++band; ++band;
} }
} }
if (rw.bandShoot > 8 && rw.bandSize > 16) {
if (rw.bandShoot > 8 && rw.bandSize > 16)
rw.bandSize = (rw.bandSize >> 1); rw.bandSize = (rw.bandSize >> 1);
}
return rw.rle; 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 rle = tvg::calloc<SwRle*>(sizeof(SwRle), 1);
auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y); rle->spans.reserve(bbox->h());
rle->spans.count = bbox->h();
auto rle = tvg::malloc<SwRle*>(sizeof(SwRle)); //cheaper without push()
rle->spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * height); auto x = uint16_t(bbox->min.x);
rle->size = height; auto y = uint16_t(bbox->min.y);
rle->alloc = height; auto len = uint16_t(bbox->w());
auto span = rle->spans; ARRAY_FOREACH(p, rle->spans) {
for (uint16_t i = 0; i < height; ++i, ++span) { *p = {x, y++, len, 255};
span->x = bbox->min.x;
span->y = bbox->min.y + i;
span->len = width;
span->coverage = 255;
} }
return rle; return rle;
@ -985,44 +864,88 @@ SwRle* rleRender(const SwBBox* bbox)
void rleReset(SwRle* rle) void rleReset(SwRle* rle)
{ {
if (!rle) return; if (rle) rle->spans.clear();
rle->size = 0;
} }
void rleFree(SwRle* rle) void rleFree(SwRle* rle)
{ {
if (!rle) return; delete(rle);
if (rle->spans) tvg::free(rle->spans);
tvg::free(rle);
} }
bool rleClip(SwRle *rle, const SwRle *clip) bool rleClip(SwRle *rle, const SwRle *clip)
{ {
if (rle->size == 0 || clip->size == 0) return false; if (rle->spans.empty() || clip->spans.empty()) return false;
auto spanCnt = 2 * (rle->size > clip->size ? rle->size : clip->size); //factor 2 added for safety (no real cases observed where the factor exceeded 1.4)
auto spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * (spanCnt));
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
_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; 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; if (rle->spans.empty() || clip->invalid()) return false;
auto spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * (rle->size));
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
_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; return true;
} }

View file

@ -419,13 +419,13 @@ static SwOutline* _genOutline(SwShape* shape, const RenderShape* rshape, const M
/* External Class Implementation */ /* 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; if (auto out = _genOutline(shape, rshape, transform, mpool, tid, hasComposite, rshape->trimpath())) shape->outline = out;
else return false; 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; 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* shapeOutline = nullptr;
SwOutline* strokeOutline = nullptr; SwOutline* strokeOutline = nullptr;
@ -528,12 +528,12 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix&
strokeOutline = strokeExportOutline(shape->stroke, mpool, tid); strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) { if (!mathUpdateOutlineBBox(strokeOutline, clipBox, renderBox, false)) {
ret = false; ret = false;
goto clear; goto clear;
} }
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true); shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderBox, true);
clear: clear:
if (dashStroking) mpoolRetDashOutline(mpool, tid); if (dashStroking) mpoolRetDashOutline(mpool, tid);

View file

@ -31,7 +31,7 @@ struct Canvas::Impl
{ {
Scene* scene; Scene* scene;
RenderMethod* renderer; RenderMethod* renderer;
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; RenderRegion vport = {{0, 0}, {INT32_MAX, INT32_MAX}};
Status status = Status::Synced; Status status = Status::Synced;
Impl() : scene(Scene::gen()) Impl() : scene(Scene::gen())
@ -119,11 +119,11 @@ struct Canvas::Impl
{ {
if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition; 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. //intersect if the target buffer is already set.
auto surface = renderer->mainSurface(); auto surface = renderer->mainSurface();
if (surface && surface->w > 0 && surface->h > 0) { 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; if (vport == val) return Result::Success;
renderer->viewport(val); renderer->viewport(val);

View file

@ -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) return Result::MemoryCorruption;
if (!renderer->target(context, id, w, h)) return Result::Unknown; 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); renderer->viewport(pImpl->vport);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.

View file

@ -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; 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 //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]) { 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; return Result::Success;
//figure out if the clipper is totally outside of the viewport //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]) { } 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::Success;
} }
return Result::InsufficientCondition; 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.x > v2.x) std::swap(v1.x, v2.x);
if (v1.y > v2.y) std::swap(v1.y, v2.y); if (v1.y > v2.y) std::swap(v1.y, v2.y);
after.x = static_cast<int32_t>(nearbyint(v1.x)); after.min.x = static_cast<int32_t>(nearbyint(v1.x));
after.y = static_cast<int32_t>(nearbyint(v1.y)); after.min.y = static_cast<int32_t>(nearbyint(v1.y));
after.w = static_cast<int32_t>(nearbyint(v2.x)) - after.x; after.max.x = static_cast<int32_t>(nearbyint(v2.x));
after.h = static_cast<int32_t>(nearbyint(v2.y)) - after.y; after.max.y = static_cast<int32_t>(nearbyint(v2.y));
if (after.w < 0) after.w = 0; if (after.max.x < after.min.x) after.max.x = after.min.x;
if (after.h < 0) after.h = 0; if (after.max.y < after.min.y) after.max.y = after.min.y;
after.intersect(before); after.intersect(before);
renderer->viewport(after); renderer->viewport(after);
@ -168,7 +168,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
ret->pImpl->opacity = opacity; ret->pImpl->opacity = opacity;
if (maskData) ret->mask(maskData->target->duplicate(), maskData->method); 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; return ret;
} }
@ -185,7 +185,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
PAINT_METHOD(region, bounds(renderer)); PAINT_METHOD(region, bounds(renderer));
if (MASK_REGION_MERGING(maskData->method)) region.add(PAINT(maskData->target)->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); cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking);
if (renderer->beginComposite(cmp, MaskMethod::None, 255)) { if (renderer->beginComposite(cmp, MaskMethod::None, 255)) {
maskData->target->pImpl->render(renderer); 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); return pImpl->clip(clipper);
} }
Shape* Paint::clip() const noexcept
{
return pImpl->clipper;
}
Result Paint::mask(Paint* target, MaskMethod method) noexcept Result Paint::mask(Paint* target, MaskMethod method) noexcept
{ {
return pImpl->mask(target, method); return pImpl->mask(target, method);

View file

@ -54,7 +54,7 @@ namespace tvg
Paint* paint = nullptr; Paint* paint = nullptr;
Paint* parent = nullptr; Paint* parent = nullptr;
Mask* maskData = nullptr; Mask* maskData = nullptr;
Paint* clipper = nullptr; Shape* clipper = nullptr;
RenderMethod* renderer = nullptr; RenderMethod* renderer = nullptr;
RenderData rd = nullptr; RenderData rd = nullptr;
@ -163,7 +163,7 @@ namespace tvg
return tm; return tm;
} }
Result clip(Paint* clp) Result clip(Shape* clp)
{ {
if (clp && PAINT(clp)->parent) return Result::InsufficientCondition; if (clp && PAINT(clp)->parent) return Result::InsufficientCondition;
if (clipper) PAINT(clipper)->unref(clipper != clp); if (clipper) PAINT(clipper)->unref(clipper != clp);

View file

@ -253,7 +253,7 @@ struct PictureImpl : Picture
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
bool ret = false; auto ret = true;
renderer->blend(impl.blendMethod); renderer->blend(impl.blendMethod);
if (bitmap) return renderer->renderImage(impl.rd); if (bitmap) return renderer->renderImage(impl.rd);
@ -273,7 +273,7 @@ struct PictureImpl : Picture
{ {
if (impl.rd) return renderer->region(impl.rd); if (impl.rd) return renderer->region(impl.rd);
if (vector) return vector->pImpl->bounds(renderer); if (vector) return vector->pImpl->bounds(renderer);
return {0, 0, 0, 0}; return {};
} }
Result load(ImageLoader* loader) Result load(ImageLoader* loader)

View file

@ -106,33 +106,14 @@ bool RenderPath::bounds(Matrix* m, float* x, float* y, float* w, float* h)
void RenderRegion::intersect(const RenderRegion& rhs) void RenderRegion::intersect(const RenderRegion& rhs)
{ {
auto x1 = x + w; if (min.x < rhs.min.x) min.x = rhs.min.x;
auto y1 = y + h; if (min.y < rhs.min.y) min.y = rhs.min.y;
auto x2 = rhs.x + rhs.w; if (max.x > rhs.max.x) max.x = rhs.max.x;
auto y2 = rhs.y + rhs.h; if (max.y > rhs.max.y) max.y = rhs.max.y;
x = (x > rhs.x) ? x : rhs.x; // Not intersected: collapse to zero-area region
y = (y > rhs.y) ? y : rhs.y; if (max.x < min.x) max.x = min.x;
w = ((x1 < x2) ? x1 : x2) - x; if (max.y < min.y) max.y = min.y;
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;
} }
/************************************************************************/ /************************************************************************/

View file

@ -94,16 +94,47 @@ struct RenderCompositor
struct RenderRegion 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)
{
return {{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)}};
}
void intersect(const RenderRegion& rhs); 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 bool operator==(const RenderRegion& rhs) const
{ {
if (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h) return true; return (min.x == rhs.min.x && min.y == rhs.min.y && max.x == rhs.max.x && max.y == rhs.max.y);
return false;
} }
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 struct RenderPath
@ -272,7 +303,7 @@ struct RenderShape
struct RenderEffect struct RenderEffect
{ {
RenderData rd = nullptr; RenderData rd = nullptr;
RenderRegion extend = {0, 0, 0, 0}; RenderRegion extend{};
SceneEffect type; SceneEffect type;
bool valid = false; bool valid = false;
@ -353,7 +384,7 @@ struct RenderEffectTint : RenderEffect
inst->white[0] = va_arg(args, int); inst->white[0] = va_arg(args, int);
inst->white[1] = va_arg(args, int); inst->white[1] = va_arg(args, int);
inst->white[2] = 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; inst->type = SceneEffect::Tint;
return inst; return inst;
} }

View file

@ -64,7 +64,7 @@ struct SceneImpl : Scene
{ {
Paint::Impl impl; Paint::Impl impl;
list<Paint*> paints; //children list list<Paint*> paints; //children list
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; RenderRegion vport = {};
Array<RenderEffect*>* effects = nullptr; Array<RenderEffect*>* effects = nullptr;
uint8_t compFlag = CompositionFlag::Invalid; uint8_t compFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition
@ -159,40 +159,34 @@ struct SceneImpl : Scene
RenderRegion bounds(RenderMethod* renderer) const RenderRegion bounds(RenderMethod* renderer) const
{ {
if (paints.empty()) return {0, 0, 0, 0}; if (paints.empty()) return {};
int32_t x1 = INT32_MAX;
int32_t y1 = INT32_MAX;
int32_t x2 = 0;
int32_t y2 = 0;
//Merge regions
RenderRegion pRegion = {{INT32_MAX, INT32_MAX}, {0, 0}};
for (auto paint : paints) { for (auto paint : paints) {
auto region = paint->pImpl->bounds(renderer); auto region = paint->pImpl->bounds(renderer);
if (region.min.x < pRegion.min.x) pRegion.min.x = region.min.x;
//Merge regions if (pRegion.max.x < region.max.x) pRegion.max.x = region.max.x;
if (region.x < x1) x1 = region.x; if (region.min.y < pRegion.min.y) pRegion.min.y = region.min.y;
if (x2 < region.x + region.w) x2 = (region.x + region.w); if (pRegion.max.y < region.max.y) pRegion.max.y = region.max.y;
if (region.y < y1) y1 = region.y;
if (y2 < region.y + region.h) y2 = (region.y + region.h);
} }
//Extends the render region if post effects require //Extends the render region if post effects require
int32_t ex = 0, ey = 0, ew = 0, eh = 0; RenderRegion eRegion{};
if (effects) { if (effects) {
ARRAY_FOREACH(p, *effects) { ARRAY_FOREACH(p, *effects) {
auto effect = *p; auto effect = *p;
if (effect->valid && renderer->region(effect)) { if (effect->valid && renderer->region(effect)) eRegion.add(effect->extend);
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);
}
} }
} }
auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh}; pRegion.min.x += eRegion.min.x;
ret.intersect(this->vport); pRegion.min.y += eRegion.min.y;
return ret; pRegion.max.x += eRegion.max.x;
pRegion.max.y += eRegion.max.y;
pRegion.intersect(this->vport);
return pRegion;
} }
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking) Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)

View file

@ -114,7 +114,7 @@ struct ShapeImpl : Shape
RenderRegion bounds(RenderMethod* renderer) RenderRegion bounds(RenderMethod* renderer)
{ {
if (!impl.rd) return {0, 0, 0, 0}; if (!impl.rd) return {};
return renderer->region(impl.rd); return renderer->region(impl.rd);
} }

View file

@ -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) return Result::MemoryCorruption;
if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments; 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); renderer->viewport(pImpl->vport);
//FIXME: The value must be associated with an individual canvas instance. //FIXME: The value must be associated with an individual canvas instance.

View file

@ -55,7 +55,7 @@ Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w,
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
if (!renderer->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown; 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); renderer->viewport(pImpl->vport);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.

View file

@ -7,6 +7,7 @@ source_file = [
'tvgWgRenderData.h', 'tvgWgRenderData.h',
'tvgWgRenderer.h', 'tvgWgRenderer.h',
'tvgWgRenderTarget.h', 'tvgWgRenderTarget.h',
'tvgWgRenderTask.h',
'tvgWgShaderSrc.h', 'tvgWgShaderSrc.h',
'tvgWgShaderTypes.h', 'tvgWgShaderTypes.h',
'tvgWgBindGroups.cpp', 'tvgWgBindGroups.cpp',
@ -17,6 +18,7 @@ source_file = [
'tvgWgRenderData.cpp', 'tvgWgRenderData.cpp',
'tvgWgRenderer.cpp', 'tvgWgRenderer.cpp',
'tvgWgRenderTarget.cpp', 'tvgWgRenderTarget.cpp',
'tvgWgRenderTask.cpp',
'tvgWgShaderSrc.cpp', 'tvgWgShaderSrc.cpp',
'tvgWgShaderTypes.cpp' 'tvgWgShaderTypes.cpp'
] ]

View file

@ -268,4 +268,35 @@ void WgContext::releaseQueue(WGPUQueue& queue)
wgpuQueueRelease(queue); wgpuQueueRelease(queue);
queue = nullptr; 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;
}

View file

@ -71,10 +71,12 @@ struct WgContext {
// release buffer objects // release buffer objects
void releaseBuffer(WGPUBuffer& buffer); void releaseBuffer(WGPUBuffer& buffer);
bool invalid() // command encoder
{ WGPUCommandEncoder createCommandEncoder();
return !instance || !device; void submitCommandEncoder(WGPUCommandEncoder encoder);
} void releaseCommandEncoder(WGPUCommandEncoder& encoder);
bool invalid();
}; };
#endif // _TVG_WG_COMMON_H_ #endif // _TVG_WG_COMMON_H_

View file

@ -28,6 +28,7 @@ void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t heigh
{ {
// pipelines (external handle, do not release) // pipelines (external handle, do not release)
pipelines.initialize(context); pipelines.initialize(context);
stageBuffer.initialize(context);
// initialize opacity pool // initialize opacity pool
initPools(context); initPools(context);
// allocate global view matrix handles // allocate global view matrix handles
@ -37,7 +38,7 @@ void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t heigh
// create render targets handles // create render targets handles
resize(context, width, height); resize(context, width, height);
// composition and blend geometries // composition and blend geometries
meshData.blitBox(context); meshDataBlit.blitBox(context);
} }
@ -54,7 +55,7 @@ void WgCompositor::initPools(WgContext& context)
void WgCompositor::release(WgContext& context) void WgCompositor::release(WgContext& context)
{ {
// composition and blend geometries // composition and blend geometries
meshData.release(context); meshDataBlit.release(context);
// release render targets habdles // release render targets habdles
resize(context, 0, 0); resize(context, 0, 0);
// release opacity pool // release opacity pool
@ -62,6 +63,8 @@ void WgCompositor::release(WgContext& context)
// release global view matrix handles // release global view matrix handles
context.layouts.releaseBindGroup(bindGroupViewMat); context.layouts.releaseBindGroup(bindGroupViewMat);
context.releaseBuffer(bufferViewMat); context.releaseBuffer(bufferViewMat);
// release stage buffer
stageBuffer.release(context);
// release pipelines // release pipelines
pipelines.release(context); pipelines.release(context);
} }
@ -81,9 +84,9 @@ void WgCompositor::resize(WgContext& context, uint32_t width, uint32_t height) {
// release existig handles // release existig handles
if ((this->width != width) || (this->height != height)) { if ((this->width != width) || (this->height != height)) {
context.layouts.releaseBindGroup(bindGroupStorageTemp); context.layouts.releaseBindGroup(bindGroupStorageTemp);
// release intermediate render storages // release intermediate render target
storageTemp1.release(context); targetTemp1.release(context);
storageTemp0.release(context); targetTemp0.release(context);
// release global stencil buffer handles // release global stencil buffer handles
context.releaseTextureView(texViewDepthStencilMS); context.releaseTextureView(texViewDepthStencilMS);
context.releaseTexture(texDepthStencilMS); context.releaseTexture(texDepthStencilMS);
@ -107,54 +110,58 @@ void WgCompositor::resize(WgContext& context, uint32_t width, uint32_t height) {
texViewDepthStencil = context.createTextureView(texDepthStencil); texViewDepthStencil = context.createTextureView(texDepthStencil);
texDepthStencilMS = context.createTexAttachement(width, height, WGPUTextureFormat_Depth24PlusStencil8, 4); texDepthStencilMS = context.createTexAttachement(width, height, WGPUTextureFormat_Depth24PlusStencil8, 4);
texViewDepthStencilMS = context.createTextureView(texDepthStencilMS); texViewDepthStencilMS = context.createTextureView(texDepthStencilMS);
// initialize intermediate render storages // initialize intermediate render targets
storageTemp0.initialize(context, width, height); targetTemp0.initialize(context, width, height);
storageTemp1.initialize(context, width, height); targetTemp1.initialize(context, width, height);
bindGroupStorageTemp = context.layouts.createBindGroupStrorage2RO(storageTemp0.texView, storageTemp1.texView); bindGroupStorageTemp = context.layouts.createBindGroupStrorage2RO(targetTemp0.texView, targetTemp1.texView);
} }
} }
RenderRegion WgCompositor::shrinkRenderRegion(RenderRegion& rect) RenderRegion WgCompositor::shrinkRenderRegion(RenderRegion& rect)
{ {
// cut viewport to screen dimensions return RenderRegion::intersect(rect, {{0, 0}, {(int32_t)width, (int32_t)height}});
int32_t xmin = std::max(0, std::min((int32_t)width, rect.x));
int32_t ymin = std::max(0, std::min((int32_t)height, rect.y));
int32_t xmax = std::max(xmin, std::min((int32_t)width, rect.x + rect.w));
int32_t ymax = std::max(ymin, std::min((int32_t)height, rect.y + rect.h));
return { xmin, ymin, xmax - xmin, ymax - ymin };
} }
void WgCompositor::copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src) void WgCompositor::copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src)
{ {
const RenderRegion region = { 0, 0, (int32_t)src->width, (int32_t)src->height }; const RenderRegion region = {{0, 0}, {(int32_t)src->width, (int32_t)src->height}};
copyTexture(dst, src, region); copyTexture(dst, src, region);
} }
void WgCompositor::copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src, const RenderRegion& region) void WgCompositor::copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src, const RenderRegion& region)
{ {
assert(dst); assert(dst);
assert(src); assert(src);
assert(commandEncoder); assert(commandEncoder);
const WGPUImageCopyTexture texSrc { .texture = src->texture, .origin = { .x = (uint32_t)region.x, .y = (uint32_t)region.y } }; const WGPUImageCopyTexture texSrc { .texture = src->texture, .origin = { .x = region.x(), .y = region.y() } };
const WGPUImageCopyTexture texDst { .texture = dst->texture, .origin = { .x = (uint32_t)region.x, .y = (uint32_t)region.y } }; const WGPUImageCopyTexture texDst { .texture = dst->texture, .origin = { .x = region.x(), .y = region.y() } };
const WGPUExtent3D copySize { .width = (uint32_t)region.w, .height = (uint32_t)region.h, .depthOrArrayLayers = 1 }; const WGPUExtent3D copySize { .width = region.w(), .height = region.h(), .depthOrArrayLayers = 1 };
wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, &copySize); wgpuCommandEncoderCopyTextureToTexture(commandEncoder, &texSrc, &texDst, &copySize);
} }
void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderStorage* target, bool clear, WGPUColor clearColor) void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderTarget* target, bool clear, WGPUColor clearColor)
{ {
assert(commandEncoder);
assert(target); assert(target);
assert(commandEncoder);
// do not start same render bass
if (target == currentTarget) return;
// we must to end render pass first
endRenderPass();
this->currentTarget = target; this->currentTarget = target;
// start new render pass
this->commandEncoder = commandEncoder; this->commandEncoder = commandEncoder;
const WGPURenderPassDepthStencilAttachment depthStencilAttachment{ const WGPURenderPassDepthStencilAttachment depthStencilAttachment{
.view = texViewDepthStencilMS, .view = texViewDepthStencilMS,
.depthLoadOp = WGPULoadOp_Clear, .depthStoreOp = WGPUStoreOp_Discard, .depthClearValue = 1.0f, .depthLoadOp = WGPULoadOp_Clear,
.stencilLoadOp = WGPULoadOp_Clear, .stencilStoreOp = WGPUStoreOp_Discard, .stencilClearValue = 0 .depthStoreOp = WGPUStoreOp_Discard,
.depthClearValue = 1.0f,
.stencilLoadOp = WGPULoadOp_Clear,
.stencilStoreOp = WGPUStoreOp_Discard,
.stencilClearValue = 0
}; };
const WGPURenderPassColorAttachment colorAttachment{ const WGPURenderPassColorAttachment colorAttachment{
.view = target->texViewMS, .view = target->texViewMS,
@ -163,7 +170,6 @@ void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderSt
.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load, .loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load,
.storeOp = WGPUStoreOp_Store, .storeOp = WGPUStoreOp_Store,
.clearValue = clearColor .clearValue = clearColor
}; };
WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment }; WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
renderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc); renderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
@ -177,11 +183,37 @@ void WgCompositor::endRenderPass()
assert(renderPassEncoder); assert(renderPassEncoder);
wgpuRenderPassEncoderEnd(renderPassEncoder); wgpuRenderPassEncoderEnd(renderPassEncoder);
wgpuRenderPassEncoderRelease(renderPassEncoder); wgpuRenderPassEncoderRelease(renderPassEncoder);
this->renderPassEncoder = nullptr; renderPassEncoder = nullptr;
this->currentTarget = nullptr; currentTarget = nullptr;
} }
} }
void WgCompositor::reset(WgContext& context)
{
stageBuffer.clear();
}
void WgCompositor::flush(WgContext& context)
{
stageBuffer.append(&meshDataBlit);
stageBuffer.flush(context);
}
void WgCompositor::requestShape(WgRenderDataShape* renderData)
{
stageBuffer.append(renderData);
// TODO: expand for fill settings
}
void WgCompositor::requestImage(WgRenderDataPicture* renderData)
{
stageBuffer.append(renderData);
// TODO: expand for fill settings
}
void WgCompositor::renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod) void WgCompositor::renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod)
{ {
@ -237,7 +269,7 @@ void WgCompositor::renderImage(WgContext& context, WgRenderDataPicture* renderDa
} }
void WgCompositor::renderScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose) void WgCompositor::renderScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose)
{ {
assert(scene); assert(scene);
assert(compose); assert(compose);
@ -250,27 +282,31 @@ void WgCompositor::renderScene(WgContext& context, WgRenderStorage* scene, WgCom
} }
void WgCompositor::composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* cmp) void WgCompositor::composeScene(WgContext& context, WgRenderTarget* src, WgRenderTarget* mask, WgCompose* cmp)
{ {
assert(cmp); assert(cmp);
assert(src); assert(src);
assert(mask); assert(mask);
assert(renderPassEncoder); assert(renderPassEncoder);
RenderRegion rect = shrinkRenderRegion(cmp->aabb); RenderRegion rect = shrinkRenderRegion(cmp->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x(), rect.y(), rect.w(), rect.h());
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene_compose[(uint32_t)cmp->method]); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene_compose[(uint32_t)cmp->method]);
meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &meshDataBlit);
} }
void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView) { void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderTarget* src, WGPUTextureView dstView)
{
assert(!renderPassEncoder);
const WGPURenderPassDepthStencilAttachment depthStencilAttachment{ const WGPURenderPassDepthStencilAttachment depthStencilAttachment{
.view = texViewDepthStencil, .view = texViewDepthStencil,
.depthLoadOp = WGPULoadOp_Load, .depthStoreOp = WGPUStoreOp_Discard, .depthLoadOp = WGPULoadOp_Load,
.stencilLoadOp = WGPULoadOp_Load, .stencilStoreOp = WGPUStoreOp_Discard .depthStoreOp = WGPUStoreOp_Discard,
.stencilLoadOp = WGPULoadOp_Load,
.stencilStoreOp = WGPUStoreOp_Discard
}; };
const WGPURenderPassColorAttachment colorAttachment { const WGPURenderPassColorAttachment colorAttachment {
.view = dstView, .view = dstView,
@ -279,24 +315,63 @@ void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRender
.storeOp = WGPUStoreOp_Store, .storeOp = WGPUStoreOp_Store,
}; };
const WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment }; const WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc); renderPassEncoder = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderSetBindGroup(renderPass, 0, src->bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPass, pipelines.blit); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.blit);
meshData.drawImage(context, renderPass); drawMeshImage(context, &meshDataBlit);
wgpuRenderPassEncoderEnd(renderPass); wgpuRenderPassEncoderEnd(renderPassEncoder);
wgpuRenderPassEncoderRelease(renderPass); wgpuRenderPassEncoderRelease(renderPassEncoder);
renderPassEncoder = nullptr;
} }
void WgCompositor::drawMesh(WgContext& context, WgMeshData* meshData)
{
assert(meshData);
assert(renderPassEncoder);
uint64_t icount = meshData->ibuffer.count;
uint64_t vsize = meshData->vbuffer.count * sizeof(Point);
uint64_t isize = icount * sizeof(uint32_t);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBuffer.vbuffer_gpu, meshData->voffset, vsize);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, stageBuffer.ibuffer_gpu, WGPUIndexFormat_Uint32, meshData->ioffset, isize);
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0);
};
void WgCompositor::drawMeshFan(WgContext& context, WgMeshData* meshData)
{
assert(meshData);
assert(renderPassEncoder);
uint64_t icount = (meshData->vbuffer.count - 2) * 3;
uint64_t vsize = meshData->vbuffer.count * sizeof(Point);
uint64_t isize = icount * sizeof(uint32_t);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBuffer.vbuffer_gpu, meshData->voffset, vsize);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, context.bufferIndexFan, WGPUIndexFormat_Uint32, 0, isize);
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0);
};
void WgCompositor::drawMeshImage(WgContext& context, WgMeshData* meshData)
{
assert(meshData);
assert(renderPassEncoder);
uint64_t icount = meshData->ibuffer.count;
uint64_t vsize = meshData->vbuffer.count * sizeof(Point);
uint64_t isize = icount * sizeof(uint32_t);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 0, stageBuffer.vbuffer_gpu, meshData->voffset, vsize);
wgpuRenderPassEncoderSetVertexBuffer(renderPassEncoder, 1, stageBuffer.vbuffer_gpu, meshData->toffset, vsize);
wgpuRenderPassEncoderSetIndexBuffer(renderPassEncoder, stageBuffer.ibuffer_gpu, WGPUIndexFormat_Uint32, meshData->ioffset, isize);
wgpuRenderPassEncoderDrawIndexed(renderPassEncoder, icount, 1, 0, 0, 0);
};
void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData) void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
{ {
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count); assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) return; if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
if (renderData->meshGroupShapes.meshes.count == 0) return; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// setup stencil rules // setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
@ -305,7 +380,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass) // draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes) ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder); drawMeshFan(context, (*p));
// setup fill rules // setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -322,7 +397,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
} }
// draw to color (second pass) // draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
} }
@ -331,16 +406,14 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count); assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) return; if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
if (renderData->meshGroupShapes.meshes.count == 0) return; // copy current render target data to dst target
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; WgRenderTarget *target = currentTarget;
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
endRenderPass(); endRenderPass();
copyTexture(&storageTemp0, target); copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false); beginRenderPass(commandEncoder, target, false);
// render shape with blend settings // render shape with blend settings
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// setup stencil rules // setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
@ -349,12 +422,12 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass) // draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes) ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder); drawMeshFan(context, (*p));
// setup fill rules // setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageTemp0.bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
uint32_t blendMethodInd = (uint32_t)blendMethod; uint32_t blendMethodInd = (uint32_t)blendMethod;
WgRenderSettings& settings = renderData->renderSettingsShape; WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid) { if (settings.fillType == WgRenderSettingsType::Solid) {
@ -368,7 +441,7 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
} }
// draw to color (second pass) // draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
} }
@ -377,10 +450,9 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count); assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) return; if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// setup stencil rules // setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
@ -389,12 +461,12 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass) // draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes) ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder); drawMeshFan(context, (*p));
// merge depth and stencil buffer // merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
// setup fill rules // setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -411,7 +483,7 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
} }
// draw to color (second pass) // draw to color (second pass)
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
} }
@ -420,10 +492,9 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count); assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip) return; if (renderData->renderSettingsStroke.skip || renderData->meshGroupStrokes.meshes.count == 0 || renderData->viewport.invalid()) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// draw strokes to stencil (first pass) // draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) { for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules // setup stencil rules
@ -432,7 +503,7 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass) // draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder); drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// setup fill rules // setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -449,7 +520,7 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
} }
// draw to color (second pass) // draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder); drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
} }
} }
@ -459,15 +530,14 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count); assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip) return; if (renderData->renderSettingsStroke.skip || renderData->meshGroupStrokes.meshes.count == 0 || renderData->viewport.invalid()) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; // copy current render target data to dst target
// copy current render target data to dst storage WgRenderTarget *target = currentTarget;
WgRenderStorage *target = currentTarget;
endRenderPass(); endRenderPass();
copyTexture(&storageTemp0, target); copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false); beginRenderPass(commandEncoder, target, false);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// draw strokes to stencil (first pass) // draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) { for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules // setup stencil rules
@ -476,12 +546,12 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass) // draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder); drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// setup fill rules // setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageTemp0.bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
uint32_t blendMethodInd = (uint32_t)blendMethod; uint32_t blendMethodInd = (uint32_t)blendMethod;
WgRenderSettings& settings = renderData->renderSettingsStroke; WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid) { if (settings.fillType == WgRenderSettingsType::Solid) {
@ -495,7 +565,7 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
} }
// draw to color (second pass) // draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder); drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
} }
}; };
@ -507,8 +577,8 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count); assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip) return; if (renderData->renderSettingsStroke.skip) return;
if (renderData->meshGroupStrokes.meshes.count == 0) return; if (renderData->meshGroupStrokes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// draw strokes to stencil (first pass) // draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) { for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules // setup stencil rules
@ -517,12 +587,12 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass) // draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder); drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// merge depth and stencil buffer // merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
// setup fill rules // setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -539,7 +609,7 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
} }
// draw to color (second pass) // draw to color (second pass)
renderData->meshGroupStrokesBBox.meshes[i]->drawFan(context, renderPassEncoder); drawMeshFan(context, renderData->meshGroupStrokesBBox.meshes[i]);
} }
} }
@ -548,21 +618,21 @@ void WgCompositor::drawImage(WgContext& context, WgRenderDataPicture* renderData
{ {
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// draw stencil // draw stencil
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &renderData->meshData);
// draw image // draw image
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image);
renderData->meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &renderData->meshData);
} }
@ -570,28 +640,28 @@ void WgCompositor::blendImage(WgContext& context, WgRenderDataPicture* renderDat
{ {
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// copy current render target data to dst storage // copy current render target data to dst target
WgRenderStorage *target = currentTarget; WgRenderTarget *target = currentTarget;
endRenderPass(); endRenderPass();
copyTexture(&storageTemp0, target); copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false); beginRenderPass(commandEncoder, target, false);
// setup stencil rules // setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &renderData->meshData);
// blend image // blend image
uint32_t blendMethodInd = (uint32_t)blendMethod; uint32_t blendMethodInd = (uint32_t)blendMethod;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, storageTemp0.bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 3, targetTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image_blend[blendMethodInd]); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image_blend[blendMethodInd]);
renderData->meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &renderData->meshData);
}; };
@ -599,65 +669,65 @@ void WgCompositor::clipImage(WgContext& context, WgRenderDataPicture* renderData
{ {
assert(renderData); assert(renderData);
assert(renderPassEncoder); assert(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return; if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// setup stencil rules // setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &renderData->meshData);
// merge depth and stencil buffer // merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &renderData->meshData);
// draw image // draw image
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.image);
renderData->meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &renderData->meshData);
} }
void WgCompositor::drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose) void WgCompositor::drawScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose)
{ {
assert(scene); assert(scene);
assert(compose); assert(compose);
assert(currentTarget); assert(currentTarget);
// draw scene // draw scene
RenderRegion rect = shrinkRenderRegion(compose->aabb); RenderRegion rect = shrinkRenderRegion(compose->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x(), rect.y(), rect.w(), rect.h());
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, bindGroupOpacities[compose->opacity], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, bindGroupOpacities[compose->opacity], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene);
meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &meshDataBlit);
} }
void WgCompositor::blendScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose) void WgCompositor::blendScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose)
{ {
assert(scene); assert(scene);
assert(compose); assert(compose);
assert(currentTarget); assert(currentTarget);
// copy current render target data to dst storage // copy current render target data to dst target
WgRenderStorage *target = currentTarget; WgRenderTarget *target = currentTarget;
endRenderPass(); endRenderPass();
copyTexture(&storageTemp0, target); copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false); beginRenderPass(commandEncoder, target, false);
// blend scene // blend scene
uint32_t blendMethodInd = (uint32_t)compose->blend; uint32_t blendMethodInd = (uint32_t)compose->blend;
RenderRegion rect = shrinkRenderRegion(compose->aabb); RenderRegion rect = shrinkRenderRegion(compose->aabb);
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x, rect.y, rect.w, rect.h); wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, rect.x(), rect.y(), rect.w(), rect.h());
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, storageTemp0.bindGroupTexure, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, targetTemp0.bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[compose->opacity], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[compose->opacity], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene_blend[blendMethodInd]); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.scene_blend[blendMethodInd]);
meshData.drawImage(context, renderPassEncoder); drawMeshImage(context, &meshDataBlit);
} }
@ -670,13 +740,13 @@ void WgCompositor::markupClipPath(WgContext& context, WgRenderDataShape* renderD
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
ARRAY_FOREACH(p, renderData->meshGroupStrokes.meshes) ARRAY_FOREACH(p, renderData->meshGroupStrokes.meshes)
(*p)->draw(context, renderPassEncoder); drawMesh(context, (*p));
} else { } else {
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd; WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes) ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder); drawMeshFan(context, (*p));
} }
} }
@ -698,7 +768,7 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint)
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData0->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData0->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
renderData0->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData0->meshDataBBox);
// merge clip pathes with AND logic // merge clip pathes with AND logic
for (auto p = paint->clips.begin() + 1; p < paint->clips.end(); ++p) { for (auto p = paint->clips.begin() + 1; p < paint->clips.end(); ++p) {
// get render data // get render data
@ -710,31 +780,31 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint)
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth_interm); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth_interm);
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
// copy depth to stencil // copy depth to stencil
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 1); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 1);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_depth_to_stencil); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_depth_to_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
// clear depth current (keep stencil) // clear depth current (keep stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
// clear depth original (keep stencil) // clear depth original (keep stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData0->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData0->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData0->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData0->meshDataBBox);
// copy stencil to depth (clear stencil) // copy stencil to depth (clear stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0); wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
} }
} }
@ -755,12 +825,12 @@ void WgCompositor::clearClipPath(WgContext& context, WgRenderDataPaint* paint)
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr); wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth); wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData->meshDataBBox.drawFan(context, renderPassEncoder); drawMeshFan(context, &renderData->meshDataBBox);
} }
} }
bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose) bool WgCompositor::gaussianBlur(WgContext& context, WgRenderTarget* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose)
{ {
assert(dst); assert(dst);
assert(params); assert(params);
@ -770,8 +840,8 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
auto renderData = (WgRenderDataEffectParams*)params->rd; auto renderData = (WgRenderDataEffectParams*)params->rd;
auto aabb = compose->aabb; auto aabb = compose->aabb;
auto viewport = compose->rdViewport; auto viewport = compose->rdViewport;
WgRenderStorage* sbuff = dst; WgRenderTarget* sbuff = dst;
WgRenderStorage* dbuff = &storageTemp0; WgRenderTarget* dbuff = &targetTemp0;
// begin compute pass // begin compute pass
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass gaussian blur" }; WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass gaussian blur" };
@ -784,7 +854,7 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderData->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderData->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_horz); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_horz);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (aabb.w - 1) / 128 + 1, aabb.h, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (aabb.sw() - 1) / 128 + 1, aabb.sh(), 1);
std::swap(sbuff, dbuff); std::swap(sbuff, dbuff);
} }
// vertical blur // vertical blur
@ -794,7 +864,7 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderData->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderData->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_vert); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_vert);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, aabb.w, (aabb.h - 1) / 128 + 1, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, aabb.sw(), (aabb.sh() - 1) / 128 + 1, 1);
std::swap(sbuff, dbuff); std::swap(sbuff, dbuff);
} }
} }
@ -803,14 +873,14 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
wgpuComputePassEncoderRelease(computePassEncoder); wgpuComputePassEncoderRelease(computePassEncoder);
// if final result stored in intermidiate buffer we must copy result to destination buffer // if final result stored in intermidiate buffer we must copy result to destination buffer
if (sbuff == &storageTemp0) if (sbuff == &targetTemp0)
copyTexture(sbuff, dbuff, aabb); copyTexture(sbuff, dbuff, aabb);
return true; return true;
} }
bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const RenderEffectDropShadow* params, const WgCompose* compose) bool WgCompositor::dropShadow(WgContext& context, WgRenderTarget* dst, const RenderEffectDropShadow* params, const WgCompose* compose)
{ {
assert(dst); assert(dst);
assert(params); assert(params);
@ -823,9 +893,9 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
auto viewport = compose->rdViewport; auto viewport = compose->rdViewport;
{ // apply blur { // apply blur
copyTexture(&storageTemp1, dst, aabb); copyTexture(&targetTemp1, dst, aabb);
WgRenderStorage* sbuff = &storageTemp1; WgRenderTarget* sbuff = &targetTemp1;
WgRenderStorage* dbuff = &storageTemp0; WgRenderTarget* dbuff = &targetTemp0;
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blur" }; WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blur" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc); WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
// horizontal blur // horizontal blur
@ -834,7 +904,7 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_horz); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_horz);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (aabb.w - 1) / 128 + 1, aabb.h, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (aabb.sw() - 1) / 128 + 1, aabb.h(), 1);
std::swap(sbuff, dbuff); std::swap(sbuff, dbuff);
// vertical blur // vertical blur
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, sbuff->bindGroupRead, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, sbuff->bindGroupRead, 0, nullptr);
@ -842,14 +912,14 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_vert); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.gaussian_vert);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, aabb.w, (aabb.h - 1) / 128 + 1, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, aabb.sw(), (aabb.sh() - 1) / 128 + 1, 1);
std::swap(sbuff, dbuff); std::swap(sbuff, dbuff);
wgpuComputePassEncoderEnd(computePassEncoder); wgpuComputePassEncoderEnd(computePassEncoder);
wgpuComputePassEncoderRelease(computePassEncoder); wgpuComputePassEncoderRelease(computePassEncoder);
} }
{ // blend origin (temp0), shadow (temp1) to destination { // blend origin (temp0), shadow (temp1) to destination
copyTexture(&storageTemp0, dst, aabb); copyTexture(&targetTemp0, dst, aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blend" }; WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blend" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc); WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);
@ -857,7 +927,7 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.dropshadow); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.dropshadow);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (aabb.w - 1) / 128 + 1, aabb.h, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (aabb.sw() - 1) / 128 + 1, aabb.h(), 1);
wgpuComputePassEncoderEnd(computePassEncoder); wgpuComputePassEncoderEnd(computePassEncoder);
wgpuComputePassEncoderRelease(computePassEncoder); wgpuComputePassEncoderRelease(computePassEncoder);
} }
@ -866,7 +936,7 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
} }
bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectFill* params, const WgCompose* compose) bool WgCompositor::fillEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectFill* params, const WgCompose* compose)
{ {
assert(dst); assert(dst);
assert(params); assert(params);
@ -874,7 +944,7 @@ bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const Re
assert(compose->rdViewport); assert(compose->rdViewport);
assert(!renderPassEncoder); assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb); copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass fill" }; WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass fill" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc); WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);
@ -882,7 +952,7 @@ bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const Re
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, static_cast<WgRenderDataEffectParams*>(params->rd)->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, static_cast<WgRenderDataEffectParams*>(params->rd)->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, compose->rdViewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, compose->rdViewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.fill_effect); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.fill_effect);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (compose->aabb.w - 1) / 128 + 1, compose->aabb.h, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (compose->aabb.sw() - 1) / 128 + 1, compose->aabb.sh(), 1);
wgpuComputePassEncoderEnd(computePassEncoder); wgpuComputePassEncoderEnd(computePassEncoder);
wgpuComputePassEncoderRelease(computePassEncoder); wgpuComputePassEncoderRelease(computePassEncoder);
@ -890,7 +960,7 @@ bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const Re
} }
bool WgCompositor::tintEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTint* params, const WgCompose* compose) bool WgCompositor::tintEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTint* params, const WgCompose* compose)
{ {
assert(dst); assert(dst);
assert(params); assert(params);
@ -898,7 +968,7 @@ bool WgCompositor::tintEffect(WgContext& context, WgRenderStorage* dst, const Re
assert(compose->rdViewport); assert(compose->rdViewport);
assert(!renderPassEncoder); assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb); copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tint" }; WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tint" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc); WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);
@ -906,14 +976,14 @@ bool WgCompositor::tintEffect(WgContext& context, WgRenderStorage* dst, const Re
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, static_cast<WgRenderDataEffectParams*>(params->rd)->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, static_cast<WgRenderDataEffectParams*>(params->rd)->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, compose->rdViewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, compose->rdViewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.tint_effect); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.tint_effect);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (compose->aabb.w - 1) / 128 + 1, compose->aabb.h, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (compose->aabb.sw() - 1) / 128 + 1, compose->aabb.sh(), 1);
wgpuComputePassEncoderEnd(computePassEncoder); wgpuComputePassEncoderEnd(computePassEncoder);
wgpuComputePassEncoderRelease(computePassEncoder); wgpuComputePassEncoderRelease(computePassEncoder);
return true; return true;
} }
bool WgCompositor::tritoneEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTritone* params, const WgCompose* compose) bool WgCompositor::tritoneEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTritone* params, const WgCompose* compose)
{ {
assert(dst); assert(dst);
assert(params); assert(params);
@ -921,7 +991,7 @@ bool WgCompositor::tritoneEffect(WgContext& context, WgRenderStorage* dst, const
assert(compose->rdViewport); assert(compose->rdViewport);
assert(!renderPassEncoder); assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb); copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tritone" }; WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tritone" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc); WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 0, bindGroupStorageTemp, 0, nullptr);
@ -929,7 +999,7 @@ bool WgCompositor::tritoneEffect(WgContext& context, WgRenderStorage* dst, const
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, static_cast<WgRenderDataEffectParams*>(params->rd)->bindGroupParams, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, static_cast<WgRenderDataEffectParams*>(params->rd)->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, compose->rdViewport->bindGroupViewport, 0, nullptr); wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, compose->rdViewport->bindGroupViewport, 0, nullptr);
wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.tritone_effect); wgpuComputePassEncoderSetPipeline(computePassEncoder, pipelines.tritone_effect);
wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (compose->aabb.w - 1) / 128 + 1, compose->aabb.h, 1); wgpuComputePassEncoderDispatchWorkgroups(computePassEncoder, (compose->aabb.sw() - 1) / 128 + 1, compose->aabb.sh(), 1);
wgpuComputePassEncoderEnd(computePassEncoder); wgpuComputePassEncoderEnd(computePassEncoder);
wgpuComputePassEncoderRelease(computePassEncoder); wgpuComputePassEncoderRelease(computePassEncoder);

View file

@ -38,6 +38,8 @@ class WgCompositor
private: private:
// pipelines // pipelines
WgPipelines pipelines{}; WgPipelines pipelines{};
// stage buffers
WgRenderDataStageBuffer stageBuffer{};
// global stencil/depth buffer handles // global stencil/depth buffer handles
WGPUTexture texDepthStencil{}; WGPUTexture texDepthStencil{};
WGPUTextureView texViewDepthStencil{}; WGPUTextureView texViewDepthStencil{};
@ -52,21 +54,26 @@ private:
// current render pass handles // current render pass handles
WGPURenderPassEncoder renderPassEncoder{}; WGPURenderPassEncoder renderPassEncoder{};
WGPUCommandEncoder commandEncoder{}; WGPUCommandEncoder commandEncoder{};
WgRenderStorage* currentTarget{}; WgRenderTarget* currentTarget{};
// intermediate render storages // intermediate render targets
WgRenderStorage storageTemp0; WgRenderTarget targetTemp0;
WgRenderStorage storageTemp1; WgRenderTarget targetTemp1;
WGPUBindGroup bindGroupStorageTemp{}; WGPUBindGroup bindGroupStorageTemp{};
// composition and blend geometries // composition and blend geometries
WgMeshData meshData; WgMeshData meshDataBlit;
// render target dimensions // render target dimensions
uint32_t width{}; uint32_t width{};
uint32_t height{}; uint32_t height{};
// viewport utilities // viewport utilities
RenderRegion shrinkRenderRegion(RenderRegion& rect); RenderRegion shrinkRenderRegion(RenderRegion& rect);
void copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src); void copyTexture(const WgRenderTarget* dst, const WgRenderTarget* src);
void copyTexture(const WgRenderStorage* dst, const WgRenderStorage* src, const RenderRegion& region); 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 // shapes
void drawShape(WgContext& context, WgRenderDataShape* renderData); void drawShape(WgContext& context, WgRenderDataShape* renderData);
@ -84,8 +91,8 @@ private:
void clipImage(WgContext& context, WgRenderDataPicture* renderData); void clipImage(WgContext& context, WgRenderDataPicture* renderData);
// scenes // scenes
void drawScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose); void drawScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose);
void blendScene(WgContext& context, WgRenderStorage* src, WgCompose* compose); void blendScene(WgContext& context, WgRenderTarget* src, WgCompose* compose);
// the renderer prioritizes clipping with the stroke over the shape's fill // the renderer prioritizes clipping with the stroke over the shape's fill
void markupClipPath(WgContext& context, WgRenderDataShape* renderData); void markupClipPath(WgContext& context, WgRenderDataShape* renderData);
@ -100,24 +107,32 @@ public:
void resize(WgContext& context, uint32_t width, uint32_t height); void resize(WgContext& context, uint32_t width, uint32_t height);
// render passes workflow // 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(); void endRenderPass();
// request shapes for drawing (staging)
// stage data
void reset(WgContext& context);
void flush(WgContext& context);
void requestShape(WgRenderDataShape* renderData);
void requestImage(WgRenderDataPicture* renderData);
// render shapes, images and scenes // render shapes, images and scenes
void renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod); void renderShape(WgContext& context, WgRenderDataShape* renderData, BlendMethod blendMethod);
void renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod); void renderImage(WgContext& context, WgRenderDataPicture* renderData, BlendMethod blendMethod);
void renderScene(WgContext& context, WgRenderStorage* scene, WgCompose* compose); void renderScene(WgContext& context, WgRenderTarget* scene, WgCompose* compose);
void composeScene(WgContext& context, WgRenderStorage* src, WgRenderStorage* mask, WgCompose* compose); void composeScene(WgContext& context, WgRenderTarget* src, WgRenderTarget* mask, WgCompose* compose);
// blit render storage to texture view (f.e. screen buffer) // blit render target to texture view (f.e. screen buffer)
void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderStorage* src, WGPUTextureView dstView); void blit(WgContext& context, WGPUCommandEncoder encoder, WgRenderTarget* src, WGPUTextureView dstView);
// effects // effects
bool gaussianBlur(WgContext& context, WgRenderStorage* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose); bool gaussianBlur(WgContext& context, WgRenderTarget* dst, const RenderEffectGaussianBlur* params, const WgCompose* compose);
bool dropShadow(WgContext& context, WgRenderStorage* dst, const RenderEffectDropShadow* params, const WgCompose* compose); bool dropShadow(WgContext& context, WgRenderTarget* dst, const RenderEffectDropShadow* params, const WgCompose* compose);
bool fillEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectFill* params, const WgCompose* compose); bool fillEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectFill* params, const WgCompose* compose);
bool tintEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTint* params, const WgCompose* compose); bool tintEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTint* params, const WgCompose* compose);
bool tritoneEffect(WgContext& context, WgRenderStorage* dst, const RenderEffectTritone* params, const WgCompose* compose); bool tritoneEffect(WgContext& context, WgRenderTarget* dst, const RenderEffectTritone* params, const WgCompose* compose);
}; };
#endif // _TVG_WG_COMPOSITOR_H_ #endif // _TVG_WG_COMPOSITOR_H_

View file

@ -30,95 +30,88 @@
// WgMeshData // 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(WgContext& context, const WgVertexBuffer& vertexBuffer)
{ {
assert(vertexBuffer.count > 2); assert(vertexBuffer.count > 2);
vertexCount = vertexBuffer.count; // setup vertex data
indexCount = (vertexBuffer.count - 2) * 3; vbuffer.reserve(vertexBuffer.count);
context.allocateBufferVertex(bufferPosition, (float*)vertexBuffer.data, vertexCount * sizeof(float) * 2); vbuffer.count = vertexBuffer.count;
context.allocateBufferIndexFan(vertexCount); memcpy(vbuffer.data, vertexBuffer.data, sizeof(vertexBuffer.data[0])*vertexBuffer.count);
// setup tex coords data
tbuffer.clear();
context.allocateBufferIndexFan(vbuffer.count);
} }
void WgMeshData::update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd) void WgMeshData::update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd)
{ {
assert(vertexBufferInd.vcount > 2); assert(vertexBufferInd.vcount > 2);
vertexCount = vertexBufferInd.vcount; // setup vertex data
indexCount = vertexBufferInd.icount; vbuffer.reserve(vertexBufferInd.vcount);
if (vertexCount > 0) context.allocateBufferVertex(bufferPosition, (float*)vertexBufferInd.vbuff, vertexCount * sizeof(float) * 2); vbuffer.count = vertexBufferInd.vcount;
if (indexCount > 0) context.allocateBufferIndex(bufferIndex, vertexBufferInd.ibuff, indexCount * sizeof(uint32_t)); 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(WgContext& context, 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}; 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)); // setup vertex data
context.allocateBufferIndexFan(vertexCount); vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, data, sizeof(data));
// setup tex coords data
tbuffer.clear();
context.allocateBufferIndexFan(vbuffer.count);
} }
void WgMeshData::imageBox(WgContext& context, float w, float h) void WgMeshData::imageBox(WgContext& context, 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 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 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}; const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata)); // setup vertex data
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata)); vbuffer.reserve(4);
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata)); 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(WgContext& context)
{ {
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 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 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 }; const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 };
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata)); // setup vertex data
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata)); vbuffer.reserve(4);
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata)); 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 // WgMeshDataPool
//*********************************************************************** //***********************************************************************
@ -681,3 +674,94 @@ void WgRenderDataEffectParamsPool::release(WgContext& context)
mPool.clear(); mPool.clear();
mList.clear(); mList.clear();
} }
//***********************************************************************
// WgRenderDataStageBuffer
//***********************************************************************
void WgRenderDataStageBuffer::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]);
// 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 WgRenderDataStageBuffer::append(WgMeshDataGroup* meshDataGroup)
{
ARRAY_FOREACH(p, meshDataGroup->meshes) append(*p);
}
void WgRenderDataStageBuffer::append(WgRenderDataShape* renderDataShape)
{
append(&renderDataShape->meshGroupShapes);
append(&renderDataShape->meshGroupShapesBBox);
append(&renderDataShape->meshGroupStrokes);
append(&renderDataShape->meshGroupStrokesBBox);
append(&renderDataShape->meshDataBBox);
ARRAY_FOREACH(p, renderDataShape->clips)
append((WgRenderDataShape* )(*p));
}
void WgRenderDataStageBuffer::append(WgRenderDataPicture* renderDataPicture)
{
append(&renderDataPicture->meshData);
ARRAY_FOREACH(p, renderDataPicture->clips)
append((WgRenderDataShape* )(*p));
}
void WgRenderDataStageBuffer::release(WgContext& context)
{
context.releaseBuffer(vbuffer_gpu);
context.releaseBuffer(ibuffer_gpu);
}
void WgRenderDataStageBuffer::clear()
{
vbuffer.clear();
ibuffer.clear();
}
void WgRenderDataStageBuffer::flush(WgContext& context)
{
context.allocateBufferVertex(vbuffer_gpu, (float *)vbuffer.data, vbuffer.count);
context.allocateBufferIndex(ibuffer_gpu, (uint32_t *)ibuffer.data, ibuffer.count);
}
void WgRenderDataStageBuffer::bind(WGPURenderPassEncoder renderPass, size_t voffset, size_t toffset)
{
wgpuRenderPassEncoderSetVertexBuffer(renderPass, 0, vbuffer_gpu, voffset, vbuffer.count - voffset);
wgpuRenderPassEncoderSetVertexBuffer(renderPass, 1, vbuffer_gpu, toffset, vbuffer.count - toffset);
wgpuRenderPassEncoderSetIndexBuffer(renderPass, ibuffer_gpu, WGPUIndexFormat_Uint32, 0, ibuffer.count);
}

View file

@ -28,22 +28,19 @@
#include "tvgWgShaderTypes.h" #include "tvgWgShaderTypes.h"
struct WgMeshData { struct WgMeshData {
WGPUBuffer bufferPosition{}; Array<Point> vbuffer;
WGPUBuffer bufferTexCoord{}; Array<Point> tbuffer;
WGPUBuffer bufferIndex{}; Array<uint32_t> ibuffer;
size_t vertexCount{}; size_t voffset{};
size_t indexCount{}; 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 WgVertexBuffer& vertexBuffer);
void update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd); void update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd);
void bbox(WgContext& context, const Point pmin, const Point pmax); void bbox(WgContext& context, const Point pmin, const Point pmax);
void imageBox(WgContext& context, float w, float h); void imageBox(WgContext& context, float w, float h);
void blitBox(WgContext& context); void blitBox(WgContext& context);
void release(WgContext& context); void release(WgContext& context) {};
}; };
class WgMeshDataPool { class WgMeshDataPool {
@ -224,4 +221,23 @@ public:
void release(WgContext& context); void release(WgContext& context);
}; };
class WgRenderDataStageBuffer {
private:
Array<uint8_t> vbuffer;
Array<uint8_t> ibuffer;
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);
void bind(WGPURenderPassEncoder renderPass, size_t voffset, size_t toffset);
};
#endif // _TVG_WG_RENDER_DATA_H_ #endif // _TVG_WG_RENDER_DATA_H_

View file

@ -22,7 +22,7 @@
#include "tvgWgRenderTarget.h" #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->width = width;
this->height = height; 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(bindGroupTexure);
context.layouts.releaseBindGroup(bindGroupWrite); 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) { if (pool.count > 0) {
renderStorage = pool.last(); renderTarget = pool.last();
pool.pop(); pool.pop();
} else { } else {
renderStorage = new WgRenderStorage; renderTarget = new WgRenderTarget;
renderStorage->initialize(context, width, height); renderTarget->initialize(context, width, height);
list.push(renderStorage); 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->width = width;
this->height = height; this->height = height;
} }
void WgRenderStoragePool::release(WgContext& context) void WgRenderTargetPool::release(WgContext& context)
{ {
ARRAY_FOREACH(p, list) { ARRAY_FOREACH(p, list) {
(*p)->release(context); (*p)->release(context);

View file

@ -26,7 +26,7 @@
#include "tvgWgPipelines.h" #include "tvgWgPipelines.h"
#include "tvgRender.h" #include "tvgRender.h"
struct WgRenderStorage { struct WgRenderTarget {
WGPUTexture texture{}; WGPUTexture texture{};
WGPUTexture textureMS{}; WGPUTexture textureMS{};
WGPUTextureView texView{}; WGPUTextureView texView{};
@ -42,15 +42,15 @@ struct WgRenderStorage {
}; };
class WgRenderStoragePool { class WgRenderTargetPool {
private: private:
Array<WgRenderStorage*> list; Array<WgRenderTarget*> list;
Array<WgRenderStorage*> pool; Array<WgRenderTarget*> pool;
uint32_t width{}; uint32_t width{};
uint32_t height{}; uint32_t height{};
public: public:
WgRenderStorage* allocate(WgContext& context); WgRenderTarget* allocate(WgContext& context);
void free(WgContext& context, WgRenderStorage* renderTarget); void free(WgContext& context, WgRenderTarget* renderTarget);
void initialize(WgContext& context, uint32_t width, uint32_t height); void initialize(WgContext& context, uint32_t width, uint32_t height);
void release(WgContext& context); void release(WgContext& context);

View 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;
}
}

View 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_

View file

@ -47,13 +47,13 @@ void WgRenderer::release()
mRenderDataEffectParamsPool.release(mContext); mRenderDataEffectParamsPool.release(mContext);
WgMeshDataPool::gMeshDataPool->release(mContext); WgMeshDataPool::gMeshDataPool->release(mContext);
// clear render storage pool // clear render pool
mRenderStoragePool.release(mContext); mRenderTargetPool.release(mContext);
// clear rendering tree stacks // clear rendering tree stacks
mCompositorStack.clear(); mCompositorList.clear();
mRenderStorageStack.clear(); mRenderTargetStack.clear();
mRenderStorageRoot.release(mContext); mRenderTargetRoot.release(mContext);
// release context handles // release context handles
mCompositor.release(mContext); mCompositor.release(mContext);
@ -195,50 +195,71 @@ RenderData WgRenderer::prepare(RenderSurface* surface, RenderData data, const Ma
bool WgRenderer::preRender() bool WgRenderer::preRender()
{ {
// invalidate context
if (mContext.invalid()) return false; if (mContext.invalid()) return false;
// reset stage data
// push rot render storage to the render tree stack mCompositor.reset(mContext);
assert(mRenderStorageStack.count == 0); // push root render target to the render tree stack
mRenderStorageStack.push(&mRenderStorageRoot); assert(mRenderTargetStack.count == 0);
// create command encoder for drawing mRenderTargetStack.push(&mRenderTargetRoot);
WGPUCommandEncoderDescriptor commandEncoderDesc{}; // create root compose settings
mCommandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc); WgCompose* compose = new WgCompose();
// start root render pass compose->aabb = { { 0, 0 }, { (int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h } };
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true); 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; return true;
} }
bool WgRenderer::renderShape(RenderData data) bool WgRenderer::renderShape(RenderData data)
{ {
// temporary simple render data to the current render target WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod);
mCompositor.renderShape(mContext, (WgRenderDataShape*)data, mBlendMethod); WgSceneTask* sceneTask = mSceneTaskStack.last();
sceneTask->children.push(paintTask);
mRenderTaskList.push(paintTask);
mCompositor.requestShape((WgRenderDataShape*)data);
return true; return true;
} }
bool WgRenderer::renderImage(RenderData data) bool WgRenderer::renderImage(RenderData data)
{ {
// temporary simple render data to the current render target WgPaintTask* paintTask = new WgPaintTask((WgRenderDataPaint*)data, mBlendMethod);
mCompositor.renderImage(mContext, (WgRenderDataPicture*)data, mBlendMethod); WgSceneTask* sceneTask = mSceneTaskStack.last();
sceneTask->children.push(paintTask);
mRenderTaskList.push(paintTask);
mCompositor.requestImage((WgRenderDataPicture*)data);
return true; return true;
} }
bool WgRenderer::postRender() bool WgRenderer::postRender()
{ {
// end root render pass // flush stage data to gpu
mCompositor.endRenderPass(); mCompositor.flush(mContext);
// release command encoder // create command encoder for drawing
const WGPUCommandBufferDescriptor commandBufferDesc{}; WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder();
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(mCommandEncoder, &commandBufferDesc); // run rendering (all the fun is here)
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer); WgSceneTask* sceneTaskRoot = mSceneTaskStack.last();
wgpuCommandBufferRelease(commandsBuffer); sceneTaskRoot->run(mContext, mCompositor, commandEncoder);
wgpuCommandEncoderRelease(mCommandEncoder); // execute and release command encoder
// pop root render storage to the render tree stack mContext.submitCommandEncoder(commandEncoder);
mRenderStorageStack.pop(); mContext.releaseCommandEncoder(commandEncoder);
assert(mRenderStorageStack.count == 0); // clear the render tasks tree
// clear viewport list and store allocated handles to pool 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) ARRAY_FOREACH(p, mRenderDataViewportList)
mRenderDataViewportPool.free(mContext, *p); mRenderDataViewportPool.free(mContext, *p);
mRenderDataViewportList.clear(); mRenderDataViewportList.clear();
@ -259,14 +280,9 @@ RenderRegion WgRenderer::region(RenderData data)
if (renderData->type() == Type::Shape) { if (renderData->type() == Type::Shape) {
auto& v1 = renderData->aabb.min; auto& v1 = renderData->aabb.min;
auto& v2 = renderData->aabb.max; auto& v2 = renderData->aabb.max;
RenderRegion renderRegion; return {{int32_t(nearbyint(v1.x)), int32_t(nearbyint(v1.y))}, {int32_t(nearbyint(v2.x)), int32_t(nearbyint(v2.y))}};
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 { 0, 0, (int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h }; return {{0, 0}, {(int32_t)mTargetSurface.w, (int32_t)mTargetSurface.h}};
} }
@ -330,18 +346,11 @@ bool WgRenderer::sync()
WGPUTextureView dstTextureView = mContext.createTextureView(dstTexture); WGPUTextureView dstTextureView = mContext.createTextureView(dstTexture);
// create command encoder // create command encoder
const WGPUCommandEncoderDescriptor commandEncoderDesc{}; WGPUCommandEncoder commandEncoder = mContext.createCommandEncoder();
WGPUCommandEncoder commandEncoder = wgpuDeviceCreateCommandEncoder(mContext.device, &commandEncoderDesc);
// show root offscreen buffer // show root offscreen buffer
mCompositor.blit(mContext, commandEncoder, &mRenderStorageRoot, dstTextureView); mCompositor.blit(mContext, commandEncoder, &mRenderTargetRoot, dstTextureView);
mContext.submitCommandEncoder(commandEncoder);
// release command encoder mContext.releaseCommandEncoder(commandEncoder);
const WGPUCommandBufferDescriptor commandBufferDesc{};
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
wgpuQueueSubmit(mContext.queue, 1, &commandsBuffer);
wgpuCommandBufferRelease(commandsBuffer);
wgpuCommandEncoderRelease(commandEncoder);
// release dest buffer view // release dest buffer view
mContext.releaseTextureView(dstTextureView); mContext.releaseTextureView(dstTextureView);
@ -372,8 +381,8 @@ bool WgRenderer::target(WGPUDevice device, WGPUInstance instance, void* target,
mContext.initialize(instance, device); mContext.initialize(instance, device);
// initialize render tree instances // initialize render tree instances
mRenderStoragePool.initialize(mContext, width, height); mRenderTargetPool.initialize(mContext, width, height);
mRenderStorageRoot.initialize(mContext, width, height); mRenderTargetRoot.initialize(mContext, width, height);
mCompositor.initialize(mContext, width, height); mCompositor.initialize(mContext, width, height);
// store target properties // store target properties
@ -392,12 +401,12 @@ bool WgRenderer::target(WGPUDevice device, WGPUInstance instance, void* target,
// update render targets dimentions // update render targets dimentions
if ((mTargetSurface.w != width) || (mTargetSurface.h != height) || (type == 0 ? (surface != (WGPUSurface)target) : (targetTexture != (WGPUTexture)target))) { if ((mTargetSurface.w != width) || (mTargetSurface.h != height) || (type == 0 ? (surface != (WGPUSurface)target) : (targetTexture != (WGPUTexture)target))) {
// release render tagets // release render tagets
mRenderStoragePool.release(mContext); mRenderTargetPool.release(mContext);
mRenderStorageRoot.release(mContext); mRenderTargetRoot.release(mContext);
clearTargets(); clearTargets();
mRenderStoragePool.initialize(mContext, width, height); mRenderTargetPool.initialize(mContext, width, height);
mRenderStorageRoot.initialize(mContext, width, height); mRenderTargetRoot.initialize(mContext, width, height);
mCompositor.resize(mContext, width, height); mCompositor.resize(mContext, width, height);
// store target properties // store target properties
@ -452,7 +461,7 @@ RenderCompositor* WgRenderer::target(const RenderRegion& region, TVG_UNUSED Colo
compose->rdViewport->update(mContext, region); compose->rdViewport->update(mContext, region);
mRenderDataViewportList.push(compose->rdViewport); mRenderDataViewportList.push(compose->rdViewport);
} }
mCompositorStack.push(compose); mCompositorList.push(compose);
return compose; return compose;
} }
@ -464,57 +473,52 @@ bool WgRenderer::beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_
compose->method = method; compose->method = method;
compose->opacity = opacity; compose->opacity = opacity;
compose->blend = mBlendMethod; compose->blend = mBlendMethod;
// end current render pass WgSceneTask* sceneTaskCurrent = mSceneTaskStack.last();
mCompositor.endRenderPass(); // allocate new render target and push to the render tree stack
// allocate new render storage and push to the render tree stack WgRenderTarget* renderTarget = mRenderTargetPool.allocate(mContext);
WgRenderStorage* storage = mRenderStoragePool.allocate(mContext); mRenderTargetStack.push(renderTarget);
mRenderStorageStack.push(storage); // create and setup new scene task
// begin newly added render pass WgSceneTask* sceneTask = new WgSceneTask(renderTarget, compose, sceneTaskCurrent);
WGPUColor color{}; sceneTaskCurrent->children.push(sceneTask);
if ((compose->method == MaskMethod::None) && (compose->blend != BlendMethod::Normal)) color = { 1.0, 1.0, 1.0, 0.0 }; mRenderTaskList.push(sceneTask);
mCompositor.beginRenderPass(mCommandEncoder, mRenderStorageStack.last(), true, color); mSceneTaskStack.push(sceneTask);
return true; return true;
} }
bool WgRenderer::endComposite(RenderCompositor* cmp) 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 // finish scene blending
if (comp->method == MaskMethod::None) { if (cmp->method == MaskMethod::None) {
// get source and destination render storages // get source and destination render targets
WgRenderStorage* src = mRenderStorageStack.last(); WgRenderTarget* src = mRenderTargetStack.last();
mRenderStorageStack.pop(); mRenderTargetStack.pop();
WgRenderStorage* dst = mRenderStorageStack.last(); // pop source scene
// begin previous render pass WgSceneTask* srcScene = mSceneTaskStack.last();
mCompositor.beginRenderPass(mCommandEncoder, dst, false); mSceneTaskStack.pop();
// apply composition // setup render target compose destitations
mCompositor.renderScene(mContext, src, comp); srcScene->renderTargetDst = mSceneTaskStack.last()->renderTarget;
srcScene->renderTargetMsk = nullptr;
// back render targets to the pool // back render targets to the pool
mRenderStoragePool.free(mContext, src); mRenderTargetPool.free(mContext, src);
} else { // finish composition } else { // finish scene composition
// get source, mask and destination render storages // get source, mask and destination render targets
WgRenderStorage* src = mRenderStorageStack.last(); WgRenderTarget* src = mRenderTargetStack.last();
mRenderStorageStack.pop(); mRenderTargetStack.pop();
WgRenderStorage* msk = mRenderStorageStack.last(); WgRenderTarget* msk = mRenderTargetStack.last();
mRenderStorageStack.pop(); mRenderTargetStack.pop();
WgRenderStorage* dst = mRenderStorageStack.last(); // get source and mask scenes
// begin previous render pass WgSceneTask* srcScene = mSceneTaskStack.last();
mCompositor.beginRenderPass(mCommandEncoder, dst, false); mSceneTaskStack.pop();
// apply composition WgSceneTask* mskScene = mSceneTaskStack.last();
mCompositor.composeScene(mContext, src, msk, comp); mSceneTaskStack.pop();
// setup render target compose destitations
srcScene->renderTargetDst = mSceneTaskStack.last()->renderTarget;
srcScene->renderTargetMsk = mskScene->renderTarget;
// back render targets to the pool // back render targets to the pool
mRenderStoragePool.free(mContext, src); mRenderTargetPool.free(mContext, src);
mRenderStoragePool.free(mContext, msk); mRenderTargetPool.free(mContext, msk);
} }
// delete current compositor settings
delete mCompositorStack.last();
mCompositorStack.pop();
return true; return true;
} }
@ -586,12 +590,12 @@ bool WgRenderer::region(RenderEffect* effect)
auto gaussian = (RenderEffectGaussianBlur*)effect; auto gaussian = (RenderEffectGaussianBlur*)effect;
auto renderData = (WgRenderDataEffectParams*)gaussian->rd; auto renderData = (WgRenderDataEffectParams*)gaussian->rd;
if (gaussian->direction != 2) { if (gaussian->direction != 2) {
gaussian->extend.x = -renderData->extend; gaussian->extend.min.x = -renderData->extend;
gaussian->extend.w = +renderData->extend * 2; gaussian->extend.max.x = +renderData->extend;
} }
if (gaussian->direction != 1) { if (gaussian->direction != 1) {
gaussian->extend.y = -renderData->extend; gaussian->extend.min.y = -renderData->extend;
gaussian->extend.h = +renderData->extend * 2; gaussian->extend.max.y = +renderData->extend;
} }
return true; return true;
} else } else
@ -599,10 +603,10 @@ bool WgRenderer::region(RenderEffect* effect)
if (effect->type == SceneEffect::DropShadow) { if (effect->type == SceneEffect::DropShadow) {
auto dropShadow = (RenderEffectDropShadow*)effect; auto dropShadow = (RenderEffectDropShadow*)effect;
auto renderData = (WgRenderDataEffectParams*)dropShadow->rd; auto renderData = (WgRenderDataEffectParams*)dropShadow->rd;
dropShadow->extend.x = -(renderData->extend + std::abs(renderData->offset.x)); dropShadow->extend.min.x = -(renderData->extend + std::abs(renderData->offset.x));
dropShadow->extend.w = +(renderData->extend + std::abs(renderData->offset.x)) * 2; dropShadow->extend.max.x = +(renderData->extend + std::abs(renderData->offset.x));
dropShadow->extend.y = -(renderData->extend + std::abs(renderData->offset.y)); dropShadow->extend.min.y = -(renderData->extend + std::abs(renderData->offset.y));
dropShadow->extend.h = +(renderData->extend + std::abs(renderData->offset.y)) * 2; dropShadow->extend.max.y = +(renderData->extend + std::abs(renderData->offset.y));
return true; return true;
} }
return false; return false;
@ -611,20 +615,8 @@ bool WgRenderer::region(RenderEffect* effect)
bool WgRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, TVG_UNUSED bool direct) 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 mSceneTaskStack.last()->effect = effect;
mCompositor.endRenderPass(); return true;
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;
} }

View file

@ -23,7 +23,7 @@
#ifndef _TVG_WG_RENDERER_H_ #ifndef _TVG_WG_RENDERER_H_
#define _TVG_WG_RENDERER_H_ #define _TVG_WG_RENDERER_H_
#include "tvgWgCompositor.h" #include "tvgWgRenderTask.h"
class WgRenderer : public RenderMethod class WgRenderer : public RenderMethod
{ {
@ -72,13 +72,15 @@ private:
bool surfaceConfigure(WGPUSurface surface, WgContext& context, uint32_t width, uint32_t height); bool surfaceConfigure(WGPUSurface surface, WgContext& context, uint32_t width, uint32_t height);
// render tree stacks // render tree stacks
WgRenderStorage mRenderStorageRoot; WgRenderTarget mRenderTargetRoot;
Array<WgCompose*> mCompositorStack; Array<WgCompose*> mCompositorList;
Array<WgRenderStorage*> mRenderStorageStack; Array<WgRenderTarget*> mRenderTargetStack;
Array<WgRenderDataViewport*> mRenderDataViewportList; Array<WgRenderDataViewport*> mRenderDataViewportList;
Array<WgSceneTask*> mSceneTaskStack;
Array<WgRenderTask*> mRenderTaskList;
// render storage pool // render target pool
WgRenderStoragePool mRenderStoragePool; WgRenderTargetPool mRenderTargetPool;
// render data paint pools // render data paint pools
WgRenderDataShapePool mRenderDataShapePool; WgRenderDataShapePool mRenderDataShapePool;
@ -100,7 +102,6 @@ private:
Key mDisposeKey{}; Key mDisposeKey{};
// gpu handles // gpu handles
WGPUCommandEncoder mCommandEncoder{};
WGPUTexture targetTexture{}; // external handle WGPUTexture targetTexture{}; // external handle
WGPUSurfaceTexture surfaceTexture{}; WGPUSurfaceTexture surfaceTexture{};
WGPUSurface surface{}; // external handle WGPUSurface surface{}; // external handle

View file

@ -118,10 +118,10 @@ void WgShaderTypeVec4f::update(const RenderColor& c)
void WgShaderTypeVec4f::update(const RenderRegion& r) void WgShaderTypeVec4f::update(const RenderRegion& r)
{ {
vec[0] = r.x; // left vec[0] = r.min.x;
vec[1] = r.y; // top vec[1] = r.min.y;
vec[2] = r.x + r.w - 1; // right vec[2] = r.max.x - 1;
vec[3] = r.y + r.h - 1; // bottom vec[3] = r.max.y - 1;
} }
//************************************************************************ //************************************************************************

View file

@ -223,7 +223,9 @@ TEST_CASE("Composition", "[tvgPaint]")
//Clipping //Clipping
auto comp = Shape::gen(); auto comp = Shape::gen();
REQUIRE(shape->clip() == nullptr);
REQUIRE(shape->clip(comp) == Result::Success); REQUIRE(shape->clip(comp) == Result::Success);
REQUIRE(shape->clip() == comp);
//AlphaMask //AlphaMask
comp = Shape::gen(); comp = Shape::gen();