Compare commits
19 commits
v1.0-pre21
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dfc7d268a1 | ||
![]() |
eb76769dcf | ||
![]() |
0fa5d41c8d | ||
![]() |
92070fe0de | ||
![]() |
6fd7b87754 | ||
![]() |
eeebfbb654 | ||
![]() |
dc59440744 | ||
![]() |
24509b0e41 | ||
![]() |
1f53f2d72f | ||
![]() |
2eb2b83bb0 | ||
![]() |
e2909dd6a4 | ||
![]() |
d0be8cd2bd | ||
![]() |
dc8c5bce50 | ||
![]() |
cc72eda465 | ||
![]() |
8a35f02105 | ||
![]() |
04b7bb4f25 | ||
![]() |
b221eed7fa | ||
![]() |
211fee73e2 | ||
![]() |
55847bdcb3 |
10
CODEOWNERS
|
@ -4,14 +4,8 @@
|
|||
|
||||
* @hermet
|
||||
/src/renderer/sw_engine @mgrudzinska
|
||||
/src/renderer/gl_engine @RuiwenTang @SergeyLebedkin
|
||||
/src/renderer/gl_engine @SergeyLebedkin @RuiwenTang
|
||||
/src/renderer/wg_engine @SergeyLebedkin
|
||||
/src/loaders/external_webp @JSUYA
|
||||
/src/loaders/raw @JSUYA
|
||||
/src/loaders/svg @JSUYA @mgrudzinska
|
||||
/src/loaders/webp @JSUYA
|
||||
/src/loaders/svg @mgrudzinska @JSUYA
|
||||
/src/loaders/lottie @mgrudzinska
|
||||
/src/bindings/capi @mgrudzinska
|
||||
/src/bindings/wasm @tinyjin
|
||||
/src/savers/gif @JSUYA
|
||||
/src/tools/svg2png @JSUYA
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -17,6 +17,6 @@ cpp_link_args = ['-miphoneos-version-min=11.0']
|
|||
system = 'darwin'
|
||||
subsystem = 'ios'
|
||||
kernel = 'xnu'
|
||||
cpu_family = 'arm64'
|
||||
cpu = 'arm64'
|
||||
cpu_family = 'aarch64'
|
||||
cpu = 'aarch64'
|
||||
endian = 'little'
|
||||
|
|
|
@ -65,7 +65,7 @@ struct UserExample : tvgexam::Example
|
|||
auto picture = tvg::Picture::gen();
|
||||
if (!tvgexam::verify(picture->load(data, size, "png", "", true))) return false;
|
||||
free(data);
|
||||
picture->translate(400, 0);
|
||||
picture->translate(380, 0);
|
||||
picture->scale(0.8);
|
||||
canvas->push(picture);
|
||||
|
||||
|
|
Before Width: | Height: | Size: 399 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 444 KiB After Width: | Height: | Size: 390 KiB |
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 533 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 617 KiB After Width: | Height: | Size: 192 KiB |
22
inc/thorvg.h
|
@ -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
After Width: | Height: | Size: 453 KiB |
Before Width: | Height: | Size: 818 KiB |
|
@ -972,7 +972,7 @@ TVG_API Tvg_Result tvg_paint_set_mask_method(Tvg_Paint* paint, Tvg_Paint* target
|
|||
TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint* paint, const Tvg_Paint** target, Tvg_Mask_Method* method);
|
||||
|
||||
|
||||
/*!
|
||||
/**
|
||||
* @brief Clip the drawing region of the paint object.
|
||||
*
|
||||
* This function restricts the drawing area of the paint object to the specified shape's paths.
|
||||
|
@ -985,10 +985,24 @@ TVG_API Tvg_Result tvg_paint_get_mask_method(const Tvg_Paint* paint, const Tvg_P
|
|||
* @retval TVG_RESULT_INSUFFICIENT_CONDITION if the target has already belonged to another paint.
|
||||
* @retval TVG_RESULT_NOT_SUPPORTED If the @p clipper type is not Shape.
|
||||
*
|
||||
* @see tvg_paint_get_clip()
|
||||
|
||||
* @since 1.0
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_paint_clip(Tvg_Paint* paint, Tvg_Paint* clipper);
|
||||
TVG_API Tvg_Result tvg_paint_set_clip(Tvg_Paint* paint, Tvg_Paint* clipper);
|
||||
|
||||
/**
|
||||
* @brief Get the clipper shape of the paint object.
|
||||
*
|
||||
* This function returns the clipper that has been previously set to this paint object.
|
||||
*
|
||||
* @return The shape object used as the clipper, or @c nullptr if no clipper is set.
|
||||
*
|
||||
* @see tvg_paint_set_clip()
|
||||
*
|
||||
* @since 1.0
|
||||
*/
|
||||
TVG_API Tvg_Paint* tvg_paint_get_clip(const Tvg_Paint* paint);
|
||||
|
||||
/**
|
||||
* @brief Retrieves the parent paint object.
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -57,16 +57,17 @@ bool GlGeometry::tesselate(const RenderShape& rshape, RenderUpdateFlag flag)
|
|||
|
||||
bool GlGeometry::tesselate(const RenderSurface* image, RenderUpdateFlag flag)
|
||||
{
|
||||
if (flag & RenderUpdateFlag::Image) {
|
||||
if (!(flag & RenderUpdateFlag::Image)) return true;
|
||||
|
||||
fill.clear();
|
||||
|
||||
fill.vertex.reserve(5 * 4);
|
||||
fill.index.reserve(6);
|
||||
|
||||
float left = 0.f;
|
||||
float top = 0.f;
|
||||
float right = image->w;
|
||||
float bottom = image->h;
|
||||
auto left = 0.f;
|
||||
auto top = 0.f;
|
||||
auto right = float(image->w);
|
||||
auto bottom = float(image->h);
|
||||
|
||||
// left top point
|
||||
fill.vertex.push(left);
|
||||
|
@ -101,11 +102,7 @@ bool GlGeometry::tesselate(const RenderSurface* image, RenderUpdateFlag flag)
|
|||
fill.index.push(1);
|
||||
fill.index.push(3);
|
||||
|
||||
bounds.x = 0;
|
||||
bounds.y = 0;
|
||||
bounds.w = image->w;
|
||||
bounds.h = image->h;
|
||||
}
|
||||
bounds = {{0, 0}, {int32_t(image->w), int32_t(image->h)}};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -155,31 +152,20 @@ GlStencilMode GlGeometry::getStencilMode(RenderUpdateFlag flag)
|
|||
|
||||
RenderRegion GlGeometry::getBounds() const
|
||||
{
|
||||
if (tvg::identity(&matrix)) {
|
||||
return bounds;
|
||||
} else {
|
||||
Point lt{static_cast<float>(bounds.x), static_cast<float>(bounds.y)};
|
||||
Point lb{static_cast<float>(bounds.x), static_cast<float>(bounds.y + bounds.h)};
|
||||
Point rt{static_cast<float>(bounds.x + bounds.w), static_cast<float>(bounds.y)};
|
||||
Point rb{static_cast<float>(bounds.x + bounds.w), static_cast<float>(bounds.y + bounds.h)};
|
||||
if (tvg::identity(&matrix)) return bounds;
|
||||
|
||||
lt *= matrix;
|
||||
lb *= matrix;
|
||||
rt *= matrix;
|
||||
rb *= matrix;
|
||||
auto lt = Point{float(bounds.min.x), float(bounds.min.y)} * matrix;
|
||||
auto lb = Point{float(bounds.min.x), float(bounds.max.y)} * matrix;
|
||||
auto rt = Point{float(bounds.max.x), float(bounds.min.y)} * matrix;
|
||||
auto rb = Point{float(bounds.max.x), float(bounds.max.y)} * matrix;
|
||||
|
||||
float left = min(min(lt.x, lb.x), min(rt.x, rb.x));
|
||||
float top = min(min(lt.y, lb.y), min(rt.y, rb.y));
|
||||
float right = max(max(lt.x, lb.x), max(rt.x, rb.x));
|
||||
float bottom = max(max(lt.y, lb.y), max(rt.y, rb.y));
|
||||
auto left = min(min(lt.x, lb.x), min(rt.x, rb.x));
|
||||
auto top = min(min(lt.y, lb.y), min(rt.y, rb.y));
|
||||
auto right = max(max(lt.x, lb.x), max(rt.x, rb.x));
|
||||
auto bottom = max(max(lt.y, lb.y), max(rt.y, rb.y));
|
||||
|
||||
auto bounds = RenderRegion {{int32_t(floor(left)), int32_t(floor(top))}, {int32_t(ceil(right)), int32_t(ceil(bottom))}};
|
||||
if (bounds.valid()) return bounds;
|
||||
return this->bounds;
|
||||
|
||||
auto bounds = RenderRegion {
|
||||
static_cast<int32_t>(floor(left)),
|
||||
static_cast<int32_t>(floor(top)),
|
||||
static_cast<int32_t>(ceil(right - floor(left))),
|
||||
static_cast<int32_t>(ceil(bottom - floor(top))),
|
||||
};
|
||||
if (bounds.w < 0 || bounds.h < 0) return this->bounds;
|
||||
else return bounds;
|
||||
}
|
||||
}
|
|
@ -52,17 +52,17 @@ void GlRenderPass::addRenderTask(GlRenderTask* task)
|
|||
|
||||
void GlRenderPass::getMatrix(float *dst, const Matrix &matrix) const
|
||||
{
|
||||
const auto& vp = getViewport();
|
||||
|
||||
Matrix postMatrix{};
|
||||
tvg::identity(&postMatrix);
|
||||
translate(&postMatrix, {(float)-vp.x, (float)-vp.y});
|
||||
|
||||
const auto& vp = getViewport();
|
||||
translate(&postMatrix, {(float)-vp.sx(), (float)-vp.sy()});
|
||||
|
||||
auto m = postMatrix * matrix;
|
||||
|
||||
float modelMatrix[16];
|
||||
GET_MATRIX44(m, modelMatrix);
|
||||
MVP_MATRIX(vp.w, vp.h);
|
||||
MVP_MATRIX(vp.w(), vp.h());
|
||||
|
||||
MULTIPLY_MATRIX(mvp, modelMatrix, dst);
|
||||
}
|
||||
|
|
|
@ -62,10 +62,7 @@ public:
|
|||
}
|
||||
|
||||
auto task = new T(program, targetFbo, mFbo, std::move(mTasks));
|
||||
|
||||
const auto& vp = mFbo->getViewport();
|
||||
|
||||
task->setRenderSize(static_cast<uint32_t>(vp.w), static_cast<uint32_t>(vp.h));
|
||||
task->setRenderSize(mFbo->getViewport().w(), mFbo->getViewport().h());
|
||||
|
||||
return task;
|
||||
}
|
||||
|
|
|
@ -22,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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -130,12 +130,10 @@ public:
|
|||
|
||||
protected:
|
||||
GLuint getTargetFbo() { return mTargetFbo; }
|
||||
|
||||
GLuint getSelfFbo();
|
||||
|
||||
GLuint getResolveFboId();
|
||||
|
||||
void onResolve();
|
||||
|
||||
private:
|
||||
GLuint mTargetFbo;
|
||||
GlRenderTarget* mFbo;
|
||||
|
|
|
@ -52,6 +52,8 @@ void GlRenderer::flush()
|
|||
{
|
||||
clearDisposes();
|
||||
|
||||
mRootTarget.reset();
|
||||
|
||||
ARRAY_FOREACH(p, mComposePool) delete(*p);
|
||||
mComposePool.clear();
|
||||
|
||||
|
@ -181,10 +183,10 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
|
|||
bbox.intersect(vp);
|
||||
}
|
||||
|
||||
auto x = bbox.x - vp.x;
|
||||
auto y = bbox.y - vp.y;
|
||||
auto w = bbox.w;
|
||||
auto h = bbox.h;
|
||||
auto x = bbox.sx() - vp.sx();
|
||||
auto y = bbox.sy() - vp.sy();
|
||||
auto w = bbox.sw();
|
||||
auto h = bbox.sh();
|
||||
|
||||
GlRenderTask* task = nullptr;
|
||||
if (mBlendMethod != BlendMethod::Normal && !complexBlend) task = new GlSimpleBlendTask(mBlendMethod, mPrograms[RT_Color]);
|
||||
|
@ -197,7 +199,8 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const RenderColor& c, RenderUpdat
|
|||
return;
|
||||
}
|
||||
|
||||
task->setViewport({x, vp.h - y - h, w, h});
|
||||
y = vp.sh() - y - h;
|
||||
task->setViewport({{x, y}, {x + w, y + h}});
|
||||
|
||||
GlRenderTask* stencilTask = nullptr;
|
||||
|
||||
|
@ -266,7 +269,6 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
{
|
||||
auto vp = currentPass()->getViewport();
|
||||
auto bbox = sdata.geometry.viewport;
|
||||
|
||||
bbox.intersect(vp);
|
||||
|
||||
const Fill::ColorStop* stops = nullptr;
|
||||
|
@ -275,13 +277,9 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
|
||||
GlRenderTask* task = nullptr;
|
||||
|
||||
if (fill->type() == Type::LinearGradient) {
|
||||
task = new GlRenderTask(mPrograms[RT_LinGradient]);
|
||||
} else if (fill->type() == Type::RadialGradient) {
|
||||
task = new GlRenderTask(mPrograms[RT_RadGradient]);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (fill->type() == Type::LinearGradient) task = new GlRenderTask(mPrograms[RT_LinGradient]);
|
||||
else if (fill->type() == Type::RadialGradient) task = new GlRenderTask(mPrograms[RT_RadGradient]);
|
||||
else return;
|
||||
|
||||
task->setDrawDepth(depth);
|
||||
|
||||
|
@ -291,13 +289,11 @@ void GlRenderer::drawPrimitive(GlShape& sdata, const Fill* fill, RenderUpdateFla
|
|||
}
|
||||
|
||||
auto complexBlend = beginComplexBlending(bbox, sdata.geometry.getBounds());
|
||||
|
||||
if (complexBlend) vp = currentPass()->getViewport();
|
||||
|
||||
auto x = bbox.x - vp.x;
|
||||
auto y = bbox.y - vp.y;
|
||||
|
||||
task->setViewport({x, vp.h - y - bbox.h, bbox.w, bbox.h});
|
||||
auto x = bbox.sx() - vp.sx();
|
||||
auto y = vp.sh() - (bbox.sy() - vp.sy()) - bbox.sh();
|
||||
task->setViewport({{x, y}, {x + bbox.sw(), y + bbox.sh()}});
|
||||
|
||||
GlRenderTask* stencilTask = nullptr;
|
||||
GlStencilMode stencilMode = sdata.geometry.getStencilMode(flag);
|
||||
|
@ -496,22 +492,18 @@ void GlRenderer::drawClip(Array<RenderData>& clips)
|
|||
|
||||
for (uint32_t i = 0; i < clips.count; ++i) {
|
||||
auto sdata = static_cast<GlShape*>(clips[i]);
|
||||
|
||||
auto clipTask = new GlRenderTask(mPrograms[RT_Stencil]);
|
||||
|
||||
clipTask->setDrawDepth(clipDepths[i]);
|
||||
|
||||
auto flag = (sdata->geometry.stroke.vertex.count > 0) ? RenderUpdateFlag::Stroke : RenderUpdateFlag::Path;
|
||||
sdata->geometry.draw(clipTask, &mGpuBuffer, flag);
|
||||
|
||||
auto bbox = sdata->geometry.viewport;
|
||||
|
||||
bbox.intersect(vp);
|
||||
|
||||
auto x = bbox.x - vp.x;
|
||||
auto y = bbox.y - vp.y;
|
||||
|
||||
clipTask->setViewport({x, vp.h - y - bbox.h, bbox.w, bbox.h});
|
||||
auto x = bbox.sx() - vp.sx();
|
||||
auto y = vp.sh() - (bbox.sy() - vp.sy()) - bbox.sh();
|
||||
clipTask->setViewport({{x, y}, {x + bbox.sw(), y + bbox.sh()}});
|
||||
|
||||
float matrix44[16];
|
||||
currentPass()->getMatrix(matrix44, sdata->geometry.matrix);
|
||||
|
@ -527,7 +519,7 @@ void GlRenderer::drawClip(Array<RenderData>& clips)
|
|||
maskTask->addVertexLayout(GlVertexLayout{0, 2, 2 * sizeof(float), identityVertexOffset});
|
||||
maskTask->addBindResource(GlBindingResource{0, loc, mGpuBuffer.getBufferId(), mat4Offset, 16 * sizeof(float), });
|
||||
maskTask->setDrawRange(identityIndexOffset, 6);
|
||||
maskTask->setViewport({0, 0, static_cast<int32_t>(vp.w), static_cast<int32_t>(vp.h)});
|
||||
maskTask->setViewport({{0, 0}, {vp.sw(), vp.sh()}});
|
||||
|
||||
currentPass()->addRenderTask(new GlClipTask(clipTask, maskTask));
|
||||
}
|
||||
|
@ -541,11 +533,10 @@ GlRenderPass* GlRenderer::currentPass()
|
|||
|
||||
bool GlRenderer::beginComplexBlending(const RenderRegion& vp, RenderRegion bounds)
|
||||
{
|
||||
if (vp.w == 0 || vp.h == 0) return false;
|
||||
if (vp.invalid()) return false;
|
||||
|
||||
bounds.intersect(vp);
|
||||
|
||||
if (bounds.w == 0 || bounds.h == 0) return false;
|
||||
if (bounds.invalid()) return false;
|
||||
|
||||
if (mBlendMethod == BlendMethod::Normal || mBlendMethod == BlendMethod::Add || mBlendMethod == BlendMethod::Darken || mBlendMethod == BlendMethod::Lighten) return false;
|
||||
|
||||
|
@ -571,20 +562,12 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
|
|||
if (mBlendPool.count < 2) mBlendPool.push(new GlRenderTargetPool(surface.w, surface.h));
|
||||
auto dstCopyFbo = mBlendPool[1]->getRenderTarget(vp);
|
||||
|
||||
{
|
||||
const auto& passVp = currentPass()->getViewport();
|
||||
|
||||
auto x = vp.x;
|
||||
auto y = vp.y;
|
||||
auto w = vp.w;
|
||||
auto h = vp.h;
|
||||
|
||||
stencilTask->setViewport({x, passVp.h - y - h, w, h});
|
||||
}
|
||||
auto x = vp.sx();
|
||||
auto y = currentPass()->getViewport().sh() - vp.sy() - vp.sh();
|
||||
stencilTask->setViewport({{x, y}, {x + vp.sw(), y + vp.sh()}});
|
||||
|
||||
stencilTask->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
|
||||
{
|
||||
// set view matrix
|
||||
float matrix44[16];
|
||||
currentPass()->getMatrix(matrix44, matrix);
|
||||
|
@ -596,12 +579,9 @@ void GlRenderer::endBlendingCompose(GlRenderTask* stencilTask, const Matrix& mat
|
|||
viewOffset,
|
||||
16 * sizeof(float),
|
||||
});
|
||||
}
|
||||
|
||||
auto task = new GlComplexBlendTask(getBlendProgram(), currentPass()->getFbo(), dstCopyFbo, stencilTask, composeTask);
|
||||
|
||||
prepareCmpTask(task, vp, blendPass->getFboWidth(), blendPass->getFboHeight());
|
||||
|
||||
task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
|
||||
// src and dst texture
|
||||
|
@ -632,8 +612,7 @@ GlProgram* GlRenderer::getBlendProgram()
|
|||
|
||||
void GlRenderer::prepareBlitTask(GlBlitTask* task)
|
||||
{
|
||||
RenderRegion region{0, 0, static_cast<int32_t>(surface.w), static_cast<int32_t>(surface.h)};
|
||||
prepareCmpTask(task, region, surface.w, surface.h);
|
||||
prepareCmpTask(task, {{0, 0}, {int32_t(surface.w), int32_t(surface.h)}}, surface.w, surface.h);
|
||||
task->addBindResource(GlBindingResource{0, task->getColorTexture(), task->getProgram()->getUniformLocation("uSrcTexture")});
|
||||
}
|
||||
|
||||
|
@ -648,13 +627,13 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint
|
|||
auto taskVp = vp;
|
||||
taskVp.intersect(passVp);
|
||||
|
||||
auto x = taskVp.x - passVp.x;
|
||||
auto y = taskVp.y - passVp.y;
|
||||
auto w = taskVp.w;
|
||||
auto h = taskVp.h;
|
||||
auto x = taskVp.sx() - passVp.sx();
|
||||
auto y = taskVp.sy() - passVp.sy();
|
||||
auto w = taskVp.sw();
|
||||
auto h = taskVp.sh();
|
||||
|
||||
float rw = static_cast<float>(passVp.w);
|
||||
float rh = static_cast<float>(passVp.h);
|
||||
float rw = static_cast<float>(passVp.w());
|
||||
float rh = static_cast<float>(passVp.h());
|
||||
|
||||
float l = static_cast<float>(x);
|
||||
float t = static_cast<float>(rh - y);
|
||||
|
@ -705,10 +684,9 @@ void GlRenderer::prepareCmpTask(GlRenderTask* task, const RenderRegion& vp, uint
|
|||
|
||||
task->addVertexLayout(GlVertexLayout{0, 2, 4 * sizeof(float), vertexOffset});
|
||||
task->addVertexLayout(GlVertexLayout{1, 2, 4 * sizeof(float), vertexOffset + 2 * sizeof(float)});
|
||||
|
||||
task->setDrawRange(indexOffset, indices.count);
|
||||
|
||||
task->setViewport({x, static_cast<int32_t>((passVp.h - y - h)), w, h});
|
||||
y = (passVp.sh() - y - h);
|
||||
task->setViewport({{x, y}, {x + w, y + h}});
|
||||
}
|
||||
|
||||
|
||||
|
@ -741,11 +719,11 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
if (program && !selfPass->isEmpty() && !maskPass->isEmpty()) {
|
||||
auto prev_task = maskPass->endRenderPass<GlComposeTask>(nullptr, currentPass()->getFboId());
|
||||
prev_task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
prev_task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
|
||||
prev_task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h());
|
||||
prev_task->setViewport(glCmp->bbox);
|
||||
|
||||
auto compose_task = selfPass->endRenderPass<GlDrawBlitTask>(program, currentPass()->getFboId());
|
||||
compose_task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
|
||||
compose_task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h());
|
||||
compose_task->setPrevTask(prev_task);
|
||||
|
||||
prepareCmpTask(compose_task, glCmp->bbox, selfPass->getFboWidth(), selfPass->getFboHeight());
|
||||
|
@ -754,7 +732,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
compose_task->addBindResource(GlBindingResource{1, maskPass->getTextureId(), program->getUniformLocation("uMaskTexture")});
|
||||
|
||||
compose_task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
compose_task->setParentSize(static_cast<uint32_t>(currentPass()->getViewport().w), static_cast<uint32_t>(currentPass()->getViewport().h));
|
||||
compose_task->setParentSize(currentPass()->getViewport().w(), currentPass()->getViewport().h());
|
||||
currentPass()->addRenderTask(compose_task);
|
||||
}
|
||||
|
||||
|
@ -767,7 +745,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
|
||||
if (!renderPass->isEmpty()) {
|
||||
auto task = renderPass->endRenderPass<GlDrawBlitTask>(mPrograms[RT_Image], currentPass()->getFboId());
|
||||
task->setRenderSize(static_cast<uint32_t>(glCmp->bbox.w), static_cast<uint32_t>(glCmp->bbox.h));
|
||||
task->setRenderSize(glCmp->bbox.w(), glCmp->bbox.h());
|
||||
prepareCmpTask(task, glCmp->bbox, renderPass->getFboWidth(), renderPass->getFboHeight());
|
||||
task->setDrawDepth(currentPass()->nextDrawDepth());
|
||||
|
||||
|
@ -795,7 +773,7 @@ void GlRenderer::endRenderPass(RenderCompositor* cmp)
|
|||
|
||||
// texture id
|
||||
task->addBindResource(GlBindingResource{0, renderPass->getTextureId(), task->getProgram()->getUniformLocation("uTexture")});
|
||||
task->setParentSize(static_cast<uint32_t>(currentPass()->getViewport().w), static_cast<uint32_t>(currentPass()->getViewport().h));
|
||||
task->setParentSize(currentPass()->getViewport().w(), currentPass()->getViewport().h());
|
||||
currentPass()->addRenderTask(std::move(task));
|
||||
}
|
||||
delete(renderPass);
|
||||
|
@ -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) {
|
||||
|
|
|
@ -1516,12 +1516,7 @@ void Stroker::stroke(const RenderShape *rshape, const RenderPath& path)
|
|||
|
||||
RenderRegion Stroker::bounds() const
|
||||
{
|
||||
return RenderRegion {
|
||||
static_cast<int32_t>(floor(mLeftTop.x)),
|
||||
static_cast<int32_t>(floor(mLeftTop.y)),
|
||||
static_cast<int32_t>(ceil(mRightBottom.x - floor(mLeftTop.x))),
|
||||
static_cast<int32_t>(ceil(mRightBottom.y - floor(mLeftTop.y))),
|
||||
};
|
||||
return {{int32_t(floor(mLeftTop.x)), int32_t(floor(mLeftTop.y))}, {int32_t(ceil(mRightBottom.x)), int32_t(ceil(mRightBottom.y))}};
|
||||
}
|
||||
|
||||
|
||||
|
@ -2197,26 +2192,15 @@ void BWTessellator::tessellate(const RenderPath& path, const Matrix& matrix)
|
|||
|
||||
RenderRegion BWTessellator::bounds() const
|
||||
{
|
||||
return RenderRegion {
|
||||
static_cast<int32_t>(floor(bbox.min.x)),
|
||||
static_cast<int32_t>(floor(bbox.min.y)),
|
||||
static_cast<int32_t>(ceil(bbox.max.x - floor(bbox.min.x))),
|
||||
static_cast<int32_t>(ceil(bbox.max.y - floor(bbox.min.y))),
|
||||
};
|
||||
return {{int32_t(floor(bbox.min.x)), int32_t(floor(bbox.min.y))}, {int32_t(ceil(bbox.max.x)), int32_t(ceil(bbox.max.y))}};
|
||||
}
|
||||
|
||||
|
||||
uint32_t BWTessellator::pushVertex(float x, float y)
|
||||
{
|
||||
auto index = _pushVertex(mBuffer->vertex, x, y);
|
||||
|
||||
if (index == 0) {
|
||||
bbox.max = bbox.min = {x, y};
|
||||
} else {
|
||||
bbox.min = {std::min(bbox.min.x, x), std::min(bbox.min.y, y)};
|
||||
bbox.max = {std::max(bbox.max.x, x), std::max(bbox.max.y, y)};
|
||||
}
|
||||
|
||||
if (index == 0) bbox.max = bbox.min = {x, y};
|
||||
else bbox = {{std::min(bbox.min.x, x), std::min(bbox.min.y, y)}, {std::max(bbox.max.x, x), std::max(bbox.max.y, y)}};
|
||||
return index;
|
||||
}
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ private:
|
|||
void pushTriangle(uint32_t a, uint32_t b, uint32_t c);
|
||||
|
||||
GlGeometryBuffer* mBuffer;
|
||||
BBox bbox = {{}, {}};
|
||||
BBox bbox = {};
|
||||
};
|
||||
|
||||
} // namespace tvg
|
||||
|
|
|
@ -121,19 +121,20 @@ struct SwSpan
|
|||
|
||||
struct SwRle
|
||||
{
|
||||
SwSpan *spans;
|
||||
uint32_t alloc;
|
||||
uint32_t size;
|
||||
};
|
||||
Array<SwSpan> spans;
|
||||
|
||||
struct SwBBox
|
||||
{
|
||||
SwPoint min, max;
|
||||
|
||||
void reset()
|
||||
bool invalid() const
|
||||
{
|
||||
min.x = min.y = max.x = max.y = 0;
|
||||
return spans.empty();
|
||||
}
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
return !invalid();
|
||||
}
|
||||
|
||||
uint32_t size() const { return spans.count; }
|
||||
SwSpan* data() const { return spans.data; }
|
||||
};
|
||||
|
||||
struct SwFill
|
||||
|
@ -215,7 +216,7 @@ struct SwShape
|
|||
SwFill* fill = nullptr;
|
||||
SwRle* rle = nullptr;
|
||||
SwRle* strokeRle = nullptr;
|
||||
SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
|
||||
RenderRegion bbox; //Keep it boundary without stroke region. Using for optimal filling.
|
||||
|
||||
bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
|
||||
};
|
||||
|
@ -279,7 +280,7 @@ struct SwCompositor : RenderCompositor
|
|||
SwSurface* recoverSfc; //Recover surface when composition is started
|
||||
SwCompositor* recoverCmp; //Recover compositor when composition is done
|
||||
SwImage image;
|
||||
SwBBox bbox;
|
||||
RenderRegion bbox;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
|
@ -499,16 +500,15 @@ SwFixed mathLength(const SwPoint& pt);
|
|||
int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
|
||||
SwPoint mathTransform(const Point* to, const Matrix& transform);
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack);
|
||||
|
||||
void shapeReset(SwShape* shape);
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepared(const SwShape* shape);
|
||||
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
|
||||
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid);
|
||||
void shapeFree(SwShape* shape);
|
||||
void shapeDelStroke(SwShape* shape);
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
|
||||
|
@ -523,8 +523,8 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
|||
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
|
||||
void strokeFree(SwStroke* stroke);
|
||||
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid);
|
||||
bool imageGenRle(SwImage* image, const RenderRegion& bbox, bool antiAlias);
|
||||
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
|
||||
void imageReset(SwImage* image);
|
||||
void imageFree(SwImage* image);
|
||||
|
@ -547,13 +547,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
|
||||
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
|
||||
SwRle* rleRender(const SwBBox* bbox);
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const RenderRegion& bbox, bool antiAlias);
|
||||
SwRle* rleRender(const RenderRegion* bbox);
|
||||
void rleFree(SwRle* rle);
|
||||
void rleReset(SwRle* rle);
|
||||
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
|
||||
bool rleClip(SwRle* rle, const SwRle* clip);
|
||||
bool rleClip(SwRle* rle, const SwBBox* clip);
|
||||
bool rleClip(SwRle* rle, const RenderRegion* clip);
|
||||
|
||||
SwMpool* mpoolInit(uint32_t threads);
|
||||
bool mpoolTerm(SwMpool* mpool);
|
||||
|
@ -568,7 +568,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);
|
||||
|
|
|
@ -72,7 +72,7 @@ static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool,
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
image->direct = _onlyShifted(transform);
|
||||
|
||||
|
@ -91,13 +91,13 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg
|
|||
}
|
||||
|
||||
if (!_genOutline(image, transform, mpool, tid)) return false;
|
||||
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
|
||||
return mathUpdateOutlineBBox(image->outline, clipBox, renderBox, image->direct);
|
||||
}
|
||||
|
||||
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
|
||||
bool imageGenRle(SwImage* image, const RenderRegion& renderBox, bool antiAlias)
|
||||
{
|
||||
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
|
||||
if ((image->rle = rleRender(image->rle, image->outline, renderBox, antiAlias))) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -271,27 +271,12 @@ SwPoint mathTransform(const Point* to, const Matrix& transform)
|
|||
}
|
||||
|
||||
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee)
|
||||
{
|
||||
clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x;
|
||||
clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y;
|
||||
clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x;
|
||||
clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y;
|
||||
|
||||
//Check boundary
|
||||
if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y ||
|
||||
clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const RenderRegion& clipBox, RenderRegion& renderBox, bool fastTrack)
|
||||
{
|
||||
if (!outline) return false;
|
||||
|
||||
if (outline->pts.empty() || outline->cntrs.empty()) {
|
||||
renderRegion.reset();
|
||||
renderBox.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -310,16 +295,13 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
|
|||
}
|
||||
|
||||
if (fastTrack) {
|
||||
renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
|
||||
renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
|
||||
renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
|
||||
renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
|
||||
renderBox.min = {int32_t(round(xMin / 64.0f)), int32_t(round(yMin / 64.0f))};
|
||||
renderBox.max = {int32_t(round(xMax / 64.0f)), int32_t(round(yMax / 64.0f))};
|
||||
} else {
|
||||
renderRegion.min.x = xMin >> 6;
|
||||
renderRegion.max.x = (xMax + 63) >> 6;
|
||||
renderRegion.min.y = yMin >> 6;
|
||||
renderRegion.max.y = (yMax + 63) >> 6;
|
||||
renderBox.min = {xMin >> 6, yMin >> 6};
|
||||
renderBox.max = {(xMax + 63) >> 6, (yMax + 63) >> 6};
|
||||
}
|
||||
|
||||
return mathClipBBox(clipRegion, renderRegion);
|
||||
renderBox.intersect(clipBox);
|
||||
return renderBox.valid();
|
||||
}
|
||||
|
|
|
@ -61,7 +61,7 @@ static inline int _gaussianRemap(int end, int idx)
|
|||
|
||||
//TODO: SIMD OPTIMIZATION?
|
||||
template<int border = 0>
|
||||
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, bool flipped)
|
||||
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const RenderRegion& bbox, int32_t dimension, bool flipped)
|
||||
{
|
||||
if (flipped) {
|
||||
src += (bbox.min.x * stride + bbox.min.y) << 2;
|
||||
|
@ -136,17 +136,17 @@ static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality)
|
|||
|
||||
bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params)
|
||||
{
|
||||
//bbox region expansion for feathering
|
||||
auto& region = params->extend;
|
||||
//region expansion for feathering
|
||||
auto& bbox = params->extend;
|
||||
auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends;
|
||||
|
||||
if (params->direction != 2) {
|
||||
region.x = -extra;
|
||||
region.w = extra * 2;
|
||||
bbox.min.x = -extra;
|
||||
bbox.max.x = extra;
|
||||
}
|
||||
if (params->direction != 1) {
|
||||
region.y = -extra;
|
||||
region.h = extra * 2;
|
||||
bbox.min.y = -extra;
|
||||
bbox.max.y = extra;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -230,7 +230,7 @@ struct SwDropShadow : SwGaussianBlur
|
|||
|
||||
|
||||
//TODO: SIMD OPTIMIZATION?
|
||||
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const SwBBox& bbox, int32_t dimension, uint32_t color, bool flipped)
|
||||
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const RenderRegion& bbox, int32_t dimension, uint32_t color, bool flipped)
|
||||
{
|
||||
if (flipped) {
|
||||
src += (bbox.min.x * stride + bbox.min.y);
|
||||
|
@ -267,20 +267,20 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i
|
|||
}
|
||||
|
||||
|
||||
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct)
|
||||
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, RenderRegion& bbox, SwPoint& offset, uint8_t opacity, bool direct)
|
||||
{
|
||||
src += (region.min.y * sstride + region.min.x);
|
||||
dst += (region.min.y * dstride + region.min.x);
|
||||
src += (bbox.min.y * sstride + bbox.min.x);
|
||||
dst += (bbox.min.y * dstride + bbox.min.x);
|
||||
|
||||
auto w = region.max.x - region.min.x;
|
||||
auto h = region.max.y - region.min.y;
|
||||
auto w = bbox.max.x - bbox.min.x;
|
||||
auto h = bbox.max.y - bbox.min.y;
|
||||
auto translucent = (direct || opacity < 255);
|
||||
|
||||
//shift offset
|
||||
if (region.min.x + offset.x < 0) src -= offset.x;
|
||||
if (bbox.min.x + offset.x < 0) src -= offset.x;
|
||||
else dst += offset.x;
|
||||
|
||||
if (region.min.y + offset.y < 0) src -= (offset.y * sstride);
|
||||
if (bbox.min.y + offset.y < 0) src -= (offset.y * sstride);
|
||||
else dst += (offset.y * dstride);
|
||||
|
||||
for (auto y = 0; y < h; ++y) {
|
||||
|
@ -294,20 +294,19 @@ static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstr
|
|||
|
||||
bool effectDropShadowRegion(RenderEffectDropShadow* params)
|
||||
{
|
||||
//bbox region expansion for feathering
|
||||
auto& region = params->extend;
|
||||
//region expansion for feathering
|
||||
auto& bbox = params->extend;
|
||||
auto& offset = static_cast<SwDropShadow*>(params->rd)->offset;
|
||||
auto extra = static_cast<SwDropShadow*>(params->rd)->extends;
|
||||
|
||||
region.x = -extra;
|
||||
region.w = extra * 2;
|
||||
region.y = -extra;
|
||||
region.h = extra * 2;
|
||||
bbox.min = {-extra, -extra};
|
||||
bbox.max = {extra, extra};
|
||||
|
||||
region.x = std::min(region.x + (int32_t)offset.x, region.x);
|
||||
region.y = std::min(region.y + (int32_t)offset.y, region.y);
|
||||
region.w += abs(offset.x);
|
||||
region.h += abs(offset.y);
|
||||
if (offset.x < 0) bbox.min.x += (int32_t) offset.x;
|
||||
else bbox.max.x += offset.x;
|
||||
|
||||
if (offset.y < 0) bbox.min.y += (int32_t) offset.y;
|
||||
else bbox.max.y += offset.y;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -99,15 +99,15 @@ static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32
|
|||
}
|
||||
|
||||
|
||||
static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c)
|
||||
static bool avxRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
auto h = bbox.h();
|
||||
auto w = bbox.w();
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
|
||||
uint32_t ialpha = 255 - c.a;
|
||||
|
||||
|
@ -145,7 +145,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, c
|
|||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = ~c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
@ -160,14 +160,12 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, c
|
|||
|
||||
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
uint32_t src;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
|
@ -213,7 +211,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
|
||||
else src = c.a;
|
||||
|
|
|
@ -94,13 +94,11 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int
|
|||
|
||||
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
uint32_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
@ -112,7 +110,7 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, c
|
|||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
|
||||
else src = c.a;
|
||||
|
@ -126,29 +124,26 @@ static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, c
|
|||
}
|
||||
|
||||
|
||||
static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c)
|
||||
static bool inline cRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = 255 - c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
for (uint32_t y = 0; y < bbox.h(); ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = ~c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
for (uint32_t y = 0; y < bbox.h(); ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
for (uint32_t x = 0; x < bbox.w(); ++x, ++dst) {
|
||||
*dst = c.a + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,8 +91,6 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3
|
|||
|
||||
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const RenderColor& c)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
|
@ -100,7 +98,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
uint8x8_t *vDst = nullptr;
|
||||
uint16_t align;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
||||
|
@ -132,7 +130,7 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
ARRAY_FOREACH(span, rle->spans) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, c.a);
|
||||
else src = c.a;
|
||||
|
@ -146,15 +144,15 @@ static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, const
|
|||
}
|
||||
|
||||
|
||||
static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, const RenderColor& c)
|
||||
static bool neonRasterTranslucentRect(SwSurface* surface, const RenderRegion& bbox, const RenderColor& c)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
auto h = bbox.h();
|
||||
auto w = bbox.w();
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(c.r, c.g, c.b, c.a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf32 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = 255 - c.a;
|
||||
|
||||
auto vColor = vdup_n_u32(color);
|
||||
|
@ -185,7 +183,7 @@ static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region,
|
|||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto buffer = surface->buf8 + (bbox.min.y * surface->stride) + bbox.min.x;
|
||||
auto ialpha = ~c.a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -419,13 +419,13 @@ static SwOutline* _genOutline(SwShape* shape, const RenderShape* rshape, const M
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
if (auto out = _genOutline(shape, rshape, transform, mpool, tid, hasComposite, rshape->trimpath())) shape->outline = out;
|
||||
else return false;
|
||||
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
|
||||
if (!mathUpdateOutlineBBox(shape->outline, clipBox, renderBox, shape->fastTrack)) return false;
|
||||
|
||||
shape->bbox = renderRegion;
|
||||
shape->bbox = renderBox;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -500,7 +500,7 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& t
|
|||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const RenderRegion& clipBox, RenderRegion& renderBox, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
SwOutline* shapeOutline = nullptr;
|
||||
SwOutline* strokeOutline = nullptr;
|
||||
|
@ -528,12 +528,12 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix&
|
|||
|
||||
strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
|
||||
|
||||
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
|
||||
if (!mathUpdateOutlineBBox(strokeOutline, clipBox, renderBox, false)) {
|
||||
ret = false;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
|
||||
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderBox, true);
|
||||
|
||||
clear:
|
||||
if (dashStroking) mpoolRetDashOutline(mpool, tid);
|
||||
|
|
|
@ -31,7 +31,7 @@ struct Canvas::Impl
|
|||
{
|
||||
Scene* scene;
|
||||
RenderMethod* renderer;
|
||||
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
|
||||
RenderRegion vport = {{0, 0}, {INT32_MAX, INT32_MAX}};
|
||||
Status status = Status::Synced;
|
||||
|
||||
Impl() : scene(Scene::gen())
|
||||
|
@ -119,11 +119,11 @@ struct Canvas::Impl
|
|||
{
|
||||
if (status != Status::Damaged && status != Status::Synced) return Result::InsufficientCondition;
|
||||
|
||||
RenderRegion val = {x, y, w, h};
|
||||
RenderRegion val = {{x, y}, {x + w, y + h}};
|
||||
//intersect if the target buffer is already set.
|
||||
auto surface = renderer->mainSurface();
|
||||
if (surface && surface->w > 0 && surface->h > 0) {
|
||||
val.intersect({0, 0, (int32_t)surface->w, (int32_t)surface->h});
|
||||
val.intersect({{0, 0}, {(int32_t)surface->w, (int32_t)surface->h}});
|
||||
}
|
||||
if (vport == val) return Result::Success;
|
||||
renderer->viewport(val);
|
||||
|
|
|
@ -51,7 +51,7 @@ Result GlCanvas::target(void* context, int32_t id, uint32_t w, uint32_t h, Color
|
|||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(context, id, w, h)) return Result::Unknown;
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
|
|
|
@ -58,7 +58,7 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix&
|
|||
if (tmp[i].y > max.y) max.y = tmp[i].y;
|
||||
}
|
||||
|
||||
float region[4] = {float(before.x), float(before.x + before.w), float(before.y), float(before.y + before.h)};
|
||||
float region[4] = {float(before.min.x), float(before.max.x), float(before.min.y), float(before.max.y)};
|
||||
|
||||
//figure out if the clipper is a superset of the current viewport(before) region
|
||||
if (min.x <= region[0] && max.x >= region[1] && min.y <= region[2] && max.y >= region[3]) {
|
||||
|
@ -66,7 +66,7 @@ static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix&
|
|||
return Result::Success;
|
||||
//figure out if the clipper is totally outside of the viewport
|
||||
} else if (max.x <= region[0] || min.x >= region[1] || max.y <= region[2] || min.y >= region[3]) {
|
||||
renderer->viewport({0, 0, 0, 0});
|
||||
renderer->viewport({});
|
||||
return Result::Success;
|
||||
}
|
||||
return Result::InsufficientCondition;
|
||||
|
@ -122,13 +122,13 @@ static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Mat
|
|||
if (v1.x > v2.x) std::swap(v1.x, v2.x);
|
||||
if (v1.y > v2.y) std::swap(v1.y, v2.y);
|
||||
|
||||
after.x = static_cast<int32_t>(nearbyint(v1.x));
|
||||
after.y = static_cast<int32_t>(nearbyint(v1.y));
|
||||
after.w = static_cast<int32_t>(nearbyint(v2.x)) - after.x;
|
||||
after.h = static_cast<int32_t>(nearbyint(v2.y)) - after.y;
|
||||
after.min.x = static_cast<int32_t>(nearbyint(v1.x));
|
||||
after.min.y = static_cast<int32_t>(nearbyint(v1.y));
|
||||
after.max.x = static_cast<int32_t>(nearbyint(v2.x));
|
||||
after.max.y = static_cast<int32_t>(nearbyint(v2.y));
|
||||
|
||||
if (after.w < 0) after.w = 0;
|
||||
if (after.h < 0) after.h = 0;
|
||||
if (after.max.x < after.min.x) after.max.x = after.min.x;
|
||||
if (after.max.y < after.min.y) after.max.y = after.min.y;
|
||||
|
||||
after.intersect(before);
|
||||
renderer->viewport(after);
|
||||
|
@ -168,7 +168,7 @@ Paint* Paint::Impl::duplicate(Paint* ret)
|
|||
ret->pImpl->opacity = opacity;
|
||||
|
||||
if (maskData) ret->mask(maskData->target->duplicate(), maskData->method);
|
||||
if (clipper) ret->clip(clipper->duplicate());
|
||||
if (clipper) ret->clip(static_cast<Shape*>(clipper->duplicate()));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ bool Paint::Impl::render(RenderMethod* renderer)
|
|||
PAINT_METHOD(region, bounds(renderer));
|
||||
|
||||
if (MASK_REGION_MERGING(maskData->method)) region.add(PAINT(maskData->target)->bounds(renderer));
|
||||
if (region.w == 0 || region.h == 0) return true;
|
||||
if (region.invalid()) return true;
|
||||
cmp = renderer->target(region, MASK_TO_COLORSPACE(renderer, maskData->method), CompositionFlag::Masking);
|
||||
if (renderer->beginComposite(cmp, MaskMethod::None, 255)) {
|
||||
maskData->target->pImpl->render(renderer);
|
||||
|
@ -376,16 +376,18 @@ Paint* Paint::duplicate() const noexcept
|
|||
}
|
||||
|
||||
|
||||
Result Paint::clip(Paint* clipper) noexcept
|
||||
Result Paint::clip(Shape* clipper) noexcept
|
||||
{
|
||||
if (clipper && clipper->type() != Type::Shape) {
|
||||
TVGERR("RENDERER", "Clipping only supports the Shape!");
|
||||
return Result::NonSupport;
|
||||
}
|
||||
return pImpl->clip(clipper);
|
||||
}
|
||||
|
||||
|
||||
Shape* Paint::clip() const noexcept
|
||||
{
|
||||
return pImpl->clipper;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::mask(Paint* target, MaskMethod method) noexcept
|
||||
{
|
||||
return pImpl->mask(target, method);
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace tvg
|
|||
Paint* paint = nullptr;
|
||||
Paint* parent = nullptr;
|
||||
Mask* maskData = nullptr;
|
||||
Paint* clipper = nullptr;
|
||||
Shape* clipper = nullptr;
|
||||
RenderMethod* renderer = nullptr;
|
||||
RenderData rd = nullptr;
|
||||
|
||||
|
@ -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);
|
||||
|
|
|
@ -253,7 +253,7 @@ struct PictureImpl : Picture
|
|||
|
||||
bool render(RenderMethod* renderer)
|
||||
{
|
||||
bool ret = false;
|
||||
auto ret = true;
|
||||
renderer->blend(impl.blendMethod);
|
||||
|
||||
if (bitmap) return renderer->renderImage(impl.rd);
|
||||
|
@ -273,7 +273,7 @@ struct PictureImpl : Picture
|
|||
{
|
||||
if (impl.rd) return renderer->region(impl.rd);
|
||||
if (vector) return vector->pImpl->bounds(renderer);
|
||||
return {0, 0, 0, 0};
|
||||
return {};
|
||||
}
|
||||
|
||||
Result load(ImageLoader* loader)
|
||||
|
|
|
@ -106,33 +106,14 @@ bool RenderPath::bounds(Matrix* m, float* x, float* y, float* w, float* h)
|
|||
|
||||
void RenderRegion::intersect(const RenderRegion& rhs)
|
||||
{
|
||||
auto x1 = x + w;
|
||||
auto y1 = y + h;
|
||||
auto x2 = rhs.x + rhs.w;
|
||||
auto y2 = rhs.y + rhs.h;
|
||||
if (min.x < rhs.min.x) min.x = rhs.min.x;
|
||||
if (min.y < rhs.min.y) min.y = rhs.min.y;
|
||||
if (max.x > rhs.max.x) max.x = rhs.max.x;
|
||||
if (max.y > rhs.max.y) max.y = rhs.max.y;
|
||||
|
||||
x = (x > rhs.x) ? x : rhs.x;
|
||||
y = (y > rhs.y) ? y : rhs.y;
|
||||
w = ((x1 < x2) ? x1 : x2) - x;
|
||||
h = ((y1 < y2) ? y1 : y2) - y;
|
||||
|
||||
if (w < 0) w = 0;
|
||||
if (h < 0) h = 0;
|
||||
}
|
||||
|
||||
|
||||
void RenderRegion::add(const RenderRegion& rhs)
|
||||
{
|
||||
if (rhs.x < x) {
|
||||
w += (x - rhs.x);
|
||||
x = rhs.x;
|
||||
}
|
||||
if (rhs.y < y) {
|
||||
h += (y - rhs.y);
|
||||
y = rhs.y;
|
||||
}
|
||||
if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x;
|
||||
if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y;
|
||||
// Not intersected: collapse to zero-area region
|
||||
if (max.x < min.x) max.x = min.x;
|
||||
if (max.y < min.y) max.y = min.y;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -94,16 +94,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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto region = paint->pImpl->bounds(renderer);
|
||||
if (paints.empty()) return {};
|
||||
|
||||
//Merge regions
|
||||
if (region.x < x1) x1 = region.x;
|
||||
if (x2 < region.x + region.w) x2 = (region.x + region.w);
|
||||
if (region.y < y1) y1 = region.y;
|
||||
if (y2 < region.y + region.h) y2 = (region.y + region.h);
|
||||
RenderRegion pRegion = {{INT32_MAX, INT32_MAX}, {0, 0}};
|
||||
for (auto paint : paints) {
|
||||
auto region = paint->pImpl->bounds(renderer);
|
||||
if (region.min.x < pRegion.min.x) pRegion.min.x = region.min.x;
|
||||
if (pRegion.max.x < region.max.x) pRegion.max.x = region.max.x;
|
||||
if (region.min.y < pRegion.min.y) pRegion.min.y = region.min.y;
|
||||
if (pRegion.max.y < region.max.y) pRegion.max.y = region.max.y;
|
||||
}
|
||||
|
||||
//Extends the render region if post effects require
|
||||
int32_t ex = 0, ey = 0, ew = 0, eh = 0;
|
||||
RenderRegion eRegion{};
|
||||
if (effects) {
|
||||
ARRAY_FOREACH(p, *effects) {
|
||||
auto effect = *p;
|
||||
if (effect->valid && renderer->region(effect)) {
|
||||
ex = std::min(ex, effect->extend.x);
|
||||
ey = std::min(ey, effect->extend.y);
|
||||
ew = std::max(ew, effect->extend.w);
|
||||
eh = std::max(eh, effect->extend.h);
|
||||
}
|
||||
if (effect->valid && renderer->region(effect)) eRegion.add(effect->extend);
|
||||
}
|
||||
}
|
||||
|
||||
auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh};
|
||||
ret.intersect(this->vport);
|
||||
return ret;
|
||||
pRegion.min.x += eRegion.min.x;
|
||||
pRegion.min.y += eRegion.min.y;
|
||||
pRegion.max.x += eRegion.max.x;
|
||||
pRegion.max.y += eRegion.max.y;
|
||||
|
||||
pRegion.intersect(this->vport);
|
||||
return pRegion;
|
||||
}
|
||||
|
||||
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)
|
||||
|
|
|
@ -114,7 +114,7 @@ struct ShapeImpl : Shape
|
|||
|
||||
RenderRegion bounds(RenderMethod* renderer)
|
||||
{
|
||||
if (!impl.rd) return {0, 0, 0, 0};
|
||||
if (!impl.rd) return {};
|
||||
return renderer->region(impl.rd);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
|
|||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//FIXME: The value must be associated with an individual canvas instance.
|
||||
|
|
|
@ -55,7 +55,7 @@ Result WgCanvas::target(void* device, void* instance, void* target, uint32_t w,
|
|||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target((WGPUDevice)device, (WGPUInstance)instance, target, w, h, type)) return Result::Unknown;
|
||||
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
|
||||
renderer->viewport(pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
|
|
|
@ -7,6 +7,7 @@ source_file = [
|
|||
'tvgWgRenderData.h',
|
||||
'tvgWgRenderer.h',
|
||||
'tvgWgRenderTarget.h',
|
||||
'tvgWgRenderTask.h',
|
||||
'tvgWgShaderSrc.h',
|
||||
'tvgWgShaderTypes.h',
|
||||
'tvgWgBindGroups.cpp',
|
||||
|
@ -17,6 +18,7 @@ source_file = [
|
|||
'tvgWgRenderData.cpp',
|
||||
'tvgWgRenderer.cpp',
|
||||
'tvgWgRenderTarget.cpp',
|
||||
'tvgWgRenderTask.cpp',
|
||||
'tvgWgShaderSrc.cpp',
|
||||
'tvgWgShaderTypes.cpp'
|
||||
]
|
||||
|
|
|
@ -269,3 +269,34 @@ void WgContext::releaseQueue(WGPUQueue& queue)
|
|||
queue = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WGPUCommandEncoder WgContext::createCommandEncoder()
|
||||
{
|
||||
WGPUCommandEncoderDescriptor commandEncoderDesc{};
|
||||
return wgpuDeviceCreateCommandEncoder(device, &commandEncoderDesc);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::submitCommandEncoder(WGPUCommandEncoder commandEncoder)
|
||||
{
|
||||
const WGPUCommandBufferDescriptor commandBufferDesc{};
|
||||
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
|
||||
wgpuQueueSubmit(queue, 1, &commandsBuffer);
|
||||
wgpuCommandBufferRelease(commandsBuffer);
|
||||
}
|
||||
|
||||
|
||||
void WgContext::releaseCommandEncoder(WGPUCommandEncoder& commandEncoder)
|
||||
{
|
||||
if (commandEncoder) {
|
||||
wgpuCommandEncoderRelease(commandEncoder);
|
||||
commandEncoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool WgContext::invalid()
|
||||
{
|
||||
return !instance || !device;
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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, ©Size);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "tvgWgRenderTarget.h"
|
||||
|
||||
void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
void WgRenderTarget::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
|
@ -36,7 +36,7 @@ void WgRenderStorage::initialize(WgContext& context, uint32_t width, uint32_t he
|
|||
}
|
||||
|
||||
|
||||
void WgRenderStorage::release(WgContext& context)
|
||||
void WgRenderTarget::release(WgContext& context)
|
||||
{
|
||||
context.layouts.releaseBindGroup(bindGroupTexure);
|
||||
context.layouts.releaseBindGroup(bindGroupWrite);
|
||||
|
@ -50,38 +50,38 @@ void WgRenderStorage::release(WgContext& context)
|
|||
}
|
||||
|
||||
//*****************************************************************************
|
||||
// render storage pool
|
||||
// render target pool
|
||||
//*****************************************************************************
|
||||
|
||||
WgRenderStorage* WgRenderStoragePool::allocate(WgContext& context)
|
||||
WgRenderTarget* WgRenderTargetPool::allocate(WgContext& context)
|
||||
{
|
||||
WgRenderStorage* renderStorage{};
|
||||
WgRenderTarget* renderTarget{};
|
||||
if (pool.count > 0) {
|
||||
renderStorage = pool.last();
|
||||
renderTarget = pool.last();
|
||||
pool.pop();
|
||||
} else {
|
||||
renderStorage = new WgRenderStorage;
|
||||
renderStorage->initialize(context, width, height);
|
||||
list.push(renderStorage);
|
||||
renderTarget = new WgRenderTarget;
|
||||
renderTarget->initialize(context, width, height);
|
||||
list.push(renderTarget);
|
||||
}
|
||||
return renderStorage;
|
||||
return renderTarget;
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStoragePool::free(WgContext& context, WgRenderStorage* renderStorage)
|
||||
void WgRenderTargetPool::free(WgContext& context, WgRenderTarget* renderTarget)
|
||||
{
|
||||
pool.push(renderStorage);
|
||||
pool.push(renderTarget);
|
||||
};
|
||||
|
||||
|
||||
void WgRenderStoragePool::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
void WgRenderTargetPool::initialize(WgContext& context, uint32_t width, uint32_t height)
|
||||
{
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
}
|
||||
|
||||
|
||||
void WgRenderStoragePool::release(WgContext& context)
|
||||
void WgRenderTargetPool::release(WgContext& context)
|
||||
{
|
||||
ARRAY_FOREACH(p, list) {
|
||||
(*p)->release(context);
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include "tvgWgPipelines.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
struct WgRenderStorage {
|
||||
struct WgRenderTarget {
|
||||
WGPUTexture texture{};
|
||||
WGPUTexture textureMS{};
|
||||
WGPUTextureView texView{};
|
||||
|
@ -42,15 +42,15 @@ struct WgRenderStorage {
|
|||
};
|
||||
|
||||
|
||||
class WgRenderStoragePool {
|
||||
class WgRenderTargetPool {
|
||||
private:
|
||||
Array<WgRenderStorage*> list;
|
||||
Array<WgRenderStorage*> pool;
|
||||
Array<WgRenderTarget*> list;
|
||||
Array<WgRenderTarget*> pool;
|
||||
uint32_t width{};
|
||||
uint32_t height{};
|
||||
public:
|
||||
WgRenderStorage* allocate(WgContext& context);
|
||||
void free(WgContext& context, WgRenderStorage* renderTarget);
|
||||
WgRenderTarget* allocate(WgContext& context);
|
||||
void free(WgContext& context, WgRenderTarget* renderTarget);
|
||||
|
||||
void initialize(WgContext& context, uint32_t width, uint32_t height);
|
||||
void release(WgContext& context);
|
||||
|
|
93
src/renderer/wg_engine/tvgWgRenderTask.cpp
Normal file
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2025 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgWgRenderTask.h"
|
||||
#include <iostream>
|
||||
|
||||
//***********************************************************************
|
||||
// WgPaintTask
|
||||
//***********************************************************************
|
||||
|
||||
void WgPaintTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
if (renderData->type() == tvg::Type::Shape)
|
||||
compositor.renderShape(context, (WgRenderDataShape*)renderData, blendMethod);
|
||||
if (renderData->type() == tvg::Type::Picture)
|
||||
compositor.renderImage(context, (WgRenderDataPicture*)renderData, blendMethod);
|
||||
else assert(true);
|
||||
}
|
||||
|
||||
//***********************************************************************
|
||||
// WgSceneTask
|
||||
//***********************************************************************
|
||||
|
||||
void WgSceneTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
// begin the render pass for the current scene and clear the target content
|
||||
WGPUColor color{};
|
||||
if ((compose->method == MaskMethod::None) && (compose->blend != BlendMethod::Normal)) color = { 1.0, 1.0, 1.0, 0.0 };
|
||||
compositor.beginRenderPass(encoder, renderTarget, true, color);
|
||||
// run all childs (scenes and shapes)
|
||||
runChildren(context, compositor, encoder);
|
||||
// we must to end current render pass for current scene
|
||||
compositor.endRenderPass();
|
||||
// we must to apply effect for current scene
|
||||
if (effect)
|
||||
runEffect(context, compositor, encoder);
|
||||
// there's no point in continuing if the scene has no destination target (e.g., the root scene)
|
||||
if (!renderTargetDst) return;
|
||||
// apply scene blending
|
||||
if (compose->method == MaskMethod::None) {
|
||||
compositor.beginRenderPass(encoder, renderTargetDst, false);
|
||||
compositor.renderScene(context, renderTarget, compose);
|
||||
// apply scene composition (for scenes, that have a handle to mask)
|
||||
} else if (renderTargetMsk) {
|
||||
compositor.beginRenderPass(encoder, renderTargetDst, false);
|
||||
compositor.composeScene(context, renderTarget, renderTargetMsk, compose);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgSceneTask::runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
ARRAY_FOREACH(task, children) {
|
||||
WgRenderTask* renderTask = *task;
|
||||
// we need to restore current render pass without clear
|
||||
compositor.beginRenderPass(encoder, renderTarget, false);
|
||||
// run children (shape or scene)
|
||||
renderTask->run(context, compositor, encoder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WgSceneTask::runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
|
||||
{
|
||||
assert(effect);
|
||||
switch (effect->type) {
|
||||
case SceneEffect::GaussianBlur: compositor.gaussianBlur(context, renderTarget, (RenderEffectGaussianBlur*)effect, compose); break;
|
||||
case SceneEffect::DropShadow: compositor.dropShadow(context, renderTarget, (RenderEffectDropShadow*)effect, compose); break;
|
||||
case SceneEffect::Fill: compositor.fillEffect(context, renderTarget, (RenderEffectFill*)effect, compose); break;
|
||||
case SceneEffect::Tint: compositor.tintEffect(context, renderTarget, (RenderEffectTint*)effect, compose); break;
|
||||
case SceneEffect::Tritone : compositor.tritoneEffect(context, renderTarget, (RenderEffectTritone*)effect, compose); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
72
src/renderer/wg_engine/tvgWgRenderTask.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (c) 2025 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_WG_RENDER_TASK_H_
|
||||
#define _TVG_WG_RENDER_TASK_H_
|
||||
|
||||
#include "tvgWgCompositor.h"
|
||||
|
||||
// base class for any renderable objects
|
||||
struct WgRenderTask {
|
||||
virtual ~WgRenderTask() {}
|
||||
virtual void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) = 0;
|
||||
};
|
||||
|
||||
// task for sinlge shape rendering
|
||||
struct WgPaintTask: public WgRenderTask {
|
||||
// shape render properties
|
||||
WgRenderDataPaint* renderData{};
|
||||
BlendMethod blendMethod{};
|
||||
|
||||
WgPaintTask(WgRenderDataPaint* renderData, BlendMethod blendMethod) :
|
||||
renderData(renderData), blendMethod(blendMethod) {}
|
||||
// apply shape execution, including custom blending and clipping
|
||||
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
|
||||
};
|
||||
|
||||
// task for scene rendering with blending, composition and effect
|
||||
struct WgSceneTask: public WgRenderTask {
|
||||
public:
|
||||
// parent scene (nullptr for root scene)
|
||||
WgSceneTask* parent{};
|
||||
// childs can be shapes or scenes tesks
|
||||
Array<WgRenderTask*> children;
|
||||
// scene blend/compose targets
|
||||
WgRenderTarget* renderTarget{};
|
||||
WgRenderTarget* renderTargetMsk{};
|
||||
WgRenderTarget* renderTargetDst{};
|
||||
// scene blend/compose properties
|
||||
WgCompose* compose{};
|
||||
// scene effect properties
|
||||
const RenderEffect* effect{};
|
||||
|
||||
WgSceneTask(WgRenderTarget* renderTarget, WgCompose* compose, WgSceneTask* parent) :
|
||||
parent(parent), renderTarget(renderTarget), compose(compose) {}
|
||||
// run all, including all shapes drawing, blending, composition and effect
|
||||
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
|
||||
private:
|
||||
void runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
|
||||
void runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
|
||||
};
|
||||
|
||||
#endif // _TVG_WG_RENDER_TASK_H_
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
//************************************************************************
|
||||
|
|
|
@ -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();
|
||||
|
|