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
/src/renderer/sw_engine @mgrudzinska
/src/renderer/gl_engine @RuiwenTang @SergeyLebedkin
/src/renderer/gl_engine @SergeyLebedkin @RuiwenTang
/src/renderer/wg_engine @SergeyLebedkin
/src/loaders/external_webp @JSUYA
/src/loaders/raw @JSUYA
/src/loaders/svg @JSUYA @mgrudzinska
/src/loaders/webp @JSUYA
/src/loaders/svg @mgrudzinska @JSUYA
/src/loaders/lottie @mgrudzinska
/src/bindings/capi @mgrudzinska
/src/bindings/wasm @tinyjin
/src/savers/gif @JSUYA
/src/tools/svg2png @JSUYA

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:
<p align="center">
<img width="780" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_svg.png">
<img width="780" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_svg.jpg">
</p>
The following code snippet shows how to draw SVG image using ThorVG:

View file

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

View file

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

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 Animation;
class Shape;
/**
* @defgroup ThorVG ThorVG
@ -396,13 +397,13 @@ public:
*
* @param[in] clipper The shape object as the clipper.
*
* @retval Result::NonSupport If the @p clipper type is not Shape.
* @retval Result::InsufficientCondition if the target has already belonged to another paint.
* @retval Result::InsufficientCondition if the @p clipper has already belonged to another paint.
*
* @see Paint::clip()
*
* @note @p clipper only supports the Shape type.
* @since 1.0
*/
Result clip(Paint* clipper) noexcept;
Result clip(Shape* clipper) noexcept;
/**
* @brief Sets the blending method for the paint object.
@ -478,6 +479,19 @@ public:
*/
MaskMethod mask(const Paint** target) const noexcept;
/**
* @brief Get the clipper shape of the paint object.
*
* This function returns the clipper that has been previously set to this paint object.
*
* @return The shape object used as the clipper, or @c nullptr if no clipper is set.
*
* @see Paint::clip(Shape* clipper)
*
* @since 1.0
*/
Shape* clip() const noexcept;
/**
* @brief Increment the reference count for the Paint instance.
*

BIN
res/example_svg.jpg Normal file

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);
/*!
/**
* @brief Clip the drawing region of the paint object.
*
* This function restricts the drawing area of the paint object to the specified shape's paths.
@ -985,10 +985,24 @@ TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint* paint, const Tvg_P
* @retval TVG_RESULT_INSUFFICIENT_CONDITION if the target has already belonged to another paint.
* @retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape.
*
* @see tvg_paint_get_clip()
* @since 1.0
*/
TVG_API Tvg_Result tvg_paint_clip(Tvg_Paint* paint, Tvg_Paint* clipper);
TVG_API Tvg_Result tvg_paint_set_clip(Tvg_Paint* paint, Tvg_Paint* clipper);
/**
* @brief Get the clipper shape of the paint object.
*
* This function returns the clipper that has been previously set to this paint object.
*
* @return The shape object used as the clipper, or @c nullptr if no clipper is set.
*
* @see tvg_paint_set_clip()
*
* @since 1.0
*/
TVG_API Tvg_Paint* tvg_paint_get_clip(const Tvg_Paint* paint);
/**
* @brief Retrieves the parent paint object.

File diff suppressed because it is too large Load diff

View file

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

View file

@ -33,21 +33,11 @@
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <stdint.h>
#include "tvgCommon.h"
#include "tvgJpgd.h"
#ifdef _MSC_VER
#pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
#define JPGD_NORETURN __declspec(noreturn)
#elif defined(__GNUC__)
#define JPGD_NORETURN __attribute__ ((noreturn))
#else
#define JPGD_NORETURN
#endif
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
@ -171,7 +161,7 @@ private:
jpeg_decoder(const jpeg_decoder &);
jpeg_decoder &operator =(const jpeg_decoder &);
typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
typedef bool (*pDecode_block_func)(jpeg_decoder*, int, int, int);
struct huff_tables
{
@ -198,7 +188,6 @@ private:
char m_data[1];
};
jmp_buf m_jmp_state;
mem_block *m_pMem_blocks;
int m_image_x_size;
int m_image_y_size;
@ -274,21 +263,21 @@ private:
int m_total_bytes_read;
void free_all_blocks();
JPGD_NORETURN void stop_decoding(jpgd_status status);
bool stop_decoding(jpgd_status status);
void *alloc(size_t n, bool zero = false);
void word_clear(void *p, uint16_t c, uint32_t n);
void prep_in_buffer();
void read_dht_marker();
void read_dqt_marker();
void read_sof_marker();
void skip_variable_marker();
void read_dri_marker();
void read_sos_marker();
bool prep_in_buffer();
bool read_dht_marker();
bool read_dqt_marker();
bool read_sof_marker();
bool skip_variable_marker();
bool read_dri_marker();
bool read_sos_marker();
int next_marker();
int process_markers();
void locate_soi_marker();
void locate_sof_marker();
int locate_sos_marker();
bool locate_soi_marker();
bool locate_sof_marker();
bool locate_sos_marker();
void init(jpeg_decoder_stream * pStream);
void create_look_ups();
void fix_in_buffer();
@ -297,18 +286,18 @@ private:
coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
void load_next_row();
void decode_next_row();
bool decode_next_row();
void make_huff_table(int index, huff_tables *pH);
void check_quant_tables();
void check_huff_tables();
bool check_quant_tables();
bool check_huff_tables();
void calc_mcu_block_order();
int init_scan();
void init_frame();
void process_restart();
void decode_scan(pDecode_block_func decode_block_func);
void init_progressive();
void init_sequential();
void decode_start();
bool init_frame();
bool process_restart();
bool decode_scan(pDecode_block_func decode_block_func);
bool init_progressive();
bool init_sequential();
bool decode_start();
void decode_init(jpeg_decoder_stream * pStream);
void H2V2Convert();
void H2V1Convert();
@ -326,10 +315,10 @@ private:
inline int huff_decode(huff_tables *pH);
inline int huff_decode(huff_tables *pH, int& extrabits);
static inline uint8_t clamp(int i);
static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static bool decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static bool decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static bool decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
static bool decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
};
@ -630,7 +619,7 @@ inline uint32_t jpeg_decoder::get_char()
// Any bytes remaining in buffer?
if (!m_in_buf_left) {
// Try to get more bytes.
prep_in_buffer();
if (!prep_in_buffer()) return 0xFF;
// Still nothing to get?
if (!m_in_buf_left) {
// Pad the end of the stream with 0xFF 0xD9 (EOI marker)
@ -650,7 +639,7 @@ inline uint32_t jpeg_decoder::get_char()
inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag)
{
if (!m_in_buf_left) {
prep_in_buffer();
if (!prep_in_buffer()) return 0xFF;
if (!m_in_buf_left) {
*pPadding_flag = true;
int t = m_tem_flag;
@ -1093,13 +1082,15 @@ void jpeg_decoder::free_all_blocks()
}
// This method handles all errors. It will never return.
// It could easily be changed to use C++ exceptions.
JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status)
bool jpeg_decoder::stop_decoding(jpgd_status status)
{
#if 0 //for debugging
m_error_code = status;
#else
m_error_code = JPGD_FAILED;
#endif
free_all_blocks();
longjmp(m_jmp_state, status);
return false;
}
@ -1116,8 +1107,7 @@ void *jpeg_decoder::alloc(size_t nSize, bool zero)
}
if (!rv) {
int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
mem_block *b = tvg::malloc<mem_block*>(sizeof(mem_block) + capacity);
if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
auto b = tvg::malloc<mem_block*>(sizeof(mem_block) + capacity);
b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
b->m_used_count = nSize;
b->m_size = capacity;
@ -1142,16 +1132,15 @@ void jpeg_decoder::word_clear(void *p, uint16_t c, uint32_t n)
// Refill the input buffer.
// This method will sit in a loop until (A) the buffer is full or (B)
// the stream's read() method reports and end of file condition.
void jpeg_decoder::prep_in_buffer()
bool jpeg_decoder::prep_in_buffer()
{
m_in_buf_left = 0;
m_pIn_buf_ofs = m_in_buf;
if (m_eof_flag) return;
if (m_eof_flag) return true;
do {
int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
if (bytes_read == -1) stop_decoding(JPGD_STREAM_READ);
auto bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
if (bytes_read == -1) return stop_decoding(JPGD_STREAM_READ);
m_in_buf_left += bytes_read;
} while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
@ -1160,18 +1149,19 @@ void jpeg_decoder::prep_in_buffer()
// Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
// (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
return true;
}
// Read a Huffman code table.
void jpeg_decoder::read_dht_marker()
bool jpeg_decoder::read_dht_marker()
{
int i, index, count;
uint8_t huff_num[17];
uint8_t huff_val[256];
uint32_t num_left = get_bits(16);
if (num_left < 2) stop_decoding(JPGD_BAD_DHT_MARKER);
if (num_left < 2) return stop_decoding(JPGD_BAD_DHT_MARKER);
num_left -= 2;
while (num_left) {
@ -1184,19 +1174,19 @@ void jpeg_decoder::read_dht_marker()
count += huff_num[i];
}
if (count > 255) stop_decoding(JPGD_BAD_DHT_COUNTS);
if (count > 255) return stop_decoding(JPGD_BAD_DHT_COUNTS);
for (i = 0; i < count; i++)
huff_val[i] = static_cast<uint8_t>(get_bits(8));
i = 1 + 16 + count;
if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DHT_MARKER);
if (num_left < (uint32_t)i) return stop_decoding(JPGD_BAD_DHT_MARKER);
num_left -= i;
if ((index & 0x10) > 0x10) stop_decoding(JPGD_BAD_DHT_INDEX);
if ((index & 0x10) > 0x10) return stop_decoding(JPGD_BAD_DHT_INDEX);
index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
if (index >= JPGD_MAX_HUFF_TABLES) stop_decoding(JPGD_BAD_DHT_INDEX);
if (index >= JPGD_MAX_HUFF_TABLES) return stop_decoding(JPGD_BAD_DHT_INDEX);
if (!m_huff_num[index]) m_huff_num[index] = (uint8_t *)alloc(17);
if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256);
@ -1205,16 +1195,17 @@ void jpeg_decoder::read_dht_marker()
memcpy(m_huff_num[index], huff_num, 17);
memcpy(m_huff_val[index], huff_val, 256);
}
return true;
}
// Read a quantization table.
void jpeg_decoder::read_dqt_marker()
bool jpeg_decoder::read_dqt_marker()
{
int n, i, prec;
uint32_t temp;
uint32_t num_left = get_bits(16);
if (num_left < 2) stop_decoding(JPGD_BAD_DQT_MARKER);
if (num_left < 2) return stop_decoding(JPGD_BAD_DQT_MARKER);
num_left -= 2;
while (num_left) {
@ -1222,7 +1213,7 @@ void jpeg_decoder::read_dqt_marker()
prec = n >> 4;
n &= 0x0F;
if (n >= JPGD_MAX_QUANT_TABLES) stop_decoding(JPGD_BAD_DQT_TABLE);
if (n >= JPGD_MAX_QUANT_TABLES) return stop_decoding(JPGD_BAD_DQT_TABLE);
if (!m_quant[n]) m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t));
@ -1234,30 +1225,31 @@ void jpeg_decoder::read_dqt_marker()
}
i = 64 + 1;
if (prec) i += 64;
if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DQT_LENGTH);
if (num_left < (uint32_t)i) return stop_decoding(JPGD_BAD_DQT_LENGTH);
num_left -= i;
}
return true;
}
// Read the start of frame (SOF) marker.
void jpeg_decoder::read_sof_marker()
bool jpeg_decoder::read_sof_marker()
{
int i;
uint32_t num_left = get_bits(16);
if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */
if (get_bits(8) != 8) return stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */
m_image_y_size = get_bits(16);
if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT);
if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) return stop_decoding(JPGD_BAD_HEIGHT);
m_image_x_size = get_bits(16);
if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) stop_decoding(JPGD_BAD_WIDTH);
if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) return stop_decoding(JPGD_BAD_WIDTH);
m_comps_in_frame = get_bits(8);
if (m_comps_in_frame > JPGD_MAX_COMPONENTS) stop_decoding(JPGD_TOO_MANY_COMPONENTS);
if (m_comps_in_frame > JPGD_MAX_COMPONENTS) return stop_decoding(JPGD_TOO_MANY_COMPONENTS);
if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) stop_decoding(JPGD_BAD_SOF_LENGTH);
if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) return stop_decoding(JPGD_BAD_SOF_LENGTH);
for (i = 0; i < m_comps_in_frame; i++) {
m_comp_ident[i] = get_bits(8);
@ -1265,33 +1257,36 @@ void jpeg_decoder::read_sof_marker()
m_comp_v_samp[i] = get_bits(4);
m_comp_quant[i] = get_bits(8);
}
return true;
}
// Used to skip unrecognized markers.
void jpeg_decoder::skip_variable_marker()
bool jpeg_decoder::skip_variable_marker()
{
uint32_t num_left = get_bits(16);
if (num_left < 2) stop_decoding(JPGD_BAD_VARIABLE_MARKER);
if (num_left < 2) return stop_decoding(JPGD_BAD_VARIABLE_MARKER);
num_left -= 2;
while (num_left) {
get_bits(8);
num_left--;
}
return true;
}
// Read a define restart interval (DRI) marker.
void jpeg_decoder::read_dri_marker()
bool jpeg_decoder::read_dri_marker()
{
if (get_bits(16) != 4) stop_decoding(JPGD_BAD_DRI_LENGTH);
if (get_bits(16) != 4) return stop_decoding(JPGD_BAD_DRI_LENGTH);
m_restart_interval = get_bits(16);
return true;
}
// Read a start of scan (SOS) marker.
void jpeg_decoder::read_sos_marker()
bool jpeg_decoder::read_sos_marker()
{
int i, ci, c, cc;
uint32_t num_left = get_bits(16);
@ -1300,7 +1295,7 @@ void jpeg_decoder::read_sos_marker()
m_comps_in_scan = n;
num_left -= 3;
if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) stop_decoding(JPGD_BAD_SOS_LENGTH);
if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) return stop_decoding(JPGD_BAD_SOS_LENGTH);
for (i = 0; i < n; i++) {
cc = get_bits(8);
@ -1310,7 +1305,7 @@ void jpeg_decoder::read_sos_marker()
for (ci = 0; ci < m_comps_in_frame; ci++)
if (cc == m_comp_ident[ci]) break;
if (ci >= m_comps_in_frame) stop_decoding(JPGD_BAD_SOS_COMP_ID);
if (ci >= m_comps_in_frame) return stop_decoding(JPGD_BAD_SOS_COMP_ID);
m_comp_list[i] = ci;
m_comp_dc_tab[ci] = (c >> 4) & 15;
@ -1331,6 +1326,7 @@ void jpeg_decoder::read_sos_marker()
get_bits(8);
num_left--;
}
return true;
}
@ -1380,21 +1376,21 @@ int jpeg_decoder::process_markers()
case M_EOI:
case M_SOS: return c;
case M_DHT: {
read_dht_marker();
break;
if (read_dht_marker()) break;
else return M_EOI;
}
// No arithmetic support - dumb patents!
case M_DAC: {
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
break;
return M_EOI;
}
case M_DQT: {
read_dqt_marker();
break;
if (read_dqt_marker()) break;
else return M_EOI;
}
case M_DRI: {
read_dri_marker();
break;
if (read_dri_marker()) break;
else return M_EOI;
}
//case M_APP0: /* no need to read the JFIF marker */
case M_JPG:
@ -1408,11 +1404,11 @@ int jpeg_decoder::process_markers()
case M_RST7:
case M_TEM: {
stop_decoding(JPGD_UNEXPECTED_MARKER);
break;
return M_EOI;
}
default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
skip_variable_marker();
break;
if (skip_variable_marker()) break;
else return M_EOI;
}
}
}
@ -1422,71 +1418,62 @@ int jpeg_decoder::process_markers()
// Finds the start of image (SOI) marker.
// This code is rather defensive: it only checks the first 512 bytes to avoid
// false positives.
void jpeg_decoder::locate_soi_marker()
bool jpeg_decoder::locate_soi_marker()
{
uint32_t lastchar = get_bits(8);
uint32_t thischar = get_bits(8);
/* ok if it's a normal JPEG file without a special header */
if ((lastchar == 0xFF) && (thischar == M_SOI)) return;
if ((lastchar == 0xFF) && (thischar == M_SOI)) return true;
uint32_t bytesleft = 4096; //512;
while (true) {
if (--bytesleft == 0) stop_decoding(JPGD_NOT_JPEG);
if (--bytesleft == 0) return stop_decoding(JPGD_NOT_JPEG);
lastchar = thischar;
thischar = get_bits(8);
if (lastchar == 0xFF) {
if (thischar == M_SOI) break;
else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
else if (thischar == M_EOI) return stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
}
}
// Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
thischar = (m_bit_buf >> 24) & 0xFF;
if (thischar != 0xFF) stop_decoding(JPGD_NOT_JPEG);
if (thischar != 0xFF) return stop_decoding(JPGD_NOT_JPEG);
return true;
}
// Find a start of frame (SOF) marker.
void jpeg_decoder::locate_sof_marker()
bool jpeg_decoder::locate_sof_marker()
{
locate_soi_marker();
int c = process_markers();
switch (c) {
if (!locate_soi_marker()) return false;
switch (process_markers()) {
case M_SOF2: {
m_progressive_flag = true;
read_sof_marker();
break;
return read_sof_marker();
}
case M_SOF0: /* baseline DCT */
case M_SOF1: { /* extended sequential DCT */
read_sof_marker();
break;
}
case M_SOF9: { /* Arithmetic coding */
stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
break;
}
default: {
stop_decoding(JPGD_UNSUPPORTED_MARKER);
break;
return read_sof_marker();
}
case M_SOF9: return stop_decoding(JPGD_NO_ARITHMETIC_SUPPORT);
default: return stop_decoding(JPGD_UNSUPPORTED_MARKER);
}
return true;
}
// Find a start of scan (SOS) marker.
int jpeg_decoder::locate_sos_marker()
bool jpeg_decoder::locate_sos_marker()
{
int c = process_markers();
auto c = process_markers();
if (c == M_EOI) return false;
else if (c != M_SOS) stop_decoding(JPGD_UNEXPECTED_MARKER);
read_sos_marker();
return true;
else if (c != M_SOS) return stop_decoding(JPGD_UNEXPECTED_MARKER);
return read_sos_marker();
}
@ -1828,7 +1815,7 @@ void jpeg_decoder::load_next_row()
// Restart interval processing.
void jpeg_decoder::process_restart()
bool jpeg_decoder::process_restart()
{
int i;
int c = 0;
@ -1842,15 +1829,15 @@ void jpeg_decoder::process_restart()
for (i = 1536; i > 0; i--) {
if (get_char() == 0xFF) break;
}
if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
if (i == 0) return stop_decoding(JPGD_BAD_RESTART_MARKER);
for ( ; i > 0; i--) {
if ((c = get_char()) != 0xFF) break;
}
if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
if (i == 0) return stop_decoding(JPGD_BAD_RESTART_MARKER);
// Is it the expected marker? If not, something bad happened.
if (c != (m_next_restart_num + M_RST0)) stop_decoding(JPGD_BAD_RESTART_MARKER);
if (c != (m_next_restart_num + M_RST0)) return stop_decoding(JPGD_BAD_RESTART_MARKER);
// Reset each component's DC prediction values.
memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
@ -1863,6 +1850,8 @@ void jpeg_decoder::process_restart()
m_bits_left = 16;
get_bits_no_markers(16);
get_bits_no_markers(16);
return true;
}
@ -1873,10 +1862,12 @@ static inline int dequantize_ac(int c, int q)
}
// Decodes and dequantizes the next row of coefficients.
void jpeg_decoder::decode_next_row()
bool jpeg_decoder::decode_next_row()
{
for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
if ((m_restart_interval) && (m_restarts_left == 0)) {
if (!process_restart()) return false;
}
jpgd_block_t* p = m_pMCU_coefficients;
@ -1903,7 +1894,7 @@ void jpeg_decoder::decode_next_row()
if (s) {
if (r) {
if ((k + r) > 63) stop_decoding(JPGD_DECODE_ERROR);
if ((k + r) > 63) return stop_decoding(JPGD_DECODE_ERROR);
if (k < prev_num_set) {
int n = JPGD_MIN(r, prev_num_set - k);
int kt = k;
@ -1916,7 +1907,7 @@ void jpeg_decoder::decode_next_row()
p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
} else {
if (r == 15) {
if ((k + 16) > 64) stop_decoding(JPGD_DECODE_ERROR);
if ((k + 16) > 64) return stop_decoding(JPGD_DECODE_ERROR);
if (k < prev_num_set) {
int n = JPGD_MIN(16, prev_num_set - k);
int kt = k;
@ -1942,6 +1933,7 @@ void jpeg_decoder::decode_next_row()
else transform_mcu(mcu_row);
m_restarts_left--;
}
return true;
}
@ -2183,9 +2175,8 @@ int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len)
if ((m_error_code) || (!m_ready_flag)) return JPGD_FAILED;
if (m_total_lines_left == 0) return JPGD_DONE;
if (m_mcu_lines_left == 0) {
if (setjmp(m_jmp_state)) return JPGD_FAILED;
if (m_progressive_flag) load_next_row();
else decode_next_row();
else if (!decode_next_row()) return JPGD_FAILED;
// Find the EOI marker if that was the last row.
if (m_total_lines_left <= m_max_mcu_y_size) find_eoi();
m_mcu_lines_left = m_max_mcu_y_size;
@ -2341,20 +2332,21 @@ void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
// Verifies the quantization tables needed for this scan are available.
void jpeg_decoder::check_quant_tables()
bool jpeg_decoder::check_quant_tables()
{
for (int i = 0; i < m_comps_in_scan; i++) {
if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr) stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
if (!m_quant[m_comp_quant[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
}
return true;
}
// Verifies that all the Huffman tables needed for this scan are available.
void jpeg_decoder::check_huff_tables()
bool jpeg_decoder::check_huff_tables()
{
for (int i = 0; i < m_comps_in_scan; i++) {
if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
if ((m_spectral_start == 0) && !m_huff_num[m_comp_dc_tab[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
if ((m_spectral_end > 0) && !m_huff_num[m_comp_ac_tab[m_comp_list[i]]]) return stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
}
for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) {
@ -2363,6 +2355,7 @@ void jpeg_decoder::check_huff_tables()
make_huff_table(i, m_pHuff_tabs[i]);
}
}
return true;
}
@ -2418,7 +2411,7 @@ int jpeg_decoder::init_scan()
calc_mcu_block_order();
check_huff_tables();
check_quant_tables();
if (!check_quant_tables()) return false;
memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
@ -2435,19 +2428,20 @@ int jpeg_decoder::init_scan()
// Starts a frame. Determines if the number of components or sampling factors
// are supported.
void jpeg_decoder::init_frame()
bool jpeg_decoder::init_frame()
{
int i;
if (m_comps_in_frame == 1) {
if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
m_scan_type = JPGD_GRAYSCALE;
m_max_blocks_per_mcu = 1;
m_max_mcu_x_size = 8;
m_max_mcu_y_size = 8;
} else if (m_comps_in_frame == 3) {
if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)))
stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1))) {
return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
}
if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) {
m_scan_type = JPGD_YH1V1;
@ -2469,8 +2463,8 @@ void jpeg_decoder::init_frame()
m_max_blocks_per_mcu = 6;
m_max_mcu_x_size = 16;
m_max_mcu_y_size = 16;
} else stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
} else stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
} else return stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
} else return stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
@ -2491,7 +2485,7 @@ void jpeg_decoder::init_frame()
m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
// Should never happen
if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) stop_decoding(JPGD_ASSERTION_ERROR);
if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) return stop_decoding(JPGD_ASSERTION_ERROR);
// Allocate the coefficient buffer, enough for one MCU
m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
@ -2517,6 +2511,8 @@ void jpeg_decoder::init_frame()
m_total_lines_left = m_image_y_size;
m_mcu_lines_left = 0;
create_look_ups();
return true;
}
@ -2546,7 +2542,7 @@ inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, in
// The following methods decode the various types of m_blocks encountered
// in progressively encoded images.
void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
bool jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
{
int s, r;
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
@ -2557,25 +2553,28 @@ void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int
}
pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
p[0] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
return true;
}
void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
bool jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
{
if (pD->get_bits_no_markers(1)) {
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
p[0] |= (1 << pD->m_successive_low);
}
return true;
}
void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
bool jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
{
int k, s, r;
if (pD->m_eob_run) {
pD->m_eob_run--;
return;
return true;
}
jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
@ -2584,13 +2583,13 @@ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int
r = s >> 4;
s &= 15;
if (s) {
if ((k += r) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
if ((k += r) > 63) return pD->stop_decoding(JPGD_DECODE_ERROR);
r = pD->get_bits_no_markers(s);
s = JPGD_HUFF_EXTEND(r, s);
p[g_ZAG[k]] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
} else {
if (r == 15) {
if ((k += 15) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
if ((k += 15) > 63) return pD->stop_decoding(JPGD_DECODE_ERROR);
} else {
pD->m_eob_run = 1 << r;
if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
@ -2599,10 +2598,11 @@ void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int
}
}
}
return true;
}
void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
bool jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
{
int s, k, r;
int p1 = 1 << pD->m_successive_low;
@ -2619,7 +2619,7 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
r = s >> 4;
s &= 15;
if (s) {
if (s != 1) pD->stop_decoding(JPGD_DECODE_ERROR);
if (s != 1) return pD->stop_decoding(JPGD_DECODE_ERROR);
if (pD->get_bits_no_markers(1)) s = p1;
else s = m1;
} else {
@ -2667,11 +2667,12 @@ void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, in
}
pD->m_eob_run--;
}
return true;
}
// Decode a scan in a progressively encoded image.
void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
bool jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
{
int mcu_row, mcu_col, mcu_block;
int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS];
@ -2685,11 +2686,13 @@ void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
if ((m_restart_interval) && (m_restarts_left == 0)) {
if (!process_restart()) return false;
}
for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
component_id = m_mcu_org[mcu_block];
decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
if (!decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs)) return false;
if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
else {
@ -2714,15 +2717,16 @@ void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
}
}
}
return true;
}
// Decode a progressively encoded image.
void jpeg_decoder::init_progressive()
bool jpeg_decoder::init_progressive()
{
int i;
if (m_comps_in_frame == 4) stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
if (m_comps_in_frame == 4) return stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
// Allocate the coefficient buffers.
for (i = 0; i < m_comps_in_frame; i++) {
@ -2739,15 +2743,13 @@ void jpeg_decoder::init_progressive()
dc_only_scan = (m_spectral_start == 0);
refinement_scan = (m_successive_high != 0);
if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) return stop_decoding(JPGD_BAD_SOS_SPECTRAL);
if (dc_only_scan) {
if (m_spectral_end) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
} else if (m_comps_in_scan != 1) { /* AC scans can only contain one component */
stop_decoding(JPGD_BAD_SOS_SPECTRAL);
}
if (m_spectral_end) return stop_decoding(JPGD_BAD_SOS_SPECTRAL);
} else if (m_comps_in_scan != 1) return stop_decoding(JPGD_BAD_SOS_SPECTRAL); // AC scans can only contain one component
if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) return stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
if (dc_only_scan) {
if (refinement_scan) decode_block_func = decode_block_dc_refine;
@ -2756,7 +2758,7 @@ void jpeg_decoder::init_progressive()
if (refinement_scan) decode_block_func = decode_block_ac_refine;
else decode_block_func = decode_block_ac_first;
}
decode_scan(decode_block_func);
if (!decode_scan(decode_block_func)) return false;
m_bits_left = 16;
get_bits(16);
get_bits(16);
@ -2769,20 +2771,23 @@ void jpeg_decoder::init_progressive()
}
calc_mcu_block_order();
return true;
}
void jpeg_decoder::init_sequential()
bool jpeg_decoder::init_sequential()
{
if (!init_scan()) stop_decoding(JPGD_UNEXPECTED_MARKER);
if (!init_scan()) return stop_decoding(JPGD_UNEXPECTED_MARKER);
return true;
}
void jpeg_decoder::decode_start()
bool jpeg_decoder::decode_start()
{
init_frame();
if (m_progressive_flag) init_progressive();
else init_sequential();
if (!init_frame()) return false;
if (m_progressive_flag) return init_progressive();
return init_sequential();
}
@ -2795,7 +2800,6 @@ void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
{
if (setjmp(m_jmp_state)) return;
decode_init(pStream);
}
@ -2804,9 +2808,7 @@ int jpeg_decoder::begin_decoding()
{
if (m_ready_flag) return JPGD_SUCCESS;
if (m_error_code) return JPGD_FAILED;
if (setjmp(m_jmp_state)) return JPGD_FAILED;
decode_start();
if (!decode_start()) return JPGD_FAILED;
m_ready_flag = true;
return JPGD_SUCCESS;
@ -2970,7 +2972,7 @@ unsigned char* jpgdDecompress(jpeg_decoder* decoder)
if (!pImage_data) return nullptr;
for (int y = 0; y < image_height; y++) {
const uint8_t* pScan_line;
const uint8_t* pScan_line = nullptr;
uint32_t scan_line_len;
if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) {
tvg::free(pImage_data);

View file

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

View file

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

View file

@ -52,17 +52,17 @@ void GlRenderPass::addRenderTask(GlRenderTask* task)
void GlRenderPass::getMatrix(float *dst, const Matrix &matrix) const
{
const auto& vp = getViewport();
Matrix postMatrix{};
tvg::identity(&postMatrix);
translate(&postMatrix, {(float)-vp.x, (float)-vp.y});
const auto& vp = getViewport();
translate(&postMatrix, {(float)-vp.sx(), (float)-vp.sy()});
auto m = postMatrix * matrix;
float modelMatrix[16];
GET_MATRIX44(m, modelMatrix);
MVP_MATRIX(vp.w, vp.h);
MVP_MATRIX(vp.w(), vp.h());
MULTIPLY_MATRIX(mvp, modelMatrix, dst);
}

View file

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

View file

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

View file

@ -28,11 +28,11 @@
class GlRenderTarget
{
public:
GlRenderTarget() = default;
GlRenderTarget(uint32_t width, uint32_t height);
GlRenderTarget();
~GlRenderTarget();
void init(GLint resolveId);
void init(uint32_t width, uint32_t height, GLint resolveId);
void reset();
GLuint getFboId() { return mFbo; }
GLuint getResolveFboId() { return mResolveFbo; }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -121,19 +121,20 @@ struct SwSpan
struct SwRle
{
SwSpan *spans;
uint32_t alloc;
uint32_t size;
};
Array<SwSpan> spans;
struct SwBBox
{
SwPoint min, max;
void reset()
bool invalid() const
{
min.x = min.y = max.x = max.y = 0;
return spans.empty();
}
bool valid() const
{
return !invalid();
}
uint32_t size() const { return spans.count; }
SwSpan* data() const { return spans.data; }
};
struct SwFill
@ -210,14 +211,14 @@ struct SwDashStroke
struct SwShape
{
SwOutline* outline = nullptr;
SwStroke* stroke = nullptr;
SwFill* fill = nullptr;
SwRle* rle = nullptr;
SwRle* strokeRle = nullptr;
SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
SwOutline* outline = nullptr;
SwStroke* stroke = nullptr;
SwFill* fill = nullptr;
SwRle* rle = nullptr;
SwRle* strokeRle = nullptr;
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
@ -279,7 +280,7 @@ struct SwCompositor : RenderCompositor
SwSurface* recoverSfc; //Recover surface when composition is started
SwCompositor* recoverCmp; //Recover compositor when composition is done
SwImage image;
SwBBox bbox;
RenderRegion bbox;
bool valid;
};
@ -499,16 +500,15 @@ SwFixed mathLength(const SwPoint& pt);
int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
SwPoint mathTransform(const Point* to, const Matrix& transform);
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack);
void shapeReset(SwShape* shape);
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool hasComposite);
bool shapePrepared(const SwShape* shape);
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform);
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid);
void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
@ -523,8 +523,8 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke);
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
bool imagePrepare(SwImage* image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid);
bool imageGenRle(SwImage* image, const RenderRegion& bbox, bool antiAlias);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image);
void imageFree(SwImage* image);
@ -547,13 +547,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
SwRle* rleRender(const SwBBox* bbox);
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const RenderRegion& bbox, bool antiAlias);
SwRle* rleRender(const RenderRegion* bbox);
void rleFree(SwRle* rle);
void rleReset(SwRle* rle);
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
bool rleClip(SwRle* rle, const SwRle* clip);
bool rleClip(SwRle* rle, const SwBBox* clip);
bool rleClip(SwRle* rle, const RenderRegion* clip);
SwMpool* mpoolInit(uint32_t threads);
bool mpoolTerm(SwMpool* mpool);
@ -568,7 +568,7 @@ void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterShape(SwSurface* surface, SwShape* shape, RenderColor& c);
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity);
bool rasterStroke(SwSurface* surface, SwShape* shape, RenderColor& c);
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
@ -576,7 +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 rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped);
void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, bool flipped);
void rasterUnpremultiply(RenderSurface* surface);
void rasterPremultiply(RenderSurface* surface);
bool rasterConvertCS(RenderSurface* surface, ColorSpace to);

View file

@ -72,7 +72,7 @@ static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool,
/* External Class Implementation */
/************************************************************************/
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
bool imagePrepare(SwImage* image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid)
{
image->direct = _onlyShifted(transform);
@ -91,13 +91,13 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg
}
if (!_genOutline(image, transform, mpool, tid)) return false;
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
return mathUpdateOutlineBBox(image->outline, clipBox, renderBox, image->direct);
}
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
bool imageGenRle(SwImage* image, const RenderRegion& renderBox, bool antiAlias)
{
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
if ((image->rle = rleRender(image->rle, image->outline, renderBox, antiAlias))) return true;
return false;
}

View file

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

View file

@ -61,7 +61,7 @@ static inline int _gaussianRemap(int end, int idx)
//TODO: SIMD OPTIMIZATION?
template<int border = 0>
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, bool flipped)
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, int32_t dimension, bool flipped)
{
if (flipped) {
src += (bbox.min.x * stride + bbox.min.y) << 2;
@ -136,17 +136,17 @@ static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality)
bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params)
{
//bbox region expansion for feathering
auto& region = params->extend;
//region expansion for feathering
auto& bbox = params->extend;
auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends;
if (params->direction != 2) {
region.x = -extra;
region.w = extra * 2;
bbox.min.x = -extra;
bbox.max.x = extra;
}
if (params->direction != 1) {
region.y = -extra;
region.h = extra * 2;
bbox.min.y = -extra;
bbox.max.y = extra;
}
return true;
@ -230,7 +230,7 @@ struct SwDropShadow : SwGaussianBlur
//TODO: SIMD OPTIMIZATION?
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const SwBBox& bbox, int32_t dimension, uint32_t color, bool flipped)
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const RenderRegion& bbox, int32_t dimension, uint32_t color, bool flipped)
{
if (flipped) {
src += (bbox.min.x * stride + bbox.min.y);
@ -267,20 +267,20 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i
}
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct)
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, RenderRegion& bbox, SwPoint& offset, uint8_t opacity, bool direct)
{
src += (region.min.y * sstride + region.min.x);
dst += (region.min.y * dstride + region.min.x);
src += (bbox.min.y * sstride + bbox.min.x);
dst += (bbox.min.y * dstride + bbox.min.x);
auto w = region.max.x - region.min.x;
auto h = region.max.y - region.min.y;
auto w = bbox.max.x - bbox.min.x;
auto h = bbox.max.y - bbox.min.y;
auto translucent = (direct || opacity < 255);
//shift offset
if (region.min.x + offset.x < 0) src -= offset.x;
if (bbox.min.x + offset.x < 0) src -= offset.x;
else dst += offset.x;
if (region.min.y + offset.y < 0) src -= (offset.y * sstride);
if (bbox.min.y + offset.y < 0) src -= (offset.y * sstride);
else dst += (offset.y * dstride);
for (auto y = 0; y < h; ++y) {
@ -294,20 +294,19 @@ static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstr
bool effectDropShadowRegion(RenderEffectDropShadow* params)
{
//bbox region expansion for feathering
auto& region = params->extend;
//region expansion for feathering
auto& bbox = params->extend;
auto& offset = static_cast<SwDropShadow*>(params->rd)->offset;
auto extra = static_cast<SwDropShadow*>(params->rd)->extends;
region.x = -extra;
region.w = extra * 2;
region.y = -extra;
region.h = extra * 2;
bbox.min = {-extra, -extra};
bbox.max = {extra, extra};
region.x = std::min(region.x + (int32_t)offset.x, region.x);
region.y = std::min(region.y + (int32_t)offset.y, region.y);
region.w += abs(offset.x);
region.h += abs(offset.y);
if (offset.x < 0) bbox.min.x += (int32_t) offset.x;
else bbox.max.x += offset.x;
if (offset.y < 0) bbox.min.y += (int32_t) offset.y;
else bbox.max.y += offset.y;
return true;
}

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

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)
{
auto span = rle->spans;
//32bit channels
if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a);
uint32_t src;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
ARRAY_FOREACH(span, rle->spans) {
auto dst = &surface->buf32[span->y * surface->stride + span->x];
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
else src = color;
@ -112,7 +110,7 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, c
//8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) {
uint8_t src;
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
ARRAY_FOREACH(span, rle->spans) {
auto dst = &surface->buf8[span->y * surface->stride + span->x];
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
else src = c.a;
@ -126,29 +124,26 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, c
}
static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c)
static bool inline cRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
//32bits channels
if (surface->channelSize == sizeof(uint32_t)) {
auto color = surface->join(c.r, c.g, c.b, c.a);
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
auto ialpha = 255 - c.a;
for (uint32_t y = 0; y < h; ++y) {
for (uint32_t y = 0; y < bbox.h(); ++y) {
auto dst = &buffer[y * surface->stride];
for (uint32_t x = 0; x < w; ++x, ++dst) {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) {
*dst = color + ALPHA_BLEND(*dst, ialpha);
}
}
//8bit grayscale
} else if (surface->channelSize == sizeof(uint8_t)) {
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
auto ialpha = ~c.a;
for (uint32_t y = 0; y < h; ++y) {
for (uint32_t y = 0; y < bbox.h(); ++y) {
auto dst = &buffer[y * surface->stride];
for (uint32_t x = 0; x < w; ++x, ++dst) {
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) {
*dst = c.a + MULTIPLY(*dst, ialpha);
}
}

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

View file

@ -52,33 +52,33 @@ static float xa, xb, ua, va;
//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) {
regionTop = region->min.y;
regionBottom = region->max.y;
if (bbox) {
bboxTop = bbox->min.y;
bboxBottom = bbox->max.y;
} else {
regionTop = image->rle->spans->y;
regionBottom = image->rle->spans[image->rle->size - 1].y;
bboxTop = image->rle->spans.first().y;
bboxBottom = image->rle->spans.last().y;
}
if (yStart < regionTop) yStart = regionTop;
if (yEnd > regionBottom) yEnd = regionBottom;
if (yStart < bboxTop) yStart = bboxTop;
if (yEnd > bboxBottom) yEnd = bboxBottom;
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()");
return false;
}
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
{
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
@ -94,16 +94,16 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
uint32_t* buf;
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
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
if (bbox) {
minx = bbox->min.x;
maxx = bbox->max.x;
} else {
span = image->rle->spans;
span = image->rle->data();
while (span->y < yStart) {
++span;
++spanIdx;
@ -116,11 +116,11 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
x1 = (int32_t)_xa;
x2 = (int32_t)_xb;
if (!region) {
if (!bbox) {
minx = INT32_MAX;
maxx = 0;
//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 (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
@ -195,7 +195,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
_ua += _dudya;
_va += _dvdya;
if (!region && spanIdx >= image->rle->size) break;
if (!bbox && spanIdx >= image->rle->size()) break;
++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 _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;
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
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
if (bbox) {
minx = bbox->min.x;
maxx = bbox->max.x;
} else {
span = image->rle->spans;
span = image->rle->data();
while (span->y < yStart) {
++span;
++spanIdx;
@ -249,11 +249,11 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
x1 = (int32_t)_xa;
x2 = (int32_t)_xb;
if (!region) {
if (!bbox) {
minx = INT32_MAX;
maxx = 0;
//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 (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
@ -387,7 +387,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
_ua += _dudya;
_va += _dvdya;
if (!region && spanIdx >= image->rle->size) break;
if (!bbox && spanIdx >= image->rle->size()) break;
++y;
}
@ -399,7 +399,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
/* This mapping algorithm is based on Mikael Kalms's. */
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const RenderRegion* bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
{
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@ -460,7 +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[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 blending = _blending(surface); //Blending required
@ -479,7 +479,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible
if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
off_y = y[0] < bboxTop ? (bboxTop - y[0]) : 0;
xa += (off_y * dxdya);
ua += (off_y * dudya);
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);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 1);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
}
upper = true;
}
//Draw lower segment if possibly visible
if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
off_y = y[1] < bboxTop ? (bboxTop - y[1]) : 0;
if (!upper) {
xa += (off_y * dxdya);
ua += (off_y * dudya);
@ -510,12 +510,12 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
dxdyb = dxdy[2];
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 2);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
}
}
//Longer edge is on the right side
@ -527,7 +527,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible
if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
off_y = y[0] < bboxTop ? (bboxTop - y[0]) : 0;
xb += (off_y *dxdyb);
// 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);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 3);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
}
upper = true;
}
//Draw lower segment if possibly visible
if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
off_y = y[1] < bboxTop ? (bboxTop - y[1]) : 0;
if (!upper) xb += (off_y *dxdyb);
// 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);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 4);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
}
}
}
}
static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const RenderRegion* bbox)
{
auto yStart = static_cast<int>(ymin);
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));
aaSpans->yStart = yStart;
@ -863,7 +863,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
| / |
3 -- 2
*/
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity)
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const RenderRegion* bbox, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
@ -871,7 +871,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
}
//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.
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;
}
auto aaSpans = _AASpans(ys, ye, image, region);
auto aaSpans = _AASpans(ys, ye, image, bbox);
if (!aaSpans) return true;
Polygon polygon;
@ -898,14 +898,14 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
polygon.vertex[1] = vertices[1];
polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
//Draw the second polygon
polygon.vertex[0] = vertices[1];
polygon.vertex[1] = vertices[2];
polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
#if 0
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {

View file

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

View file

@ -324,27 +324,18 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
if (!rw.antiAlias) coverage = 255;
//see whether we can add this span to the current list
if (rle->size > 0) {
auto span = rle->spans + rle->size - 1;
if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
if (!rle->spans.empty()) {
auto& span = rle->spans.last();
if ((span.coverage == coverage) && (span.y == y) && (span.x + span.len == x)) {
//Clip x range
SwCoord xOver = 0;
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
span->len += (aCount + xOver);
span.len += (aCount + xOver);
return;
}
}
//span pool is full, grow it.
if (rle->size >= rle->alloc) {
auto newSize = (rle->size > 0) ? (rle->size * 2) : 256;
if (rle->alloc < newSize) {
rle->alloc = newSize;
rle->spans = tvg::realloc<SwSpan*>(rle->spans, rle->alloc * sizeof(SwSpan));
}
}
//Clip x range
SwCoord xOver = 0;
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
@ -357,12 +348,7 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
if (aCount + xOver <= 0) return;
//add a span to the current list
auto span = rle->spans + rle->size;
span->x = x;
span->y = y;
span->len = (aCount + xOver);
span->coverage = coverage;
rle->size++;
rle->spans.next() = {(uint16_t)x, (uint16_t)y, uint16_t(aCount + xOver), (uint8_t)coverage};
}
@ -737,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 */
/************************************************************************/
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;
@ -865,8 +748,8 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
rw.area = 0;
rw.cover = 0;
rw.invalid = true;
rw.cellMin = renderRegion.min;
rw.cellMax = renderRegion.max;
rw.cellMin = {bbox.min.x, bbox.min.y};
rw.cellMax = {bbox.max.x, bbox.max.y};
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.outline = const_cast<SwOutline*>(outline);
@ -874,8 +757,9 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
rw.bandShoot = 0;
rw.antiAlias = antiAlias;
if (!rle) rw.rle = tvg::calloc<SwRle*>(1, sizeof(SwRle));
if (!rle) rw.rle = new SwRle;
else rw.rle = rle;
rw.rle->spans.reserve(256);
//Generate RLE
Band bands[BAND_SIZE];
@ -938,7 +822,10 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
/* This is too complex for a single scanline; there must
be some problems */
if (middle == bottom) goto error;
if (middle == bottom) {
rleFree(rw.rle);
return nullptr;
}
if (bottom - top >= rw.bandSize) ++rw.bandShoot;
@ -949,34 +836,26 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
++band;
}
}
if (rw.bandShoot > 8 && rw.bandSize > 16)
if (rw.bandShoot > 8 && rw.bandSize > 16) {
rw.bandSize = (rw.bandSize >> 1);
}
return rw.rle;
error:
tvg::free(rw.rle);
return nullptr;
}
SwRle* rleRender(const SwBBox* bbox)
SwRle* rleRender(const RenderRegion* bbox)
{
auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
auto rle = tvg::calloc<SwRle*>(sizeof(SwRle), 1);
rle->spans.reserve(bbox->h());
rle->spans.count = bbox->h();
auto rle = tvg::malloc<SwRle*>(sizeof(SwRle));
rle->spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * height);
rle->size = height;
rle->alloc = height;
//cheaper without push()
auto x = uint16_t(bbox->min.x);
auto y = uint16_t(bbox->min.y);
auto len = uint16_t(bbox->w());
auto span = rle->spans;
for (uint16_t i = 0; i < height; ++i, ++span) {
span->x = bbox->min.x;
span->y = bbox->min.y + i;
span->len = width;
span->coverage = 255;
ARRAY_FOREACH(p, rle->spans) {
*p = {x, y++, len, 255};
}
return rle;
@ -985,44 +864,88 @@ SwRle* rleRender(const SwBBox* bbox)
void rleReset(SwRle* rle)
{
if (!rle) return;
rle->size = 0;
if (rle) rle->spans.clear();
}
void rleFree(SwRle* rle)
{
if (!rle) return;
if (rle->spans) tvg::free(rle->spans);
tvg::free(rle);
delete(rle);
}
bool rleClip(SwRle *rle, const SwRle *clip)
{
if (rle->size == 0 || clip->size == 0) return false;
auto spanCnt = 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);
if (rle->spans.empty() || clip->spans.empty()) return false;
_replaceClipSpan(rle, spans, spansEnd - spans);
Array<SwSpan> out;
out.reserve(std::max(rle->spans.count, clip->spans.count));
TVGLOG("SW_ENGINE", "Using Path Clipping!");
auto spans = rle->data();
auto end = rle->spans.end();
auto cspans = clip->data();
auto cend = clip->spans.end();
while(spans < end && cspans < cend) {
//align y-coordinates.
if (cspans->y > spans->y) {
++spans;
continue;
}
if (spans->y > cspans->y) {
++cspans;
continue;
}
//try clipping with all clip spans which have a same y-coordinate.
auto temp = cspans;
while(temp < cend && temp->y == cspans->y) {
//span must be left(x1) to right(x2) direction. Not intersected.
if ((spans->x + spans->len) < spans->x || (temp->x + temp->len) < temp->x) {
++temp;
continue;
}
//clip span region
auto x = std::max(spans->x, temp->x);
auto len = std::min((spans->x + spans->len), (temp->x + temp->len)) - x;
if (len > 0) out.next() = {uint16_t(x), temp->y, uint16_t(len), (uint8_t)(((spans->coverage * temp->coverage) + 0xff) >> 8)};
++temp;
}
++spans;
}
out.move(rle->spans);
return true;
}
bool rleClip(SwRle *rle, const SwBBox* clip)
//Need to confirm: dead code?
bool rleClip(SwRle *rle, const RenderRegion* clip)
{
if (rle->size == 0) return false;
auto spans = tvg::malloc<SwSpan*>(sizeof(SwSpan) * (rle->size));
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
if (rle->spans.empty() || clip->invalid()) return false;
_replaceClipSpan(rle, spans, spansEnd - spans);
auto& min = clip->min;
auto& max = clip->max;
TVGLOG("SW_ENGINE", "Using Box Clipping!");
Array<SwSpan> out;
out.reserve(rle->spans.count);
auto data = out.data;
uint16_t x, len;
ARRAY_FOREACH(p, rle->spans) {
if (p->y >= max.y) break;
if (p->y < min.y || p->x >= max.x || (p->x + p->len) <= min.x) continue;
if (p->x < min.x) {
x = min.x;
len = std::min((p->len - (x - p->x)), (max.x - x));
} else {
x = p->x;
len = std::min(p->len, uint16_t(max.x - x));
}
if (len > 0) {
*data = {x, p->y, len, p->coverage};
++data;
++out.count;
}
}
out.move(rle->spans);
return true;
}
}

View file

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

View file

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

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->target(context, id, w, h)) return Result::Unknown;
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
renderer->viewport(pImpl->vport);
//Paints must be updated again with this new target.

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;
}
float region[4] = {float(before.x), float(before.x + before.w), float(before.y), float(before.y + before.h)};
float region[4] = {float(before.min.x), float(before.max.x), float(before.min.y), float(before.max.y)};
//figure out if the clipper is a superset of the current viewport(before) region
if (min.x <= region[0] && max.x >= region[1] && min.y <= region[2] && max.y >= region[3]) {
@ -66,7 +66,7 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix&
return Result::Success;
//figure out if the clipper is totally outside of the viewport
} else if (max.x <= region[0] || min.x >= region[1] || max.y <= region[2] || min.y >= region[3]) {
renderer->viewport({0, 0, 0, 0});
renderer->viewport({});
return Result::Success;
}
return Result::InsufficientCondition;
@ -122,13 +122,13 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
if (v1.x > v2.x) std::swap(v1.x, v2.x);
if (v1.y > v2.y) std::swap(v1.y, v2.y);
after.x = static_cast<int32_t>(nearbyint(v1.x));
after.y = static_cast<int32_t>(nearbyint(v1.y));
after.w = static_cast<int32_t>(nearbyint(v2.x)) - after.x;
after.h = static_cast<int32_t>(nearbyint(v2.y)) - after.y;
after.min.x = static_cast<int32_t>(nearbyint(v1.x));
after.min.y = static_cast<int32_t>(nearbyint(v1.y));
after.max.x = static_cast<int32_t>(nearbyint(v2.x));
after.max.y = static_cast<int32_t>(nearbyint(v2.y));
if (after.w < 0) after.w = 0;
if (after.h < 0) after.h = 0;
if (after.max.x < after.min.x) after.max.x = after.min.x;
if (after.max.y < after.min.y) after.max.y = after.min.y;
after.intersect(before);
renderer->viewport(after);
@ -168,7 +168,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
ret->pImpl->opacity = opacity;
if (maskData) ret->mask(maskData->target->duplicate(), maskData->method);
if (clipper) ret->clip(clipper->duplicate());
if (clipper) ret->clip(static_cast<Shape*>(clipper->duplicate()));
return ret;
}
@ -185,7 +185,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
PAINT_METHOD(region, bounds(renderer));
if (MASK_REGION_MERGING(maskData->method)) region.add(PAINT(maskData->target)->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
if (region.invalid()) return true;
cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking);
if (renderer->beginComposite(cmp, MaskMethod::None, 255)) {
maskData->target->pImpl->render(renderer);
@ -376,16 +376,18 @@ Paint* Paint::duplicate() const noexcept
}
Result Paint::clip(Paint* clipper) noexcept
Result Paint::clip(Shape* clipper) noexcept
{
if (clipper && clipper->type() != Type::Shape) {
TVGERR("RENDERER", "Clipping only supports the Shape!");
return Result::NonSupport;
}
return pImpl->clip(clipper);
}
Shape* Paint::clip() const noexcept
{
return pImpl->clipper;
}
Result Paint::mask(Paint* target, MaskMethod method) noexcept
{
return pImpl->mask(target, method);

View file

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

View file

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

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)
{
auto x1 = x + w;
auto y1 = y + h;
auto x2 = rhs.x + rhs.w;
auto y2 = rhs.y + rhs.h;
if (min.x < rhs.min.x) min.x = rhs.min.x;
if (min.y < rhs.min.y) min.y = rhs.min.y;
if (max.x > rhs.max.x) max.x = rhs.max.x;
if (max.y > rhs.max.y) max.y = rhs.max.y;
x = (x > rhs.x) ? x : rhs.x;
y = (y > rhs.y) ? y : rhs.y;
w = ((x1 < x2) ? x1 : x2) - x;
h = ((y1 < y2) ? y1 : y2) - y;
if (w < 0) w = 0;
if (h < 0) h = 0;
}
void RenderRegion::add(const RenderRegion& rhs)
{
if (rhs.x < x) {
w += (x - rhs.x);
x = rhs.x;
}
if (rhs.y < y) {
h += (y - rhs.y);
y = rhs.y;
}
if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x;
if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y;
// Not intersected: collapse to zero-area region
if (max.x < min.x) max.x = min.x;
if (max.y < min.y) max.y = min.y;
}
/************************************************************************/

View file

@ -94,16 +94,47 @@ struct RenderCompositor
struct RenderRegion
{
int32_t x, y, w, h;
struct {
int32_t x, y;
} min;
struct {
int32_t x, y;
} max;
static constexpr RenderRegion intersect(const RenderRegion& lhs, const RenderRegion& rhs)
{
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 add(const RenderRegion& rhs);
void add(const RenderRegion& rhs)
{
if (rhs.min.x < min.x) min.x = rhs.min.x;
if (rhs.min.y < min.y) min.y = rhs.min.y;
if (rhs.max.x > max.x) max.x = rhs.max.x;
if (rhs.max.y > max.y) max.y = rhs.max.y;
}
bool operator==(const RenderRegion& rhs) const
{
if (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h) return true;
return false;
return (min.x == rhs.min.x && min.y == rhs.min.y && max.x == rhs.max.x && max.y == rhs.max.y);
}
void reset() { min.x = min.y = max.x = max.y = 0; }
bool valid() const { return (max.x > min.x && max.y > min.y); }
bool invalid() const { return !valid(); }
int32_t sx() const { return min.x; }
int32_t sy() const { return min.y; }
int32_t sw() const { return max.x - min.x; }
int32_t sh() const { return max.y - min.y; }
uint32_t x() const { return (uint32_t) sx(); }
uint32_t y() const { return (uint32_t) sy(); }
uint32_t w() const { return (uint32_t) sw(); }
uint32_t h() const { return (uint32_t) sh(); }
};
struct RenderPath
@ -272,7 +303,7 @@ struct RenderShape
struct RenderEffect
{
RenderData rd = nullptr;
RenderRegion extend = {0, 0, 0, 0};
RenderRegion extend{};
SceneEffect type;
bool valid = false;
@ -353,7 +384,7 @@ struct RenderEffectTint : RenderEffect
inst->white[0] = va_arg(args, int);
inst->white[1] = va_arg(args, int);
inst->white[2] = va_arg(args, int);
inst->intensity = (uint8_t)(va_arg(args, double) * 2.55);
inst->intensity = (uint8_t)(static_cast<float>(va_arg(args, double)) * 2.55f);
inst->type = SceneEffect::Tint;
return inst;
}

View file

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

View file

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

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

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->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown;
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
renderer->viewport(pImpl->vport);
//Paints must be updated again with this new target.

View file

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

View file

@ -268,4 +268,35 @@ void WgContext::releaseQueue(WGPUQueue& queue)
wgpuQueueRelease(queue);
queue = nullptr;
}
}
}
WGPUCommandEncoder WgContext::createCommandEncoder()
{
WGPUCommandEncoderDescriptor commandEncoderDesc{};
return wgpuDeviceCreateCommandEncoder(device, &commandEncoderDesc);
}
void WgContext::submitCommandEncoder(WGPUCommandEncoder commandEncoder)
{
const WGPUCommandBufferDescriptor commandBufferDesc{};
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
wgpuQueueSubmit(queue, 1, &commandsBuffer);
wgpuCommandBufferRelease(commandsBuffer);
}
void WgContext::releaseCommandEncoder(WGPUCommandEncoder& commandEncoder)
{
if (commandEncoder) {
wgpuCommandEncoderRelease(commandEncoder);
commandEncoder = nullptr;
}
}
bool WgContext::invalid()
{
return !instance || !device;
}

View file

@ -71,10 +71,12 @@ struct WgContext {
// release buffer objects
void releaseBuffer(WGPUBuffer& buffer);
bool invalid()
{
return !instance || !device;
}
// command encoder
WGPUCommandEncoder createCommandEncoder();
void submitCommandEncoder(WGPUCommandEncoder encoder);
void releaseCommandEncoder(WGPUCommandEncoder& encoder);
bool invalid();
};
#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.initialize(context);
stageBuffer.initialize(context);
// initialize opacity pool
initPools(context);
// allocate global view matrix handles
@ -37,7 +38,7 @@ void WgCompositor::initialize(WgContext& context, uint32_t width, uint32_t heigh
// create render targets handles
resize(context, width, height);
// composition and blend geometries
meshData.blitBox(context);
meshDataBlit.blitBox(context);
}
@ -54,7 +55,7 @@ void WgCompositor::initPools(WgContext& context)
void WgCompositor::release(WgContext& context)
{
// composition and blend geometries
meshData.release(context);
meshDataBlit.release(context);
// release render targets habdles
resize(context, 0, 0);
// release opacity pool
@ -62,6 +63,8 @@ void WgCompositor::release(WgContext& context)
// release global view matrix handles
context.layouts.releaseBindGroup(bindGroupViewMat);
context.releaseBuffer(bufferViewMat);
// release stage buffer
stageBuffer.release(context);
// release pipelines
pipelines.release(context);
}
@ -81,9 +84,9 @@ void WgCompositor::resize(WgContext& context, uint32_t width, uint32_t height) {
// release existig handles
if ((this->width != width) || (this->height != height)) {
context.layouts.releaseBindGroup(bindGroupStorageTemp);
// release intermediate render storages
storageTemp1.release(context);
storageTemp0.release(context);
// release intermediate render target
targetTemp1.release(context);
targetTemp0.release(context);
// release global stencil buffer handles
context.releaseTextureView(texViewDepthStencilMS);
context.releaseTexture(texDepthStencilMS);
@ -107,54 +110,58 @@ void WgCompositor::resize(WgContext& context, uint32_t width, uint32_t height) {
texViewDepthStencil = context.createTextureView(texDepthStencil);
texDepthStencilMS = context.createTexAttachement(width, height, WGPUTextureFormat_Depth24PlusStencil8, 4);
texViewDepthStencilMS = context.createTextureView(texDepthStencilMS);
// initialize intermediate render storages
storageTemp0.initialize(context, width, height);
storageTemp1.initialize(context, width, height);
bindGroupStorageTemp = context.layouts.createBindGroupStrorage2RO(storageTemp0.texView, storageTemp1.texView);
// initialize intermediate render targets
targetTemp0.initialize(context, width, height);
targetTemp1.initialize(context, width, height);
bindGroupStorageTemp = context.layouts.createBindGroupStrorage2RO(targetTemp0.texView, targetTemp1.texView);
}
}
RenderRegion WgCompositor::shrinkRenderRegion(RenderRegion& rect)
{
// cut viewport to screen dimensions
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 };
return RenderRegion::intersect(rect, {{0, 0}, {(int32_t)width, (int32_t)height}});
}
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);
}
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(src);
assert(commandEncoder);
const WGPUImageCopyTexture texSrc { .texture = src->texture, .origin = { .x = (uint32_t)region.x, .y = (uint32_t)region.y } };
const WGPUImageCopyTexture texDst { .texture = dst->texture, .origin = { .x = (uint32_t)region.x, .y = (uint32_t)region.y } };
const WGPUExtent3D copySize { .width = (uint32_t)region.w, .height = (uint32_t)region.h, .depthOrArrayLayers = 1 };
const WGPUImageCopyTexture texSrc { .texture = src->texture, .origin = { .x = region.x(), .y = region.y() } };
const WGPUImageCopyTexture texDst { .texture = dst->texture, .origin = { .x = region.x(), .y = region.y() } };
const WGPUExtent3D copySize { .width = region.w(), .height = region.h(), .depthOrArrayLayers = 1 };
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(commandEncoder);
// do not start same render bass
if (target == currentTarget) return;
// we must to end render pass first
endRenderPass();
this->currentTarget = target;
// start new render pass
this->commandEncoder = commandEncoder;
const WGPURenderPassDepthStencilAttachment depthStencilAttachment{
.view = texViewDepthStencilMS,
.depthLoadOp = WGPULoadOp_Clear, .depthStoreOp = WGPUStoreOp_Discard, .depthClearValue = 1.0f,
.stencilLoadOp = WGPULoadOp_Clear, .stencilStoreOp = WGPUStoreOp_Discard, .stencilClearValue = 0
.depthLoadOp = WGPULoadOp_Clear,
.depthStoreOp = WGPUStoreOp_Discard,
.depthClearValue = 1.0f,
.stencilLoadOp = WGPULoadOp_Clear,
.stencilStoreOp = WGPUStoreOp_Discard,
.stencilClearValue = 0
};
const WGPURenderPassColorAttachment colorAttachment{
.view = target->texViewMS,
@ -163,7 +170,6 @@ void WgCompositor::beginRenderPass(WGPUCommandEncoder commandEncoder, WgRenderSt
.loadOp = clear ? WGPULoadOp_Clear : WGPULoadOp_Load,
.storeOp = WGPUStoreOp_Store,
.clearValue = clearColor
};
WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
renderPassEncoder = wgpuCommandEncoderBeginRenderPass(commandEncoder, &renderPassDesc);
@ -177,11 +183,37 @@ void WgCompositor::endRenderPass()
assert(renderPassEncoder);
wgpuRenderPassEncoderEnd(renderPassEncoder);
wgpuRenderPassEncoderRelease(renderPassEncoder);
this->renderPassEncoder = nullptr;
this->currentTarget = nullptr;
renderPassEncoder = 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)
{
@ -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(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(src);
assert(mask);
assert(renderPassEncoder);
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);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, mask->bindGroupTexure, 0, nullptr);
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{
.view = texViewDepthStencil,
.depthLoadOp = WGPULoadOp_Load, .depthStoreOp = WGPUStoreOp_Discard,
.stencilLoadOp = WGPULoadOp_Load, .stencilStoreOp = WGPUStoreOp_Discard
.depthLoadOp = WGPULoadOp_Load,
.depthStoreOp = WGPUStoreOp_Discard,
.stencilLoadOp = WGPULoadOp_Load,
.stencilStoreOp = WGPUStoreOp_Discard
};
const WGPURenderPassColorAttachment colorAttachment {
.view = dstView,
@ -279,24 +315,63 @@ void WgCompositor::blit(WgContext& context, WGPUCommandEncoder encoder, WgRender
.storeOp = WGPUStoreOp_Store,
};
const WGPURenderPassDescriptor renderPassDesc{ .colorAttachmentCount = 1, .colorAttachments = &colorAttachment, .depthStencilAttachment = &depthStencilAttachment };
WGPURenderPassEncoder renderPass = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderSetBindGroup(renderPass, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPass, pipelines.blit);
meshData.drawImage(context, renderPass);
wgpuRenderPassEncoderEnd(renderPass);
wgpuRenderPassEncoderRelease(renderPass);
renderPassEncoder = wgpuCommandEncoderBeginRenderPass(encoder, &renderPassDesc);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, src->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.blit);
drawMeshImage(context, &meshDataBlit);
wgpuRenderPassEncoderEnd(renderPassEncoder);
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)
{
assert(renderData);
assert(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) 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);
if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
@ -305,7 +380,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder);
drawMeshFan(context, (*p));
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -322,7 +397,7 @@ void WgCompositor::drawShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// 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(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) return;
if (renderData->meshGroupShapes.meshes.count == 0) return;
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false);
// 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
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
@ -349,12 +422,12 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder);
drawMeshFan(context, (*p));
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 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;
WgRenderSettings& settings = renderData->renderSettingsShape;
if (settings.fillType == WgRenderSettingsType::Solid) {
@ -368,7 +441,7 @@ void WgCompositor::blendShape(WgContext& context, WgRenderDataShape* renderData,
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
}
// 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(renderPassEncoder);
assert(renderData->meshGroupShapes.meshes.count == renderData->meshGroupShapesBBox.meshes.count);
if (renderData->renderSettingsShape.skip) 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);
if (renderData->renderSettingsShape.skip || renderData->meshGroupShapes.meshes.count == 0 || renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// setup stencil rules
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
@ -389,12 +461,12 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
// draw to stencil (first pass)
ARRAY_FOREACH(p, renderData->meshGroupShapes.meshes)
(*p)->drawFan(context, renderPassEncoder);
drawMeshFan(context, (*p));
// merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -411,7 +483,7 @@ void WgCompositor::clipShape(WgContext& context, WgRenderDataShape* renderData)
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// 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(renderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip) 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);
if (renderData->renderSettingsStroke.skip || renderData->meshGroupStrokes.meshes.count == 0 || renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules
@ -432,7 +503,7 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -449,7 +520,7 @@ void WgCompositor::drawStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// 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(renderPassEncoder);
assert(renderData->meshGroupStrokes.meshes.count == renderData->meshGroupStrokesBBox.meshes.count);
if (renderData->renderSettingsStroke.skip) 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 storage
WgRenderStorage *target = currentTarget;
if (renderData->renderSettingsStroke.skip || renderData->meshGroupStrokes.meshes.count == 0 || renderData->viewport.invalid()) return;
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
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)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules
@ -476,12 +546,12 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 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;
WgRenderSettings& settings = renderData->renderSettingsStroke;
if (settings.fillType == WgRenderSettingsType::Solid) {
@ -495,7 +565,7 @@ void WgCompositor::blendStrokes(WgContext& context, WgRenderDataShape* renderDat
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial_blend[blendMethodInd]);
}
// 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);
if (renderData->renderSettingsStroke.skip) 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);
if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// draw strokes to stencil (first pass)
for (uint32_t i = 0; i < renderData->meshGroupStrokes.meshes.count; i++) {
// setup stencil rules
@ -517,12 +587,12 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
// draw to stencil (first pass)
renderData->meshGroupStrokes.meshes[i]->draw(context, renderPassEncoder);
drawMesh(context, renderData->meshGroupStrokes.meshes[i]);
// merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// setup fill rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
@ -539,7 +609,7 @@ void WgCompositor::clipStrokes(WgContext& context, WgRenderDataShape* renderData
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.radial);
}
// 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(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// draw stencil
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// draw image
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
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(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false);
// setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// blend image
uint32_t blendMethodInd = (uint32_t)blendMethod;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 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]);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
};
@ -599,65 +669,65 @@ void WgCompositor::clipImage(WgContext& context, WgRenderDataPicture* renderData
{
assert(renderData);
assert(renderPassEncoder);
if ((renderData->viewport.w <= 0) || (renderData->viewport.h <= 0)) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x, renderData->viewport.y, renderData->viewport.w, renderData->viewport.h);
if (renderData->viewport.invalid()) return;
wgpuRenderPassEncoderSetScissorRect(renderPassEncoder, renderData->viewport.x(), renderData->viewport.y(), renderData->viewport.w(), renderData->viewport.h());
// setup stencil rules
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 255);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// merge depth and stencil buffer
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.merge_depth_stencil);
renderData->meshData.drawImage(context, renderPassEncoder);
drawMeshImage(context, &renderData->meshData);
// draw image
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, bindGroupViewMat, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, renderData->bindGroupPicture, 0, nullptr);
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(compose);
assert(currentTarget);
// draw scene
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);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 0, scene->bindGroupTexure, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, bindGroupOpacities[compose->opacity], 0, nullptr);
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(compose);
assert(currentTarget);
// copy current render target data to dst storage
WgRenderStorage *target = currentTarget;
// copy current render target data to dst target
WgRenderTarget *target = currentTarget;
endRenderPass();
copyTexture(&storageTemp0, target);
copyTexture(&targetTemp0, target);
beginRenderPass(commandEncoder, target, false);
// blend scene
uint32_t blendMethodInd = (uint32_t)compose->blend;
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);
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);
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);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.direct);
ARRAY_FOREACH(p, renderData->meshGroupStrokes.meshes)
(*p)->draw(context, renderPassEncoder);
drawMesh(context, (*p));
} else {
WGPURenderPipeline stencilPipeline = (renderData->fillRule == FillRule::NonZero) ? pipelines.nonzero : pipelines.evenodd;
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, stencilPipeline);
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, 2, bindGroupOpacities[128], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth);
renderData0->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData0->meshDataBBox);
// merge clip pathes with AND logic
for (auto p = paint->clips.begin() + 1; p < paint->clips.end(); ++p) {
// get render data
@ -710,31 +780,31 @@ void WgCompositor::renderClipPath(WgContext& context, WgRenderDataPaint* paint)
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_stencil_to_depth_interm);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// copy depth to stencil
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 1);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[190], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.copy_depth_to_stencil);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// clear depth current (keep stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData->meshDataBBox);
// clear depth original (keep stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData0->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[255], 0, nullptr);
wgpuRenderPassEncoderSetPipeline(renderPassEncoder, pipelines.clear_depth);
renderData0->meshDataBBox.drawFan(context, renderPassEncoder);
drawMeshFan(context, &renderData0->meshDataBBox);
// copy stencil to depth (clear stencil)
wgpuRenderPassEncoderSetStencilReference(renderPassEncoder, 0);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 1, renderData->bindGroupPaint, 0, nullptr);
wgpuRenderPassEncoderSetBindGroup(renderPassEncoder, 2, bindGroupOpacities[128], 0, nullptr);
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, 2, bindGroupOpacities[255], 0, nullptr);
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(params);
@ -770,8 +840,8 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
auto renderData = (WgRenderDataEffectParams*)params->rd;
auto aabb = compose->aabb;
auto viewport = compose->rdViewport;
WgRenderStorage* sbuff = dst;
WgRenderStorage* dbuff = &storageTemp0;
WgRenderTarget* sbuff = dst;
WgRenderTarget* dbuff = &targetTemp0;
// begin compute pass
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, 3, viewport->bindGroupViewport, 0, nullptr);
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);
}
// vertical blur
@ -794,7 +864,7 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderData->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr);
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);
}
}
@ -803,14 +873,14 @@ bool WgCompositor::gaussianBlur(WgContext& context, WgRenderStorage* dst, const
wgpuComputePassEncoderRelease(computePassEncoder);
// if final result stored in intermidiate buffer we must copy result to destination buffer
if (sbuff == &storageTemp0)
if (sbuff == &targetTemp0)
copyTexture(sbuff, dbuff, aabb);
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(params);
@ -823,9 +893,9 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
auto viewport = compose->rdViewport;
{ // apply blur
copyTexture(&storageTemp1, dst, aabb);
WgRenderStorage* sbuff = &storageTemp1;
WgRenderStorage* dbuff = &storageTemp0;
copyTexture(&targetTemp1, dst, aabb);
WgRenderTarget* sbuff = &targetTemp1;
WgRenderTarget* dbuff = &targetTemp0;
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blur" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
// horizontal blur
@ -834,7 +904,7 @@ bool WgCompositor::dropShadow(WgContext& context, WgRenderStorage* dst, const Re
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 2, renderDataParams->bindGroupParams, 0, nullptr);
wgpuComputePassEncoderSetBindGroup(computePassEncoder, 3, viewport->bindGroupViewport, 0, nullptr);
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);
// vertical blur
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, 3, viewport->bindGroupViewport, 0, nullptr);
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);
wgpuComputePassEncoderEnd(computePassEncoder);
wgpuComputePassEncoderRelease(computePassEncoder);
}
{ // blend origin (temp0), shadow (temp1) to destination
copyTexture(&storageTemp0, dst, aabb);
copyTexture(&targetTemp0, dst, aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass drop shadow blend" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
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, 3, viewport->bindGroupViewport, 0, nullptr);
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);
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(params);
@ -874,7 +944,7 @@ bool WgCompositor::fillEffect(WgContext& context, WgRenderStorage* dst, const Re
assert(compose->rdViewport);
assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb);
copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass fill" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
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, 3, compose->rdViewport->bindGroupViewport, 0, nullptr);
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);
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(params);
@ -898,7 +968,7 @@ bool WgCompositor::tintEffect(WgContext& context, WgRenderStorage* dst, const Re
assert(compose->rdViewport);
assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb);
copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tint" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
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, 3, compose->rdViewport->bindGroupViewport, 0, nullptr);
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);
wgpuComputePassEncoderRelease(computePassEncoder);
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(params);
@ -921,7 +991,7 @@ bool WgCompositor::tritoneEffect(WgContext& context, WgRenderStorage* dst, const
assert(compose->rdViewport);
assert(!renderPassEncoder);
copyTexture(&storageTemp0, dst, compose->aabb);
copyTexture(&targetTemp0, dst, compose->aabb);
WGPUComputePassDescriptor computePassDesc{ .label = "Compute pass tritone" };
WGPUComputePassEncoder computePassEncoder = wgpuCommandEncoderBeginComputePass(commandEncoder, &computePassDesc);
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, 3, compose->rdViewport->bindGroupViewport, 0, nullptr);
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);
wgpuComputePassEncoderRelease(computePassEncoder);

View file

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

View file

@ -30,95 +30,88 @@
// 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)
{
assert(vertexBuffer.count > 2);
vertexCount = vertexBuffer.count;
indexCount = (vertexBuffer.count - 2) * 3;
context.allocateBufferVertex(bufferPosition, (float*)vertexBuffer.data, vertexCount * sizeof(float) * 2);
context.allocateBufferIndexFan(vertexCount);
// setup vertex data
vbuffer.reserve(vertexBuffer.count);
vbuffer.count = vertexBuffer.count;
memcpy(vbuffer.data, vertexBuffer.data, sizeof(vertexBuffer.data[0])*vertexBuffer.count);
// setup tex coords data
tbuffer.clear();
context.allocateBufferIndexFan(vbuffer.count);
}
void WgMeshData::update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd)
{
assert(vertexBufferInd.vcount > 2);
vertexCount = vertexBufferInd.vcount;
indexCount = vertexBufferInd.icount;
if (vertexCount > 0) context.allocateBufferVertex(bufferPosition, (float*)vertexBufferInd.vbuff, vertexCount * sizeof(float) * 2);
if (indexCount > 0) context.allocateBufferIndex(bufferIndex, vertexBufferInd.ibuff, indexCount * sizeof(uint32_t));
// setup vertex data
vbuffer.reserve(vertexBufferInd.vcount);
vbuffer.count = vertexBufferInd.vcount;
memcpy(vbuffer.data, vertexBufferInd.vbuff, sizeof(vertexBufferInd.vbuff[0])*vertexBufferInd.vcount);
// setup tex coords data
tbuffer.clear();
// copy index data
ibuffer.reserve(vertexBufferInd.icount);
ibuffer.count = vertexBufferInd.icount;
memcpy(ibuffer.data, vertexBufferInd.ibuff, sizeof(vertexBufferInd.ibuff[0])*vertexBufferInd.icount);
};
void WgMeshData::bbox(WgContext& context, const Point pmin, const Point pmax)
{
vertexCount = 4;
indexCount = 6;
const float data[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y};
context.allocateBufferVertex(bufferPosition, data, sizeof(data));
context.allocateBufferIndexFan(vertexCount);
// setup vertex data
vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, data, sizeof(data));
// setup tex coords data
tbuffer.clear();
context.allocateBufferIndexFan(vbuffer.count);
}
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 tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata));
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata));
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata));
// setup vertex data
vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, vdata, sizeof(vdata));
// setup tex coords data
tbuffer.reserve(4);
tbuffer.count = 4;
memcpy(tbuffer.data, tdata, sizeof(tdata));
// setup indexes data
ibuffer.reserve(6);
ibuffer.count = 6;
memcpy(ibuffer.data, idata, sizeof(idata));
}
void WgMeshData::blitBox(WgContext& context)
{
vertexCount = 4;
indexCount = 6;
const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f};
const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f};
const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 };
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata));
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata));
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata));
// setup vertex data
vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, vdata, sizeof(vdata));
// setup tex coords data
tbuffer.reserve(4);
tbuffer.count = 4;
memcpy(tbuffer.data, tdata, sizeof(tdata));
// setup indexes data
ibuffer.reserve(6);
ibuffer.count = 6;
memcpy(ibuffer.data, idata, sizeof(idata));
}
void WgMeshData::release(WgContext& context)
{
context.releaseBuffer(bufferIndex);
context.releaseBuffer(bufferTexCoord);
context.releaseBuffer(bufferPosition);
};
//***********************************************************************
// WgMeshDataPool
//***********************************************************************
@ -681,3 +674,94 @@ void WgRenderDataEffectParamsPool::release(WgContext& context)
mPool.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"
struct WgMeshData {
WGPUBuffer bufferPosition{};
WGPUBuffer bufferTexCoord{};
WGPUBuffer bufferIndex{};
size_t vertexCount{};
size_t indexCount{};
void draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
void drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
void drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder);
Array<Point> vbuffer;
Array<Point> tbuffer;
Array<uint32_t> ibuffer;
size_t voffset{};
size_t toffset{};
size_t ioffset{};
void update(WgContext& context, const WgVertexBuffer& vertexBuffer);
void update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd);
void bbox(WgContext& context, const Point pmin, const Point pmax);
void imageBox(WgContext& context, float w, float h);
void blitBox(WgContext& context);
void release(WgContext& context);
void release(WgContext& context) {};
};
class WgMeshDataPool {
@ -224,4 +221,23 @@ public:
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_

View file

@ -22,7 +22,7 @@
#include "tvgWgRenderTarget.h"
void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t height)
void WgRenderTarget::initialize(WgContext& context, uint32_t width, uint32_t height)
{
this->width = width;
this->height = height;
@ -36,7 +36,7 @@ void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t he
}
void WgRenderStorage::release(WgContext& context)
void WgRenderTarget::release(WgContext& context)
{
context.layouts.releaseBindGroup(bindGroupTexure);
context.layouts.releaseBindGroup(bindGroupWrite);
@ -50,38 +50,38 @@ void WgRenderStorage::release(WgContext& context)
}
//*****************************************************************************
// render storage pool
// render target pool
//*****************************************************************************
WgRenderStorage* WgRenderStoragePool::allocate(WgContext& context)
WgRenderTarget* WgRenderTargetPool::allocate(WgContext& context)
{
WgRenderStorage* renderStorage{};
WgRenderTarget* renderTarget{};
if (pool.count > 0) {
renderStorage = pool.last();
renderTarget = pool.last();
pool.pop();
} else {
renderStorage = new WgRenderStorage;
renderStorage->initialize(context, width, height);
list.push(renderStorage);
renderTarget = new WgRenderTarget;
renderTarget->initialize(context, width, height);
list.push(renderTarget);
}
return renderStorage;
return renderTarget;
};
void WgRenderStoragePool::free(WgContext& context, WgRenderStorage* renderStorage)
void WgRenderTargetPool::free(WgContext& context, WgRenderTarget* renderTarget)
{
pool.push(renderStorage);
pool.push(renderTarget);
};
void WgRenderStoragePool::initialize(WgContext& context, uint32_t width, uint32_t height)
void WgRenderTargetPool::initialize(WgContext& context, uint32_t width, uint32_t height)
{
this->width = width;
this->height = height;
}
void WgRenderStoragePool::release(WgContext& context)
void WgRenderTargetPool::release(WgContext& context)
{
ARRAY_FOREACH(p, list) {
(*p)->release(context);

View file

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

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

View file

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

View file

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

View file

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