Compare commits

...

38 commits

Author SHA1 Message Date
Hermet Park
830db9ecb7 sw_engine: code refactoring
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
- removed a redundant code, sync() will take over.
- deferred the sync as possible as later
2025-06-13 15:43:06 +09:00
Sergii Liebodkin
ed93570756 wg_engine: remove unnecessary mesh pools
remove unnecessary mesh pools
2025-06-13 11:37:34 +09:00
Mira Grudzinska
27e78095ff lottie: fix offset with miter join
One point was skipped during the creation of the offset
corner. The error was not visible because the point lies
on the line, but it will become apparent if further
modifiers are applied to the object (not supported now).
2025-06-13 11:36:22 +09:00
Hermet Park
60f0f4cbe4 Revert "sw_engine: replaced texture mapping AA with 4x sampling interp"
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
This reverts commit 174fae9089.

Will revisit this since the quality is too poor...
2025-06-12 23:40:18 +09:00
Hermet Park
d5a5e3215c renderer: hotfix
added exceptional handling
2025-06-12 23:04:16 +09:00
Sergii Liebodkin
92e3c243ec wg_engine: uniform stage buffers implementation
Introduced stage buffer for uniforms to reduce number of memory shafles from cpu to gpu memory

https://github.com/thorvg/thorvg/issues/3505
2025-06-12 21:57:24 +09:00
Mira Grudzinska
4c3c5d9d06 lottie: readability++
Introduce the _colinear function - checks if a Bezier curve
is degenerated to a line.
2025-06-12 18:48:07 +09:00
Hermet Park
8f10f45756 renderer: caching the viewport update
reuse cached scene viewport data if scene had no update before,
just a minor optimization
2025-06-12 18:44:07 +09:00
Hermet Park
3eaf110e0a sw_engine: revised the texture clipping
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
previous logic doesn't work if the clipping shape
has any inner corners. replace the logic
with a intermediate composition approach for
stability.

- performance can be drop at texture clipping by ~11%
- size is reduced by -0.5kb

issue: https://github.com/thorvg/thorvg/issues/3520
2025-06-11 22:35:54 +09:00
Hermet Park
174fae9089 sw_engine: replaced texture mapping AA with 4x sampling interp
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
The old approach often produced incorrect results,
especially when the fixed pixel had a noticeably
different color from the texture due to the AA
target blending position was fixed.

Although the previous method worked well as an analytical AA
solution with good speed and fair quality, it couldn't
overcome the above limitation.

The new approach still applies AA only to polygon edges
for efficiency. While the quality may be slightly reduced,
it offers greater stability.

- binary size: -1.1kb
- performance diff: ignoreable

issue: https://github.com/thorvg/thorvg/issues/1729
2025-06-11 10:09:39 +09:00
Hermet Park
596f7f767f renderer: chores++
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
2025-06-11 01:49:36 +09:00
Hermet Park
2e5af58592 sw_engine: texture mapping performance optimization
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
- Replaced `modff()` with a custom method,
boosting texture mapping performance by ~15%.

- Unified opacity/non-opacity logic for improved
binary size efficiency(-0.5kb).

- Implemented minor changes for better cache effectiveness.
2025-06-10 19:38:20 +09:00
Hermet Park
bfef89858f renderer: increased the reference count capacity
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
255 is enough size in general but a bit limited.
2025-06-10 14:51:19 +09:00
Hermet Park
b3d73e1568 lottie: do not try matting if the layer has no contents
Some checks are pending
Android / build_x86_64 (push) Waiting to run
Android / build_aarch64 (push) Waiting to run
iOS / build_x86_64 (push) Waiting to run
iOS / build_arm64 (push) Waiting to run
macOS / build (push) Waiting to run
macOS / compact_test (push) Waiting to run
macOS / unit_test (push) Waiting to run
Ubuntu / build (push) Waiting to run
Ubuntu / compact_test (push) Waiting to run
Ubuntu / unit_test (push) Waiting to run
Windows / build (push) Waiting to run
Windows / compact_test (push) Waiting to run
Windows / unit_test (push) Waiting to run
2025-06-09 22:33:25 +09:00
Hermet Park
a2665cbab7 lottie: fixed a repeater opacity logic
preserve the target opacity by multiplying,
do not overwrite it.
2025-06-09 18:52:05 +09:00
Hermet Park
6c1b388d77 docs: updated contributors 2025-06-09 17:04:11 +09:00
Sungun No
676a465c55 gl_engine: fix always-true clear flag
The clear flag specified in Canvas::draw is ignored when set to false,
since GlRenderer::mClearBuffer is never explicitly reset to false.

This commit ensures that mClearBuffer is reset to its default (false)
after being used, so that the clear operation behaves correctly per frame.

- Issue: #1779
2025-06-09 17:02:56 +09:00
Hermet Park
8eb046c318 sw_engine: skip AA if texture are orthogonally rotated.
there is no necessary applying AA for 0/90/180/270 degree rotation

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

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

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

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

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

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

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

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

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

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

View file

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

View file

@ -44,3 +44,4 @@ Thaddeus Crews <repiteo@outlook.com>
Benjamin <benjaminhalko@hotmail.com> Benjamin <benjaminhalko@hotmail.com>
Benson Muite <benson_muite@emailplus.com> Benson Muite <benson_muite@emailplus.com>
kkocdko <kkocdko@gmail.com> kkocdko <kkocdko@gmail.com>
SoonGeon Noh <nors.nsg@gmail.com>

View file

@ -295,7 +295,7 @@ ThorVG facilitates [SVG Tiny Specification](https://www.w3.org/TR/SVGTiny12/) re
The figure below highlights ThorVG's SVG rendering capabilities: The figure below highlights ThorVG's SVG rendering capabilities:
<p align="center"> <p align="center">
<img width="780" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_svg.png"> <img width="780" height="auto" src="https://github.com/thorvg/thorvg/blob/main/res/example_svg.jpg">
</p> </p>
The following code snippet shows how to draw SVG image using ThorVG: The following code snippet shows how to draw SVG image using ThorVG:

View file

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

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 KiB

After

Width:  |  Height:  |  Size: 390 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 426 KiB

After

Width:  |  Height:  |  Size: 58 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 533 KiB

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 617 KiB

After

Width:  |  Height:  |  Size: 192 KiB

View file

@ -65,6 +65,7 @@ namespace tvg
class RenderMethod; class RenderMethod;
class Animation; class Animation;
class Shape;
/** /**
* @defgroup ThorVG ThorVG * @defgroup ThorVG ThorVG
@ -229,9 +230,9 @@ enum class BlendMethod : uint8_t
enum class SceneEffect : uint8_t enum class SceneEffect : uint8_t
{ {
ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state. ClearAll = 0, ///< Reset all previously applied scene effects, restoring the scene to its original state.
GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(3) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]} GaussianBlur, ///< Apply a blur effect with a Gaussian filter. Param(4) = {sigma(float)[> 0], direction(int)[both: 0 / horizontal: 1 / vertical: 2], border(int)[duplicate: 0 / wrap: 1], quality(int)[0 - 100]}
DropShadow, ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(double)[0 - 360], distance(double), blur_sigma(double)[> 0], quality(int)[0 - 100]} DropShadow, ///< Apply a drop shadow effect with a Gaussian Blur filter. Param(8) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255], angle(double)[0 - 360], distance(double), blur_sigma(double)[> 0], quality(int)[0 - 100]}
Fill, ///< Override the scene content color with a given fill information (Experimental API). Param(5) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255]} Fill, ///< Override the scene content color with a given fill information (Experimental API). Param(4) = {color_R(int)[0 - 255], color_G(int)[0 - 255], color_B(int)[0 - 255], opacity(int)[0 - 255]}
Tint, ///< Tinting the current scene color with a given black, white color paramters (Experimental API). Param(7) = {black_R(int)[0 - 255], black_G(int)[0 - 255], black_B(int)[0 - 255], white_R(int)[0 - 255], white_G(int)[0 - 255], white_B(int)[0 - 255], intensity(float)[0 - 100]} Tint, ///< Tinting the current scene color with a given black, white color paramters (Experimental API). Param(7) = {black_R(int)[0 - 255], black_G(int)[0 - 255], black_B(int)[0 - 255], white_R(int)[0 - 255], white_G(int)[0 - 255], white_B(int)[0 - 255], intensity(float)[0 - 100]}
Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights (Experimental API). Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255]} Tritone ///< Apply a tritone color effect to the scene using three color parameters for shadows, midtones, and highlights (Experimental API). Param(9) = {Shadow_R(int)[0 - 255], Shadow_G(int)[0 - 255], Shadow_B(int)[0 - 255], Midtone_R(int)[0 - 255], Midtone_G(int)[0 - 255], Midtone_B(int)[0 - 255], Highlight_R(int)[0 - 255], Highlight_G(int)[0 - 255], Highlight_B(int)[0 - 255]}
}; };
@ -396,13 +397,13 @@ public:
* *
* @param[in] clipper The shape object as the clipper. * @param[in] clipper The shape object as the clipper.
* *
* @retval Result::NonSupport If the @p clipper type is not Shape. * @retval Result::InsufficientCondition if the @p clipper has already belonged to another paint.
* @retval Result::InsufficientCondition if the target has already belonged to another paint. *
* @see Paint::clip()
* *
* @note @p clipper only supports the Shape type.
* @since 1.0 * @since 1.0
*/ */
Result clip(Paint* clipper) noexcept; Result clip(Shape* clipper) noexcept;
/** /**
* @brief Sets the blending method for the paint object. * @brief Sets the blending method for the paint object.
@ -478,6 +479,19 @@ public:
*/ */
MaskMethod mask(const Paint** target) const noexcept; MaskMethod mask(const Paint** target) const noexcept;
/**
* @brief Get the clipper shape of the paint object.
*
* This function returns the clipper that has been previously set to this paint object.
*
* @return The shape object used as the clipper, or @c nullptr if no clipper is set.
*
* @see Paint::clip(Shape* clipper)
*
* @since 1.0
*/
Shape* clip() const noexcept;
/** /**
* @brief Increment the reference count for the Paint instance. * @brief Increment the reference count for the Paint instance.
* *

BIN
res/example_svg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 818 KiB

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -90,7 +90,7 @@ bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline bool rightAngle(const Matrix& m) static inline bool rightAngle(const Matrix& m)
{ {
auto radian = fabsf(tvg::atan2(m.e21, m.e11)); auto radian = fabsf(tvg::atan2(m.e21, m.e11));
if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true; if (tvg::zero(radian) || tvg::zero(radian - MATH_PI2) || tvg::zero(radian - MATH_PI)) return true;
return false; return false;
} }

View file

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

View file

@ -352,13 +352,11 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
for (int i = 0; i < repeater->cnt; ++i) { for (int i = 0; i < repeater->cnt; ++i) {
auto multiplier = repeater->offset + static_cast<float>(i); auto multiplier = repeater->offset + static_cast<float>(i);
ARRAY_FOREACH(p, propagators) { ARRAY_FOREACH(p, propagators) {
auto shape = static_cast<Shape*>((*p)->duplicate()); auto shape = static_cast<Shape*>((*p)->duplicate());
SHAPE(shape)->rs.path = SHAPE(path)->rs.path; SHAPE(shape)->rs.path = SHAPE(path)->rs.path;
auto opacity = tvg::lerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt);
auto opacity = repeater->interpOpacity ? tvg::lerp<uint8_t>(repeater->startOpacity, repeater->endOpacity, static_cast<float>(i + 1) / repeater->cnt) : repeater->startOpacity; shape->opacity(MULTIPLY((*p)->opacity(), opacity));
shape->opacity(opacity);
Matrix m; Matrix m;
tvg::identity(&m); tvg::identity(&m);
@ -366,11 +364,10 @@ static void _repeat(LottieGroup* parent, Shape* path, RenderContext* ctx)
scale(&m, {powf(repeater->scale.x * 0.01f, multiplier), powf(repeater->scale.y * 0.01f, multiplier)}); scale(&m, {powf(repeater->scale.x * 0.01f, multiplier), powf(repeater->scale.y * 0.01f, multiplier)});
rotate(&m, repeater->rotation * multiplier); rotate(&m, repeater->rotation * multiplier);
translateR(&m, -repeater->anchor); translateR(&m, -repeater->anchor);
m = repeater->transform * m;
Matrix inv; Matrix inv;
inverse(&repeater->transform, &inv); inverse(&repeater->transform, &inv);
shape->transform(m * (inv * shape->transform())); shape->transform((repeater->transform * m) * (inv * shape->transform()));
shapes.push(shape); shapes.push(shape);
} }
} }
@ -739,7 +736,6 @@ void LottieBuilder::updateRepeater(TVG_UNUSED LottieGroup* parent, LottieObject*
r.startOpacity = repeater->startOpacity(frameNo, tween, exps); r.startOpacity = repeater->startOpacity(frameNo, tween, exps);
r.endOpacity = repeater->endOpacity(frameNo, tween, exps); r.endOpacity = repeater->endOpacity(frameNo, tween, exps);
r.inorder = repeater->inorder; r.inorder = repeater->inorder;
r.interpOpacity = (r.startOpacity == r.endOpacity) ? false : true;
ctx->repeaters.push(r); ctx->repeaters.push(r);
ctx->merging = nullptr; ctx->merging = nullptr;
@ -1174,8 +1170,8 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
{ {
if (layer->masks.count == 0) return; if (layer->masks.count == 0) return;
//Introduce an intermediate scene for embracing the matte + masking //Introduce an intermediate scene for embracing matte + masking or precomp clipping + masking replaced by clipping
if (layer->matteTarget) { if (layer->matteTarget || layer->type == LottieLayer::Precomp) {
auto scene = Scene::gen(); auto scene = Scene::gen();
scene->push(layer->scene); scene->push(layer->scene);
layer->scene = scene; layer->scene = scene;
@ -1192,7 +1188,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
auto method = mask->method; auto method = mask->method;
auto opacity = mask->opacity(frameNo); auto opacity = mask->opacity(frameNo);
auto expand = mask->expand(frameNo); auto expand = mask->expand(frameNo);
auto fastTrack = false; //single clipping
//the first mask //the first mask
if (!pShape) { if (!pShape) {
@ -1203,7 +1198,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha) { if (layer->masks.count == 1 && compMethod == MaskMethod::Alpha) {
layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity)); layer->scene->opacity(MULTIPLY(layer->scene->opacity(), opacity));
layer->scene->clip(pShape); layer->scene->clip(pShape);
fastTrack = true;
} else { } else {
layer->scene->mask(pShape, compMethod); layer->scene->mask(pShape, compMethod);
} }
@ -1229,9 +1223,6 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
auto offset = LottieOffsetModifier(expand); auto offset = LottieOffsetModifier(expand);
mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, tween, exps, &offset); mask->pathset(frameNo, SHAPE(pShape)->rs.path, nullptr, tween, exps, &offset);
} }
if (fastTrack) return;
pOpacity = opacity; pOpacity = opacity;
pMethod = method; pMethod = method;
} }
@ -1241,7 +1232,7 @@ void LottieBuilder::updateMasks(LottieLayer* layer, float frameNo)
bool LottieBuilder::updateMatte(LottieComposition* comp, float frameNo, Scene* scene, LottieLayer* layer) bool LottieBuilder::updateMatte(LottieComposition* comp, float frameNo, Scene* scene, LottieLayer* layer)
{ {
auto target = layer->matteTarget; auto target = layer->matteTarget;
if (!target) return true; if (!target || target->type == LottieLayer::Null) return true;
updateLayer(comp, scene, target, frameNo); updateLayer(comp, scene, target, frameNo);

View file

@ -42,7 +42,6 @@ struct RenderRepeater
float rotation; float rotation;
uint8_t startOpacity; uint8_t startOpacity;
uint8_t endOpacity; uint8_t endOpacity;
bool interpOpacity;
bool inorder; bool inorder;
}; };

View file

@ -27,6 +27,12 @@
/* Internal Class Implementation */ /* Internal Class Implementation */
/************************************************************************/ /************************************************************************/
static bool _colinear(const Point* p)
{
return tvg::zero(*p - *(p + 1)) && tvg::zero(*(p + 2) - *(p + 3));
}
static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, Point& prev, Point& curr, Point& next, float r) static void _roundCorner(Array<PathCommand>& cmds, Array<Point>& pts, Point& prev, Point& curr, Point& next, float r)
{ {
auto lenPrev = length(prev - curr); auto lenPrev = length(prev - curr);
@ -115,9 +121,12 @@ void LottieOffsetModifier::corner(RenderPath& out, Line& line, Line& nextLine, u
auto norm = normal(line.pt1, line.pt2); auto norm = normal(line.pt1, line.pt2);
auto nextNorm = normal(nextLine.pt1, nextLine.pt2); auto nextNorm = normal(nextLine.pt1, nextLine.pt2);
auto miterDirection = (norm + nextNorm) / length(norm + nextNorm); auto miterDirection = (norm + nextNorm) / length(norm + nextNorm);
if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) {
out.cmds.push(PathCommand::LineTo); out.cmds.push(PathCommand::LineTo);
if (1.0f <= miterLimit * fabsf(miterDirection.x * norm.x + miterDirection.y * norm.y)) out.pts.push(intersect); out.pts.push(intersect);
else out.pts.push(nextLine.pt1); }
out.cmds.push(PathCommand::LineTo);
out.pts.push(nextLine.pt1);
} else { } else {
out.cmds.push(PathCommand::LineTo); out.cmds.push(PathCommand::LineTo);
out.pts.push(nextLine.pt1); out.pts.push(nextLine.pt1);
@ -192,14 +201,10 @@ bool LottieRoundnessModifier::modifyPath(PathCommand* inCmds, uint32_t inCmdsCnt
break; break;
} }
case PathCommand::CubicTo: { case PathCommand::CubicTo: {
if (iCmds < inCmdsCnt - 1 && _colinear(inPts + iPts - 1)) {
auto& prev = inPts[iPts - 1]; auto& prev = inPts[iPts - 1];
auto& curr = inPts[iPts + 2]; auto& curr = inPts[iPts + 2];
if (iCmds < inCmdsCnt - 1 && if (inCmds[iCmds + 1] == PathCommand::CubicTo && _colinear(inPts + iPts + 2)) {
tvg::zero(inPts[iPts - 1] - inPts[iPts]) &&
tvg::zero(inPts[iPts + 1] - inPts[iPts + 2])) {
if (inCmds[iCmds + 1] == PathCommand::CubicTo &&
tvg::zero(inPts[iPts + 2] - inPts[iPts + 3]) &&
tvg::zero(inPts[iPts + 4] - inPts[iPts + 5])) {
_roundCorner(path.cmds, path.pts, prev, curr, inPts[iPts + 5], r); _roundCorner(path.cmds, path.pts, prev, curr, inPts[iPts + 5], r);
iPts += 3; iPts += 3;
break; break;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -153,7 +153,7 @@ private:
} mDisposed; } mDisposed;
BlendMethod mBlendMethod = BlendMethod::Normal; BlendMethod mBlendMethod = BlendMethod::Normal;
bool mClearBuffer = true; //FIXME: clear buffer should be optional (default is false) bool mClearBuffer = false;
}; };
#endif /* _TVG_GL_RENDERER_H_ */ #endif /* _TVG_GL_RENDERER_H_ */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

@ -51,100 +51,57 @@ static float dxdya, dxdyb, dudya, dvdya;
static float xa, xb, ua, va; static float xa, xb, ua, va;
//Y Range exception handling static inline int32_t _modf(float v)
static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
{ {
int32_t regionTop, regionBottom; return 255 - ((int(v * 256.0f)) & 255);
if (region) {
regionTop = region->min.y;
regionBottom = region->max.y;
} else {
regionTop = image->rle->spans->y;
regionBottom = image->rle->spans[image->rle->size - 1].y;
}
if (yStart < regionTop) yStart = regionTop;
if (yEnd > regionBottom) yEnd = regionBottom;
return yEnd > yStart;
} }
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0) static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{ {
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()"); TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
return false; return false;
} }
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity) static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
{ {
float _dudx = dudx, _dvdx = dvdx; float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va; float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32; auto sbuf = image.buf32;
auto dbuf = surface->buf32; auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->w); int32_t sw = static_cast<int32_t>(image.w);
int32_t sh = static_cast<int32_t>(image->h); int32_t sh = static_cast<int32_t>(image.h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0; int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0; float dx, u, v;
float dx, u, v, iptr;
uint32_t* buf; uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, region, yStart, yEnd)) return; if (yStart < bbox.min.y) yStart = bbox.min.y;
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
} else {
span = image->rle->spans;
while (span->y < yStart) {
++span;
++spanIdx;
}
}
y = yStart; y = yStart;
while (y < yEnd) { while (y < yEnd) {
x1 = (int32_t)_xa; x1 = std::max((int32_t)_xa, bbox.min.x);
x2 = (int32_t)_xb; x2 = std::min((int32_t)_xb, bbox.max.x);
if (!region) {
minx = INT32_MAX;
maxx = 0;
//one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size) {
if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
++spanIdx;
}
}
if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx;
//Anti-Aliasing frames //Anti-Aliasing frames
if (aaSpans) {
ay = y - aaSpans->yStart; ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
}
//Range allowed //Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
//Perform subtexel pre-stepping on UV //Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1); dx = 1 - (_xa - x1);
u = _ua + dx * _dudx; u = _ua + dx * _dudx;
v = _va + dx * _dvdx; v = _va + dx * _dvdx;
buf = dbuf + ((y * surface->stride) + x1); buf = dbuf + ((y * surface->stride) + x1);
x = x1; x = x1;
//Draw horizontal line //Draw horizontal line
@ -152,29 +109,29 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
uu = (int) u; uu = (int) u;
vv = (int) v; vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
ar = (int)(255 * (1 - modff(u, &iptr))); ar = _modf(u);
ab = (int)(255 * (1 - modff(v, &iptr))); ab = _modf(v);
iru = uu + 1; iru = uu + 1;
irv = vv + 1; irv = vv + 1;
px = *(sbuf + (vv * image->stride) + uu); px = *(sbuf + (vv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* right pixel */ /* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru); int px2 = *(sbuf + (vv * image.stride) + iru);
px = INTERPOLATE(px, px2, ar); px = INTERPOLATE(px, px2, ar);
} }
/* vertical interpolate */ /* vertical interpolate */
if (irv < sh) { if (irv < sh) {
/* bottom pixel */ /* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu); int px2 = *(sbuf + (irv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* bottom right pixel */ /* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru); int px3 = *(sbuf + (irv * image.stride) + iru);
px2 = INTERPOLATE(px2, px3, ar); px2 = INTERPOLATE(px2, px3, ar);
} }
px = INTERPOLATE(px, px2, ab); px = INTERPOLATE(px, px2, ab);
@ -195,8 +152,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!region && spanIdx >= image->rle->size) break;
++y; ++y;
} }
xa = _xa; xa = _xa;
@ -206,122 +161,95 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
} }
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting) static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
{ {
float _dudx = dudx, _dvdx = dvdx; float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya; float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va; float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32; auto sbuf = image.buf32;
auto dbuf = surface->buf32; auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->w); int32_t sw = static_cast<int32_t>(image.w);
int32_t sh = static_cast<int32_t>(image->h); int32_t sh = static_cast<int32_t>(image.h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay; int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0; int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0; float dx, u, v;
float dx, u, v, iptr;
uint32_t* buf; uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
//for matting(composition) //for matting(composition)
auto csize = matting ? surface->compositor->image.channelSize: 0; auto csize = matting ? surface->compositor->image.channelSize: 0;
auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr; auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
uint8_t* cmp = nullptr; uint8_t* cmp = nullptr;
if (!_arrange(image, region, yStart, yEnd)) return; if (yStart < bbox.min.y) yStart = bbox.min.y;
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
} else {
span = image->rle->spans;
while (span->y < yStart) {
++span;
++spanIdx;
}
}
y = yStart; y = yStart;
while (y < yEnd) { while (y < yEnd) {
x1 = (int32_t)_xa; x1 = std::max((int32_t)_xa, bbox.min.x);
x2 = (int32_t)_xb; x2 = std::min((int32_t)_xb, bbox.max.x);
if (!region) {
minx = INT32_MAX;
maxx = 0;
//one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size) {
if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
++spanIdx;
}
}
if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx;
//Anti-Aliasing frames //Anti-Aliasing frames
if (aaSpans) {
ay = y - aaSpans->yStart; ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1; if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2; if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
}
//Range allowed //Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) { if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
//Perform subtexel pre-stepping on UV //Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1); dx = 1 - (_xa - x1);
u = _ua + dx * _dudx; u = _ua + dx * _dudx;
v = _va + dx * _dvdx; v = _va + dx * _dvdx;
buf = dbuf + ((y * surface->stride) + x1); buf = dbuf + ((y * surface->stride) + x1);
x = x1; x = x1;
if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize]; if (matting) cmp = &surface->compositor->image.buf8[(y * surface->compositor->image.stride + x1) * csize];
if (opacity == 255) { const auto fullOpacity = (opacity == 255);
//Draw horizontal line //Draw horizontal line
while (x++ < x2) { while (x++ < x2) {
uu = (int) u; uu = (int) u;
vv = (int) v; vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue; if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
ar = (int)(255.0f * (1.0f - modff(u, &iptr))); ar = _modf(u);
ab = (int)(255.0f * (1.0f - modff(v, &iptr))); ab = _modf(v);
iru = uu + 1; iru = uu + 1;
irv = vv + 1; irv = vv + 1;
px = *(sbuf + (vv * image->stride) + uu); px = *(sbuf + (vv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* right pixel */ /* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru); int px2 = *(sbuf + (vv * image.stride) + iru);
px = INTERPOLATE(px, px2, ar); px = INTERPOLATE(px, px2, ar);
} }
/* vertical interpolate */ /* vertical interpolate */
if (irv < sh) { if (irv < sh) {
/* bottom pixel */ /* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu); int px2 = *(sbuf + (irv * image.stride) + uu);
/* horizontal interpolate */ /* horizontal interpolate */
if (iru < sw) { if (iru < sw) {
/* bottom right pixel */ /* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru); int px3 = *(sbuf + (irv * image.stride) + iru);
px2 = INTERPOLATE(px2, px3, ar); px2 = INTERPOLATE(px2, px3, ar);
} }
px = INTERPOLATE(px, px2, ab); px = INTERPOLATE(px, px2, ab);
} }
uint32_t src; uint32_t src;
if (matting) { if (matting) {
src = ALPHA_BLEND(px, alpha(cmp)); auto a = alpha(cmp);
src = fullOpacity ? ALPHA_BLEND(px, a) : ALPHA_BLEND(px, MULTIPLY(opacity, a));
cmp += csize; cmp += csize;
} else { } else {
src = px; src = fullOpacity ? px : ALPHA_BLEND(px, opacity);
} }
*buf = src + ALPHA_BLEND(*buf, IA(src)); *buf = src + ALPHA_BLEND(*buf, IA(src));
++buf; ++buf;
@ -330,55 +258,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
u += _dudx; u += _dudx;
v += _dvdx; v += _dvdx;
} }
} else {
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
}
uint32_t src;
if (matting) {
src = ALPHA_BLEND(px, MULTIPLY(opacity, alpha(cmp)));
cmp += csize;
} else {
src = ALPHA_BLEND(px, opacity);
}
*buf = src + ALPHA_BLEND(*buf, IA(src));
++buf;
//Step UV horizontally
u += _dudx;
v += _dvdx;
}
}
} }
//Step along both edges //Step along both edges
@ -387,8 +266,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
_ua += _dudya; _ua += _dudya;
_va += _dvdya; _va += _dvdya;
if (!region && spanIdx >= image->rle->size) break;
++y; ++y;
} }
xa = _xa; xa = _xa;
@ -399,7 +276,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
/* This mapping algorithm is based on Mikael Kalms's. */ /* This mapping algorithm is based on Mikael Kalms's. */
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity) static void _rasterPolygonImage(SwSurface* surface, const SwImage& image, const RenderRegion& bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
{ {
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x}; float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y}; float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@ -460,7 +337,6 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (tvg::equal(y[0], y[1])) side = x[0] > x[1]; if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
if (tvg::equal(y[1], y[2])) side = x[2] > x[1]; if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
auto compositing = _compositing(surface); //Composition required auto compositing = _compositing(surface); //Composition required
auto blending = _blending(surface); //Blending required auto blending = _blending(surface); //Blending required
@ -479,7 +355,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible //Draw upper segment if possibly visible
if (yi[0] < yi[1]) { if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
xa += (off_y * dxdya); xa += (off_y * dxdya);
ua += (off_y * dudya); ua += (off_y * dudya);
va += (off_y * dvdya); va += (off_y * dvdya);
@ -489,18 +365,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
xb = x[0] + dy * dxdyb + (off_y * dxdyb); xb = x[0] + dy * dxdyb + (off_y * dxdyb);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 1);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else { } else {
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
} }
upper = true; upper = true;
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
if (yi[1] < yi[2]) { if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
if (!upper) { if (!upper) {
xa += (off_y * dxdya); xa += (off_y * dxdya);
ua += (off_y * dudya); ua += (off_y * dudya);
@ -510,12 +386,12 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
dxdyb = dxdy[2]; dxdyb = dxdy[2];
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb); xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 2);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else { } else {
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
} }
} }
//Longer edge is on the right side //Longer edge is on the right side
@ -527,7 +403,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible //Draw upper segment if possibly visible
if (yi[0] < yi[1]) { if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0; off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
xb += (off_y *dxdyb); xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping // Set slopes along left edge and perform subpixel pre-stepping
@ -540,18 +416,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
va = v[0] + dy * dvdya + (off_y * dvdya); va = v[0] + dy * dvdya + (off_y * dvdya);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 3);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else { } else {
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false); _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
} }
upper = true; upper = true;
} }
//Draw lower segment if possibly visible //Draw lower segment if possibly visible
if (yi[1] < yi[2]) { if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0; off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
if (!upper) xb += (off_y *dxdyb); if (!upper) xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping // Set slopes along left edge and perform subpixel pre-stepping
@ -564,25 +440,20 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
va = v[1] + dy * dvdya + (off_y * dvdya); va = v[1] + dy * dvdya + (off_y * dvdya);
if (compositing) { if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true); if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4); else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 4);
} else if (blending) { } else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity); _rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else { } else {
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false); _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
} }
} }
} }
} }
static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region) static AASpans* _AASpans(int yStart, int yEnd)
{ {
auto yStart = static_cast<int>(ymin);
auto yEnd = static_cast<int>(ymax);
if (!_arrange(image, region, yStart, yEnd)) return nullptr;
auto aaSpans = tvg::malloc<AASpans*>(sizeof(AASpans)); auto aaSpans = tvg::malloc<AASpans*>(sizeof(AASpans));
aaSpans->yStart = yStart; aaSpans->yStart = yStart;
aaSpans->yEnd = yEnd; aaSpans->yEnd = yEnd;
@ -661,14 +532,9 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
ptx[1] = tx[1]; \ ptx[1] = tx[1]; \
} while (0) } while (0)
struct Point
{
int32_t x, y;
};
int32_t y = 0; int32_t y = 0;
Point pEdge = {-1, -1}; //previous edge point SwPoint pEdge = {-1, -1}; //previous edge point
Point edgeDiff = {0, 0}; //temporary used for point distance SwPoint edgeDiff = {0, 0}; //temporary used for point distance
/* store bigger to tx[0] between prev and current edge's x positions. */ /* store bigger to tx[0] between prev and current edge's x positions. */
int32_t tx[2] = {0, 0}; int32_t tx[2] = {0, 0};
@ -791,29 +657,24 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
} }
static bool _apply(SwSurface* surface, AASpans* aaSpans) static void _apply(SwSurface* surface, AASpans* aaSpans)
{ {
auto end = surface->buf32 + surface->h * surface->stride; auto end = surface->buf32 + surface->h * surface->stride;
auto buf = surface->buf32 + surface->stride * aaSpans->yStart;
auto y = aaSpans->yStart; auto y = aaSpans->yStart;
uint32_t pixel; auto line = aaSpans->lines;
uint32_t pix;
uint32_t* dst; uint32_t* dst;
int32_t pos; int32_t pos;
//left side _calcAAEdge(aaSpans, 0); //left side
_calcAAEdge(aaSpans, 0); _calcAAEdge(aaSpans, 1); //right side
//right side
_calcAAEdge(aaSpans, 1);
while (y < aaSpans->yEnd) { while (y < aaSpans->yEnd) {
auto line = &aaSpans->lines[y - aaSpans->yStart]; if (line->x[1] - line->x[0] > 0) {
auto width = line->x[1] - line->x[0];
if (width > 0) {
auto offset = y * surface->stride;
//Left edge //Left edge
dst = surface->buf32 + (offset + line->x[0]); dst = buf + line->x[0];
if (line->x[0] > 1) pixel = *(dst - 1); pix = *(dst - ((line->x[0] > 1) ? 1 : 0));
else pixel = *dst;
pos = 1; pos = 1;
//exceptional handling. out of memory bound. //exceptional handling. out of memory bound.
@ -822,34 +683,31 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
} }
while (pos <= line->length[0]) { while (pos <= line->length[0]) {
*dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos); *dst = INTERPOLATE(*dst, pix, line->coverage[0] * pos);
++dst; ++dst;
++pos; ++pos;
} }
//Right edge //Right edge
dst = surface->buf32 + offset + line->x[1] - 1; dst = buf + line->x[1] - 1;
pix = *(dst + (line->x[1] < (int32_t)(surface->w - 1) ? 1 : 0));
if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
else pixel = *dst;
pos = line->length[1]; pos = line->length[1];
//exceptional handling. out of memory bound. //exceptional handling. out of memory bound.
if (dst - pos < surface->buf32) --pos; if (dst - pos < surface->buf32) --pos;
while (pos > 0) { while (pos > 0) {
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos)); *dst = INTERPOLATE(*dst, pix, 255 - (line->coverage[1] * pos));
--dst; --dst;
--pos; --pos;
} }
} }
y++; buf += surface->stride;
++line;
++y;
} }
tvg::free(aaSpans->lines); tvg::free(aaSpans->lines);
tvg::free(aaSpans); tvg::free(aaSpans);
return true;
} }
@ -863,23 +721,19 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
| / | | / |
3 -- 2 3 -- 2
*/ */
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity) bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const RenderRegion& bbox, uint8_t opacity)
{ {
if (surface->channelSize == sizeof(uint8_t)) { if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!"); TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
return false; return false;
} }
//Exceptions: No dedicated drawing area? //Prepare vertices. Shift XY coordinates to match the sub-pixeling technique.
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
/* Prepare vertices.
shift XY coordinates to match the sub-pixeling technique. */
Vertex vertices[4]; Vertex vertices[4];
vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}}; vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}}; vertices[1] = {{float(image.w), 0.0f}, {float(image.w), 0.0f}};
vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}}; vertices[2] = {{float(image.w), float(image.h)}, {float(image.w), float(image.h)}};
vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}}; vertices[3] = {{0.0f, float(image.h)}, {0.0f, float(image.h)}};
float ys = FLT_MAX, ye = -1.0f; float ys = FLT_MAX, ye = -1.0f;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
@ -888,8 +742,9 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y; if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
} }
auto aaSpans = _AASpans(ys, ye, image, region); auto yStart = std::max(static_cast<int>(ys), bbox.min.y);
if (!aaSpans) return true; auto yEnd = std::min(static_cast<int>(ye), bbox.max.y);
auto aaSpans = rightAngle(transform) ? nullptr : _AASpans(yStart, yEnd);
Polygon polygon; Polygon polygon;
@ -898,19 +753,20 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
polygon.vertex[1] = vertices[1]; polygon.vertex[1] = vertices[1];
polygon.vertex[2] = vertices[3]; polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); _rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
//Draw the second polygon //Draw the second polygon
polygon.vertex[0] = vertices[1]; polygon.vertex[0] = vertices[1];
polygon.vertex[1] = vertices[2]; polygon.vertex[1] = vertices[2];
polygon.vertex[2] = vertices[3]; polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity); _rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
#if 0 #if 0
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) { if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox); _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
} }
#endif #endif
return _apply(surface, aaSpans); if (aaSpans) _apply(surface, aaSpans);
return true;
} }

View file

@ -40,7 +40,7 @@ struct SwTask : Task
{ {
SwSurface* surface = nullptr; SwSurface* surface = nullptr;
SwMpool* mpool = nullptr; SwMpool* mpool = nullptr;
SwBBox bbox; //Rendering Region RenderRegion bbox; //Rendering Region
Matrix transform; Matrix transform;
Array<RenderData> clips; Array<RenderData> clips;
RenderUpdateFlag flags = RenderUpdateFlag::None; RenderUpdateFlag flags = RenderUpdateFlag::None;
@ -48,22 +48,11 @@ struct SwTask : Task
bool pushed = false; //Pushed into task list? bool pushed = false; //Pushed into task list?
bool disposed = false; //Disposed task? bool disposed = false; //Disposed task?
RenderRegion bounds() const RenderRegion& bounds()
{ {
//Can we skip the synchronization? //Can we skip the synchronization?
done(); done();
return bbox;
RenderRegion region;
//Range over?
region.x = bbox.min.x > 0 ? bbox.min.x : 0;
region.y = bbox.min.y > 0 ? bbox.min.y : 0;
region.w = bbox.max.x - region.x;
region.h = bbox.max.y - region.y;
if (region.w < 0) region.w = 0;
if (region.h < 0) region.h = 0;
return region;
} }
virtual void dispose() = 0; virtual void dispose() = 0;
@ -117,7 +106,7 @@ struct SwShapeTask : SwTask
} }
auto strokeWidth = validStrokeWidth(clipper); auto strokeWidth = validStrokeWidth(clipper);
SwBBox renderRegion{}; RenderRegion renderBox{};
auto updateShape = flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip); auto updateShape = flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform | RenderUpdateFlag::Clip);
auto updateFill = false; auto updateFill = false;
@ -126,11 +115,11 @@ struct SwShapeTask : SwTask
updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill); updateFill = (MULTIPLY(rshape->color.a, opacity) || rshape->fill);
if (updateShape) shapeReset(&shape); if (updateShape) shapeReset(&shape);
if (updateFill || clipper) { if (updateFill || clipper) {
if (shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) { if (shapePrepare(&shape, rshape, transform, bbox, renderBox, mpool, tid, clips.count > 0 ? true : false)) {
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err; if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
} else { } else {
updateFill = false; updateFill = false;
renderRegion.reset(); renderBox.reset();
} }
} }
} }
@ -146,7 +135,7 @@ struct SwShapeTask : SwTask
if (updateShape || flags & RenderUpdateFlag::Stroke) { if (updateShape || flags & RenderUpdateFlag::Stroke) {
if (strokeWidth > 0.0f) { if (strokeWidth > 0.0f) {
shapeResetStroke(&shape, rshape, transform); shapeResetStroke(&shape, rshape, transform);
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err; if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderBox, mpool, tid)) goto err;
if (auto fill = rshape->strokeFill()) { if (auto fill = rshape->strokeFill()) {
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false; auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
if (ctable) shapeResetStrokeFill(&shape); if (ctable) shapeResetStrokeFill(&shape);
@ -168,7 +157,7 @@ struct SwShapeTask : SwTask
if (!clipShapeRle && !clipStrokeRle) goto err; if (!clipShapeRle && !clipStrokeRle) goto err;
} }
bbox = renderRegion; //sync bbox = renderBox; //sync
return; return;
@ -199,7 +188,7 @@ struct SwImageTask : SwTask
void run(unsigned tid) override void run(unsigned tid) override
{ {
auto clipRegion = bbox; auto clipBox = bbox;
//Convert colorspace if it's not aligned. //Convert colorspace if it's not aligned.
rasterConvertCS(source, surface->cs); rasterConvertCS(source, surface->cs);
@ -216,7 +205,7 @@ struct SwImageTask : SwTask
imageReset(&image); imageReset(&image);
if (!image.data || image.w == 0 || image.h == 0) goto end; if (!image.data || image.w == 0 || image.h == 0) goto end;
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end; if (!imagePrepare(&image, transform, clipBox, bbox, mpool, tid)) goto end;
if (clips.count > 0) { if (clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end; if (!imageGenRle(&image, bbox, false)) goto end;
@ -394,12 +383,6 @@ bool SwRenderer::postRender()
rasterUnpremultiply(surface); rasterUnpremultiply(surface);
} }
ARRAY_FOREACH(p, tasks) {
if ((*p)->disposed) delete(*p);
else (*p)->pushed = false;
}
tasks.clear();
return true; return true;
} }
@ -411,7 +394,32 @@ bool SwRenderer::renderImage(RenderData data)
if (task->opacity == 0) return true; if (task->opacity == 0) return true;
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity); //Outside of the viewport, skip the rendering
auto& bbox = task->bbox;
if (bbox.invalid() || bbox.x() >= surface->w || bbox.y() >= surface->h) return true;
auto& image = task->image;
//RLE Image
if (image.rle) {
if (image.direct) return rasterDirectRleImage(surface, image, task->opacity);
else if (image.scaled) return rasterScaledRleImage(surface, image, task->transform, bbox, task->opacity);
else {
//create a intermediate buffer for rle clipping
auto cmp = request(sizeof(pixel_t), false);
cmp->compositor->method = MaskMethod::None;
cmp->compositor->valid = true;
cmp->compositor->image.rle = image.rle;
rasterClear(cmp, bbox.x(), bbox.y(), bbox.w(), bbox.h(), 0);
rasterTexmapPolygon(cmp, image, task->transform, bbox, 255);
return rasterDirectRleImage(surface, cmp->compositor->image, task->opacity);
}
//Whole Image
} else {
if (image.direct) return rasterDirectImage(surface, image, bbox, task->opacity);
else if (image.scaled) return rasterScaledImage(surface, image, task->transform, bbox, task->opacity);
else return rasterTexmapPolygon(surface, image, task->transform, bbox, task->opacity);
}
} }
@ -570,38 +578,19 @@ SwSurface* SwRenderer::request(int channelSize, bool square)
RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags)
{ {
auto x = region.x; auto bbox = RenderRegion::intersect(region, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}});
auto y = region.y; if (bbox.invalid()) return nullptr;
auto w = region.w;
auto h = region.h;
auto sw = static_cast<int32_t>(surface->w);
auto sh = static_cast<int32_t>(surface->h);
//Out of boundary
if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing)); auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing));
//Boundary Check
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + w > sw) w = (sw - x);
if (y + h > sh) h = (sh - y);
if (w == 0 || h == 0) return nullptr;
cmp->compositor->recoverSfc = surface; cmp->compositor->recoverSfc = surface;
cmp->compositor->recoverCmp = surface->compositor; cmp->compositor->recoverCmp = surface->compositor;
cmp->compositor->valid = false; cmp->compositor->valid = false;
cmp->compositor->bbox.min.x = x; cmp->compositor->bbox = bbox;
cmp->compositor->bbox.min.y = y;
cmp->compositor->bbox.max.x = x + w;
cmp->compositor->bbox.max.y = y + h;
/* TODO: Currently, only blending might work. /* TODO: Currently, only blending might work.
Blending and composition must be handled together. */ Blending and composition must be handled together. */
auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000; auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000;
rasterClear(cmp, x, y, w, h, color); rasterClear(cmp, bbox.x(), bbox.y(), bbox.w(), bbox.h(), color);
//Switch render target //Switch render target
surface = cmp; surface = cmp;
@ -626,8 +615,7 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
//Default is alpha blending //Default is alpha blending
if (p->method == MaskMethod::None) { if (p->method == MaskMethod::None) {
auto m = tvg::identity(); return rasterDirectImage(surface, p->image, p->bbox, p->opacity);
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
} }
return true; return true;
@ -724,6 +712,19 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
if (flags == RenderUpdateFlag::None) return task; if (flags == RenderUpdateFlag::None) return task;
if ((transform.e11 == 0.0f && transform.e12 == 0.0f) || (transform.e21 == 0.0f && transform.e22 == 0.0f)) return task; //zero size? if ((transform.e11 == 0.0f && transform.e12 == 0.0f) || (transform.e21 == 0.0f && transform.e22 == 0.0f)) return task; //zero size?
task->surface = surface;
task->mpool = mpool;
task->bbox = RenderRegion::intersect(vport, {{0, 0}, {int32_t(surface->w), int32_t(surface->h)}});
task->transform = transform;
task->clips = clips;
task->opacity = opacity;
task->flags = flags;
if (!task->pushed) {
task->pushed = true;
tasks.push(task);
}
//TODO: Failed threading them. It would be better if it's possible. //TODO: Failed threading them. It would be better if it's possible.
//See: https://github.com/thorvg/thorvg/issues/1409 //See: https://github.com/thorvg/thorvg/issues/1409
//Guarantee composition targets get ready. //Guarantee composition targets get ready.
@ -731,22 +732,6 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
static_cast<SwTask*>(*p)->done(); static_cast<SwTask*>(*p)->done();
} }
task->clips = clips;
task->transform = transform;
task->opacity = opacity;
task->surface = surface;
task->mpool = mpool;
task->flags = flags;
task->bbox.min.x = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
task->bbox.min.y = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
task->bbox.max.x = std::min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
task->bbox.max.y = std::min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
if (!task->pushed) {
task->pushed = true;
tasks.push(task);
}
TaskScheduler::request(task); TaskScheduler::request(task);
return task; return task;

View file

@ -56,6 +56,8 @@ public:
bool sync() override; bool sync() override;
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs); bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
SwSurface* request(int channelSize, bool square);
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override; RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override; bool beginComposite(RenderCompositor* cmp, MaskMethod method, uint8_t opacity) override;
bool endComposite(RenderCompositor* cmp) override; bool endComposite(RenderCompositor* cmp) override;
@ -80,7 +82,6 @@ private:
SwRenderer(); SwRenderer();
~SwRenderer(); ~SwRenderer();
SwSurface* request(int channelSize, bool square);
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags); RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
}; };

View file

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

View file

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

View file

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

View file

@ -51,7 +51,7 @@ Result GlCanvas::target(void* context, int32_t id, uint32_t w, uint32_t h, Color
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
if (!renderer->target(context, id, w, h)) return Result::Unknown; if (!renderer->target(context, id, w, h)) return Result::Unknown;
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
renderer->viewport(pImpl->vport); renderer->viewport(pImpl->vport);
//Paints must be updated again with this new target. //Paints must be updated again with this new target.

View file

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

View file

@ -54,7 +54,7 @@ namespace tvg
Paint* paint = nullptr; Paint* paint = nullptr;
Paint* parent = nullptr; Paint* parent = nullptr;
Mask* maskData = nullptr; Mask* maskData = nullptr;
Paint* clipper = nullptr; Shape* clipper = nullptr;
RenderMethod* renderer = nullptr; RenderMethod* renderer = nullptr;
RenderData rd = nullptr; RenderData rd = nullptr;
@ -80,9 +80,9 @@ namespace tvg
} tr; } tr;
RenderUpdateFlag renderFlag = RenderUpdateFlag::None; RenderUpdateFlag renderFlag = RenderUpdateFlag::None;
BlendMethod blendMethod; BlendMethod blendMethod;
uint16_t refCnt = 0; //reference count
uint8_t ctxFlag; uint8_t ctxFlag;
uint8_t opacity; uint8_t opacity;
uint8_t refCnt = 0; //reference count
Impl(Paint* pnt) : paint(pnt) Impl(Paint* pnt) : paint(pnt)
{ {
@ -107,9 +107,7 @@ namespace tvg
uint8_t ref() uint8_t ref()
{ {
if (refCnt == UINT8_MAX) TVGERR("RENDERER", "Reference Count Overflow!"); return ++refCnt;
else ++refCnt;
return refCnt;
} }
uint8_t unref(bool free = true) uint8_t unref(bool free = true)
@ -163,7 +161,7 @@ namespace tvg
return tm; return tm;
} }
Result clip(Paint* clp) Result clip(Shape* clp)
{ {
if (clp && PAINT(clp)->parent) return Result::InsufficientCondition; if (clp && PAINT(clp)->parent) return Result::InsufficientCondition;
if (clipper) PAINT(clipper)->unref(clipper != clp); if (clipper) PAINT(clipper)->unref(clipper != clp);

View file

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

View file

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

View file

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

View file

@ -64,10 +64,11 @@ struct SceneImpl : Scene
{ {
Paint::Impl impl; Paint::Impl impl;
list<Paint*> paints; //children list list<Paint*> paints; //children list
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX}; RenderRegion vport = {};
Array<RenderEffect*>* effects = nullptr; Array<RenderEffect*>* effects = nullptr;
uint8_t compFlag = CompositionFlag::Invalid; uint8_t compFlag = CompositionFlag::Invalid;
uint8_t opacity; //for composition uint8_t opacity; //for composition
bool vdirty = false;
SceneImpl() : impl(Paint::Impl(this)) SceneImpl() : impl(Paint::Impl(this))
{ {
@ -105,7 +106,7 @@ struct SceneImpl : Scene
RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper) RenderData update(RenderMethod* renderer, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, TVG_UNUSED bool clipper)
{ {
this->vport = renderer->viewport(); if (paints.empty()) return nullptr;
if (needComposition(opacity)) { if (needComposition(opacity)) {
/* Overriding opacity value. If this scene is half-translucent, /* Overriding opacity value. If this scene is half-translucent,
@ -123,11 +124,17 @@ struct SceneImpl : Scene
} }
} }
//this viewport update is more performant than in bounds()?
vport = renderer->viewport();
vdirty = true;
return nullptr; return nullptr;
} }
bool render(RenderMethod* renderer) bool render(RenderMethod* renderer)
{ {
if (paints.empty()) return true;
RenderCompositor* cmp = nullptr; RenderCompositor* cmp = nullptr;
auto ret = true; auto ret = true;
@ -157,42 +164,38 @@ struct SceneImpl : Scene
return ret; return ret;
} }
RenderRegion bounds(RenderMethod* renderer) const RenderRegion bounds(RenderMethod* renderer)
{ {
if (paints.empty()) return {0, 0, 0, 0}; if (paints.empty()) return {};
if (!vdirty) return vport;
int32_t x1 = INT32_MAX; vdirty = false;
int32_t y1 = INT32_MAX;
int32_t x2 = 0;
int32_t y2 = 0;
for (auto paint : paints) {
auto region = paint->pImpl->bounds(renderer);
//Merge regions //Merge regions
if (region.x < x1) x1 = region.x; RenderRegion pRegion = {{INT32_MAX, INT32_MAX}, {0, 0}};
if (x2 < region.x + region.w) x2 = (region.x + region.w); for (auto paint : paints) {
if (region.y < y1) y1 = region.y; auto region = paint->pImpl->bounds(renderer);
if (y2 < region.y + region.h) y2 = (region.y + region.h); if (region.min.x < pRegion.min.x) pRegion.min.x = region.min.x;
if (pRegion.max.x < region.max.x) pRegion.max.x = region.max.x;
if (region.min.y < pRegion.min.y) pRegion.min.y = region.min.y;
if (pRegion.max.y < region.max.y) pRegion.max.y = region.max.y;
} }
//Extends the render region if post effects require //Extends the render region if post effects require
int32_t ex = 0, ey = 0, ew = 0, eh = 0; RenderRegion eRegion{};
if (effects) { if (effects) {
ARRAY_FOREACH(p, *effects) { ARRAY_FOREACH(p, *effects) {
auto effect = *p; auto effect = *p;
if (effect->valid && renderer->region(effect)) { if (effect->valid && renderer->region(effect)) eRegion.add(effect->extend);
ex = std::min(ex, effect->extend.x);
ey = std::min(ey, effect->extend.y);
ew = std::max(ew, effect->extend.w);
eh = std::max(eh, effect->extend.h);
}
} }
} }
auto ret = RenderRegion{x1 + ex, y1 + ey, (x2 - x1) + ew, (y2 - y1) + eh}; pRegion.min.x += eRegion.min.x;
ret.intersect(this->vport); pRegion.min.y += eRegion.min.y;
return ret; pRegion.max.x += eRegion.max.x;
pRegion.max.y += eRegion.max.y;
vport = RenderRegion::intersect(vport, pRegion);
return vport;
} }
Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking) Result bounds(Point* pt4, Matrix& m, bool obb, bool stroking)

View file

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

View file

@ -54,7 +54,7 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
if (!renderer) return Result::MemoryCorruption; if (!renderer) return Result::MemoryCorruption;
if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments; if (!renderer->target(buffer, stride, w, h, cs)) return Result::InvalidArguments;
pImpl->vport = {0, 0, (int32_t)w, (int32_t)h}; pImpl->vport = {{0, 0}, {(int32_t)w, (int32_t)h}};
renderer->viewport(pImpl->vport); renderer->viewport(pImpl->vport);
//FIXME: The value must be associated with an individual canvas instance. //FIXME: The value must be associated with an individual canvas instance.

View file

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

View file

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

View file

@ -103,9 +103,15 @@ WGPUBindGroup WgBindGroupLayouts::createBindGroupStrorage3RO(WGPUTextureView tex
WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer1Un(WGPUBuffer buff) WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer1Un(WGPUBuffer buff)
{
return createBindGroupBuffer1Un(buff, 0, wgpuBufferGetSize(buff));
}
WGPUBindGroup WgBindGroupLayouts::createBindGroupBuffer1Un(WGPUBuffer buff, uint64_t offset, uint64_t size)
{ {
const WGPUBindGroupEntry bindGroupEntrys[] = { const WGPUBindGroupEntry bindGroupEntrys[] = {
{ .binding = 0, .buffer = buff, .size = wgpuBufferGetSize(buff) } { .binding = 0, .buffer = buff, .offset = offset, .size = size }
}; };
const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutBuffer1Un, .entryCount = 1, .entries = bindGroupEntrys }; const WGPUBindGroupDescriptor bindGroupDesc { .layout = layoutBuffer1Un, .entryCount = 1, .entries = bindGroupEntrys };
return wgpuDeviceCreateBindGroup(device, &bindGroupDesc); return wgpuDeviceCreateBindGroup(device, &bindGroupDesc);

View file

@ -49,6 +49,7 @@ public:
WGPUBindGroup createBindGroupStrorage2RO(WGPUTextureView texView0, WGPUTextureView texView1); WGPUBindGroup createBindGroupStrorage2RO(WGPUTextureView texView0, WGPUTextureView texView1);
WGPUBindGroup createBindGroupStrorage3RO(WGPUTextureView texView0, WGPUTextureView texView1, WGPUTextureView texView2); WGPUBindGroup createBindGroupStrorage3RO(WGPUTextureView texView0, WGPUTextureView texView1, WGPUTextureView texView2);
WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff); WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff);
WGPUBindGroup createBindGroupBuffer1Un(WGPUBuffer buff, uint64_t offset, uint64_t size);
WGPUBindGroup createBindGroupBuffer2Un(WGPUBuffer buff0, WGPUBuffer buff1); WGPUBindGroup createBindGroupBuffer2Un(WGPUBuffer buff0, WGPUBuffer buff1);
WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2); WGPUBindGroup createBindGroupBuffer3Un(WGPUBuffer buff0, WGPUBuffer buff1, WGPUBuffer buff2);
void releaseBindGroup(WGPUBindGroup& bindGroup); void releaseBindGroup(WGPUBindGroup& bindGroup);

View file

@ -200,10 +200,7 @@ bool WgContext::allocateBufferVertex(WGPUBuffer& buffer, const float* data, uint
wgpuQueueWriteBuffer(queue, buffer, 0, data, size); wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
else { else {
releaseBuffer(buffer); releaseBuffer(buffer);
const WGPUBufferDescriptor bufferDesc { const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex, .size = size };
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Vertex,
.size = size > WG_VERTEX_BUFFER_MIN_SIZE ? size : WG_VERTEX_BUFFER_MIN_SIZE
};
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc); buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
wgpuQueueWriteBuffer(queue, buffer, 0, data, size); wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
return true; return true;
@ -218,10 +215,7 @@ bool WgContext::allocateBufferIndex(WGPUBuffer& buffer, const uint32_t* data, ui
wgpuQueueWriteBuffer(queue, buffer, 0, data, size); wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
else { else {
releaseBuffer(buffer); releaseBuffer(buffer);
const WGPUBufferDescriptor bufferDesc { const WGPUBufferDescriptor bufferDesc { .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, .size = size };
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
.size = size > WG_INDEX_BUFFER_MIN_SIZE ? size : WG_INDEX_BUFFER_MIN_SIZE
};
buffer = wgpuDeviceCreateBuffer(device, &bufferDesc); buffer = wgpuDeviceCreateBuffer(device, &bufferDesc);
wgpuQueueWriteBuffer(queue, buffer, 0, data, size); wgpuQueueWriteBuffer(queue, buffer, 0, data, size);
return true; return true;
@ -241,10 +235,7 @@ bool WgContext::allocateBufferIndexFan(uint64_t vertexCount)
indexes.push(i + 2); indexes.push(i + 2);
} }
releaseBuffer(bufferIndexFan); releaseBuffer(bufferIndexFan);
WGPUBufferDescriptor bufferDesc{ WGPUBufferDescriptor bufferDesc{ .usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index, .size = indexCount * sizeof(uint32_t) };
.usage = WGPUBufferUsage_CopyDst | WGPUBufferUsage_Index,
.size = indexCount * sizeof(uint32_t)
};
bufferIndexFan = wgpuDeviceCreateBuffer(device, &bufferDesc); bufferIndexFan = wgpuDeviceCreateBuffer(device, &bufferDesc);
wgpuQueueWriteBuffer(queue, bufferIndexFan, 0, &indexes[0], indexCount * sizeof(uint32_t)); wgpuQueueWriteBuffer(queue, bufferIndexFan, 0, &indexes[0], indexCount * sizeof(uint32_t));
return true; return true;
@ -269,3 +260,34 @@ void WgContext::releaseQueue(WGPUQueue& queue)
queue = nullptr; queue = nullptr;
} }
} }
WGPUCommandEncoder WgContext::createCommandEncoder()
{
WGPUCommandEncoderDescriptor commandEncoderDesc{};
return wgpuDeviceCreateCommandEncoder(device, &commandEncoderDesc);
}
void WgContext::submitCommandEncoder(WGPUCommandEncoder commandEncoder)
{
const WGPUCommandBufferDescriptor commandBufferDesc{};
WGPUCommandBuffer commandsBuffer = wgpuCommandEncoderFinish(commandEncoder, &commandBufferDesc);
wgpuQueueSubmit(queue, 1, &commandsBuffer);
wgpuCommandBufferRelease(commandsBuffer);
}
void WgContext::releaseCommandEncoder(WGPUCommandEncoder& commandEncoder)
{
if (commandEncoder) {
wgpuCommandEncoderRelease(commandEncoder);
commandEncoder = nullptr;
}
}
bool WgContext::invalid()
{
return !instance || !device;
}

View file

@ -25,9 +25,6 @@
#include "tvgWgBindGroups.h" #include "tvgWgBindGroups.h"
#define WG_VERTEX_BUFFER_MIN_SIZE 2048
#define WG_INDEX_BUFFER_MIN_SIZE 2048
struct WgContext { struct WgContext {
// external webgpu handles // external webgpu handles
WGPUInstance instance{}; WGPUInstance instance{};
@ -71,10 +68,12 @@ struct WgContext {
// release buffer objects // release buffer objects
void releaseBuffer(WGPUBuffer& buffer); void releaseBuffer(WGPUBuffer& buffer);
bool invalid() // command encoder
{ WGPUCommandEncoder createCommandEncoder();
return !instance || !device; void submitCommandEncoder(WGPUCommandEncoder encoder);
} void releaseCommandEncoder(WGPUCommandEncoder& encoder);
bool invalid();
}; };
#endif // _TVG_WG_COMMON_H_ #endif // _TVG_WG_COMMON_H_

File diff suppressed because it is too large Load diff

View file

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

View file

@ -167,17 +167,17 @@ void WgPipelines::initialize(WgContext& context)
const WgBindGroupLayouts& layouts = context.layouts; const WgBindGroupLayouts& layouts = context.layouts;
// bind group layouts helpers // bind group layouts helpers
const WGPUBindGroupLayout bindGroupLayoutsStencil[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un }; const WGPUBindGroupLayout bindGroupLayoutsStencil[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un };
const WGPUBindGroupLayout bindGroupLayoutsDepth[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un }; const WGPUBindGroupLayout bindGroupLayoutsDepth[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutBuffer1Un };
// bind group layouts normal blend // bind group layouts normal blend
const WGPUBindGroupLayout bindGroupLayoutsSolid[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un }; const WGPUBindGroupLayout bindGroupLayoutsSolid[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un };
const WGPUBindGroupLayout bindGroupLayoutsGradient[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampledBuff2Un }; const WGPUBindGroupLayout bindGroupLayoutsGradient[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
const WGPUBindGroupLayout bindGroupLayoutsImage[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsImage[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
const WGPUBindGroupLayout bindGroupLayoutsScene[] { layouts.layoutTexSampled, layouts.layoutBuffer1Un }; const WGPUBindGroupLayout bindGroupLayoutsScene[] { layouts.layoutTexSampled, layouts.layoutBuffer1Un };
// bind group layouts custom blend // bind group layouts custom blend
const WGPUBindGroupLayout bindGroupLayoutsSolidBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsSolidBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled };
const WGPUBindGroupLayout bindGroupLayoutsGradientBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampledBuff2Un, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsGradientBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled, layouts.layoutTexSampled };
const WGPUBindGroupLayout bindGroupLayoutsImageBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer2Un, layouts.layoutTexSampled, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsImageBlend[] { layouts.layoutBuffer1Un, layouts.layoutBuffer1Un, layouts.layoutTexSampled, layouts.layoutTexSampled };
const WGPUBindGroupLayout bindGroupLayoutsSceneBlend[] { layouts.layoutTexSampled, layouts.layoutTexSampled, layouts.layoutBuffer1Un }; const WGPUBindGroupLayout bindGroupLayoutsSceneBlend[] { layouts.layoutTexSampled, layouts.layoutTexSampled, layouts.layoutBuffer1Un };
// bind group layouts scene compose // bind group layouts scene compose
const WGPUBindGroupLayout bindGroupLayoutsSceneCompose[] { layouts.layoutTexSampled, layouts.layoutTexSampled }; const WGPUBindGroupLayout bindGroupLayoutsSceneCompose[] { layouts.layoutTexSampled, layouts.layoutTexSampled };
@ -229,7 +229,7 @@ void WgPipelines::initialize(WgContext& context)
layout_stencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2); layout_stencil = createPipelineLayout(context.device, bindGroupLayoutsStencil, 2);
layout_depth = createPipelineLayout(context.device, bindGroupLayoutsDepth, 3); layout_depth = createPipelineLayout(context.device, bindGroupLayoutsDepth, 3);
// layouts normal blend // layouts normal blend
layout_solid = createPipelineLayout(context.device, bindGroupLayoutsSolid, 3); layout_solid = createPipelineLayout(context.device, bindGroupLayoutsSolid, 2);
layout_gradient = createPipelineLayout(context.device, bindGroupLayoutsGradient, 3); layout_gradient = createPipelineLayout(context.device, bindGroupLayoutsGradient, 3);
layout_image = createPipelineLayout(context.device, bindGroupLayoutsImage, 3); layout_image = createPipelineLayout(context.device, bindGroupLayoutsImage, 3);
layout_scene = createPipelineLayout(context.device, bindGroupLayoutsScene, 2); layout_scene = createPipelineLayout(context.device, bindGroupLayoutsScene, 2);

View file

@ -30,163 +30,115 @@
// WgMeshData // WgMeshData
//*********************************************************************** //***********************************************************************
void WgMeshData::draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder) void WgMeshData::update(const WgVertexBuffer& vertexBuffer)
{
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); assert(vertexBuffer.count > 2);
vertexCount = vertexBuffer.count; // setup vertex data
indexCount = (vertexBuffer.count - 2) * 3; vbuffer.reserve(vertexBuffer.count);
context.allocateBufferVertex(bufferPosition, (float*)vertexBuffer.data, vertexCount * sizeof(float) * 2); vbuffer.count = vertexBuffer.count;
context.allocateBufferIndexFan(vertexCount); memcpy(vbuffer.data, vertexBuffer.data, sizeof(vertexBuffer.data[0])*vertexBuffer.count);
// setup tex coords data
tbuffer.clear();
} }
void WgMeshData::update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd) void WgMeshData::update(const WgIndexedVertexBuffer& vertexBufferInd)
{ {
assert(vertexBufferInd.vcount > 2); assert(vertexBufferInd.vcount > 2);
vertexCount = vertexBufferInd.vcount; // setup vertex data
indexCount = vertexBufferInd.icount; vbuffer.reserve(vertexBufferInd.vcount);
if (vertexCount > 0) context.allocateBufferVertex(bufferPosition, (float*)vertexBufferInd.vbuff, vertexCount * sizeof(float) * 2); vbuffer.count = vertexBufferInd.vcount;
if (indexCount > 0) context.allocateBufferIndex(bufferIndex, vertexBufferInd.ibuff, indexCount * sizeof(uint32_t)); memcpy(vbuffer.data, vertexBufferInd.vbuff, sizeof(vertexBufferInd.vbuff[0])*vertexBufferInd.vcount);
// setup tex coords data
tbuffer.clear();
// copy index data
ibuffer.reserve(vertexBufferInd.icount);
ibuffer.count = vertexBufferInd.icount;
memcpy(ibuffer.data, vertexBufferInd.ibuff, sizeof(vertexBufferInd.ibuff[0])*vertexBufferInd.icount);
}; };
void WgMeshData::bbox(WgContext& context, const Point pmin, const Point pmax) void WgMeshData::bbox(const Point pmin, const Point pmax)
{ {
vertexCount = 4;
indexCount = 6;
const float data[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y}; const float data[] = {pmin.x, pmin.y, pmax.x, pmin.y, pmax.x, pmax.y, pmin.x, pmax.y};
context.allocateBufferVertex(bufferPosition, data, sizeof(data)); // setup vertex data
context.allocateBufferIndexFan(vertexCount); vbuffer.reserve(4);
vbuffer.count = 4;
memcpy(vbuffer.data, data, sizeof(data));
// setup tex coords data
tbuffer.clear();
} }
void WgMeshData::imageBox(WgContext& context, float w, float h) void WgMeshData::imageBox(float w, float h)
{ {
vertexCount = 4;
indexCount = 6;
const float vdata[] = {0.0f, 0.0f, w, 0.0f, w, h, 0.0f, h}; const float vdata[] = {0.0f, 0.0f, w, 0.0f, w, h, 0.0f, h};
const float tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f}; const float tdata[] = {0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
const uint32_t idata[] = {0, 1, 2, 0, 2, 3}; const uint32_t idata[] = {0, 1, 2, 0, 2, 3};
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata)); // setup vertex data
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata)); vbuffer.reserve(4);
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata)); vbuffer.count = 4;
memcpy(vbuffer.data, vdata, sizeof(vdata));
// setup tex coords data
tbuffer.reserve(4);
tbuffer.count = 4;
memcpy(tbuffer.data, tdata, sizeof(tdata));
// setup indexes data
ibuffer.reserve(6);
ibuffer.count = 6;
memcpy(ibuffer.data, idata, sizeof(idata));
} }
void WgMeshData::blitBox(WgContext& context) void WgMeshData::blitBox()
{ {
vertexCount = 4;
indexCount = 6;
const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f}; const float vdata[] = {-1.0f, +1.0f, +1.0f, +1.0f, +1.0f, -1.0f, -1.0f, -1.0f};
const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f}; const float tdata[] = {+0.0f, +0.0f, +1.0f, +0.0f, +1.0f, +1.0f, +0.0f, +1.0f};
const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 }; const uint32_t idata[] = { 0, 1, 2, 0, 2, 3 };
context.allocateBufferVertex(bufferPosition, vdata, sizeof(vdata)); // setup vertex data
context.allocateBufferVertex(bufferTexCoord, tdata, sizeof(tdata)); vbuffer.reserve(4);
context.allocateBufferIndex(bufferIndex, idata, sizeof(idata)); vbuffer.count = 4;
memcpy(vbuffer.data, vdata, sizeof(vdata));
// setup tex coords data
tbuffer.reserve(4);
tbuffer.count = 4;
memcpy(tbuffer.data, tdata, sizeof(tdata));
// setup indexes data
ibuffer.reserve(6);
ibuffer.count = 6;
memcpy(ibuffer.data, idata, sizeof(idata));
} }
void WgMeshData::release(WgContext& context)
{
context.releaseBuffer(bufferIndex);
context.releaseBuffer(bufferTexCoord);
context.releaseBuffer(bufferPosition);
};
//***********************************************************************
// WgMeshDataPool
//***********************************************************************
WgMeshData* WgMeshDataPool::allocate(WgContext& context)
{
WgMeshData* meshData{};
if (mPool.count > 0) {
meshData = mPool.last();
mPool.pop();
} else {
meshData = new WgMeshData();
mList.push(meshData);
}
return meshData;
}
void WgMeshDataPool::free(WgContext& context, WgMeshData* meshData)
{
mPool.push(meshData);
}
void WgMeshDataPool::release(WgContext& context)
{
ARRAY_FOREACH(p, mList) {
(*p)->release(context);
delete(*p);
}
mPool.clear();
mList.clear();
}
WgMeshDataPool gMeshDataPoolInstance;
WgMeshDataPool* WgMeshDataPool::gMeshDataPool = &gMeshDataPoolInstance;
//*********************************************************************** //***********************************************************************
// WgMeshDataGroup // WgMeshDataGroup
//*********************************************************************** //***********************************************************************
void WgMeshDataGroup::append(WgContext& context, const WgVertexBuffer& vertexBuffer) void WgMeshDataGroup::append(const WgVertexBuffer& vertexBuffer)
{ {
assert(vertexBuffer.count >= 3); assert(vertexBuffer.count >= 3);
meshes.push(WgMeshDataPool::gMeshDataPool->allocate(context)); meshes.push(new WgMeshData());
meshes.last()->update(context, vertexBuffer); meshes.last()->update(vertexBuffer);
} }
void WgMeshDataGroup::append(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd) void WgMeshDataGroup::append(const WgIndexedVertexBuffer& vertexBufferInd)
{ {
assert(vertexBufferInd.vcount >= 3); assert(vertexBufferInd.vcount >= 3);
meshes.push(WgMeshDataPool::gMeshDataPool->allocate(context)); meshes.push(new WgMeshData());
meshes.last()->update(context, vertexBufferInd); meshes.last()->update(vertexBufferInd);
} }
void WgMeshDataGroup::append(WgContext& context, const Point pmin, const Point pmax) void WgMeshDataGroup::append(const Point pmin, const Point pmax)
{ {
meshes.push(WgMeshDataPool::gMeshDataPool->allocate(context)); meshes.push(new WgMeshData());
meshes.last()->bbox(context, pmin, pmax); meshes.last()->bbox(pmin, pmax);
} }
void WgMeshDataGroup::release(WgContext& context) void WgMeshDataGroup::release()
{ {
ARRAY_FOREACH(p, meshes) ARRAY_FOREACH(p, meshes) delete *p;
WgMeshDataPool::gMeshDataPool->free(context, *p);
meshes.clear(); meshes.clear();
}; };
@ -209,12 +161,38 @@ void WgImageData::update(WgContext& context, const RenderSurface* surface)
if (texHandleChanged) { if (texHandleChanged) {
context.releaseTextureView(textureView); context.releaseTextureView(textureView);
textureView = context.createTextureView(texture); textureView = context.createTextureView(texture);
// update bind group
context.layouts.releaseBindGroup(bindGroup);
bindGroup = context.layouts.createBindGroupTexSampled(context.samplerLinearRepeat, textureView);
}
};
void WgImageData::update(WgContext& context, const Fill* fill)
{
// compute gradient data
WgShaderTypeGradientData gradientData;
gradientData.update(fill);
// allocate new texture handle
bool texHandleChanged = context.allocateTexture(texture, WG_TEXTURE_GRADIENT_SIZE, 1, WGPUTextureFormat_RGBA8Unorm, gradientData.data);
// update texture view of texture handle was changed
if (texHandleChanged) {
context.releaseTextureView(textureView);
textureView = context.createTextureView(texture);
// get sampler by spread type
WGPUSampler sampler = context.samplerLinearClamp;
if (fill->spread() == FillSpread::Reflect) sampler = context.samplerLinearMirror;
if (fill->spread() == FillSpread::Repeat) sampler = context.samplerLinearRepeat;
// update bind group
context.layouts.releaseBindGroup(bindGroup);
bindGroup = context.layouts.createBindGroupTexSampled(sampler, textureView);
} }
}; };
void WgImageData::release(WgContext& context) void WgImageData::release(WgContext& context)
{ {
context.layouts.releaseBindGroup(bindGroup);
context.releaseTextureView(textureView); context.releaseTextureView(textureView);
context.releaseTexture(texture); context.releaseTexture(texture);
}; };
@ -223,52 +201,31 @@ void WgImageData::release(WgContext& context)
// WgRenderSettings // WgRenderSettings
//*********************************************************************** //***********************************************************************
void WgRenderSettings::updateFill(WgContext& context, const Fill* fill) void WgRenderSettings::update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity)
{ {
rasterType = WgRenderRasterType::Gradient; settings.transform.update(transform);
// get gradient transfrom matrix settings.options.update(cs, opacity);
Matrix invFillTransform; }
WgShaderTypeMat4x4f gradientTrans; // identity by default
if (inverse(&fill->transform(), &invFillTransform)) void WgRenderSettings::update(WgContext& context, const Fill* fill)
gradientTrans.update(invFillTransform); {
assert(fill);
settings.gradient.update(fill);
gradientData.update(context, fill);
// get gradient rasterisation settings // get gradient rasterisation settings
WgShaderTypeGradient gradient; rasterType = WgRenderRasterType::Gradient;
if (fill->type() == Type::LinearGradient) { if (fill->type() == Type::LinearGradient)
gradient.update((LinearGradient*)fill);
fillType = WgRenderSettingsType::Linear; fillType = WgRenderSettingsType::Linear;
} else if (fill->type() == Type::RadialGradient) { else if (fill->type() == Type::RadialGradient)
gradient.update((RadialGradient*)fill);
fillType = WgRenderSettingsType::Radial; fillType = WgRenderSettingsType::Radial;
}
// update gpu assets
bool bufferGradientSettingsChanged = context.allocateBufferUniform(bufferGroupGradient, &gradient.settings, sizeof(gradient.settings));
bool bufferGradientTransformChanged = context.allocateBufferUniform(bufferGroupTransfromGrad, &gradientTrans.mat, sizeof(gradientTrans.mat));
bool textureGradientChanged = context.allocateTexture(texGradient, WG_TEXTURE_GRADIENT_SIZE, 1, WGPUTextureFormat_RGBA8Unorm, gradient.texData);
if (bufferGradientSettingsChanged || textureGradientChanged || bufferGradientTransformChanged) {
// update texture view
context.releaseTextureView(texViewGradient);
texViewGradient = context.createTextureView(texGradient);
// get sampler by spread type
WGPUSampler sampler = context.samplerLinearClamp;
if (fill->spread() == FillSpread::Reflect) sampler = context.samplerLinearMirror;
if (fill->spread() == FillSpread::Repeat) sampler = context.samplerLinearRepeat;
// update bind group
context.layouts.releaseBindGroup(bindGroupGradient);
bindGroupGradient = context.layouts.createBindGroupTexSampledBuff2Un(
sampler, texViewGradient, bufferGroupGradient, bufferGroupTransfromGrad);
}
skip = false; skip = false;
}; };
void WgRenderSettings::updateColor(WgContext& context, const RenderColor& c) void WgRenderSettings::update(WgContext& context, const RenderColor& c)
{ {
settings.color.update(c);
rasterType = WgRenderRasterType::Solid; rasterType = WgRenderRasterType::Solid;
WgShaderTypeVec4f solidColor(c);
if (context.allocateBufferUniform(bufferGroupSolid, &solidColor, sizeof(solidColor))) {
context.layouts.releaseBindGroup(bindGroupSolid);
bindGroupSolid = context.layouts.createBindGroupBuffer1Un(bufferGroupSolid);
}
fillType = WgRenderSettingsType::Solid; fillType = WgRenderSettingsType::Solid;
skip = (c.a == 0); skip = (c.a == 0);
}; };
@ -276,13 +233,7 @@ void WgRenderSettings::updateColor(WgContext& context, const RenderColor& c)
void WgRenderSettings::release(WgContext& context) void WgRenderSettings::release(WgContext& context)
{ {
context.layouts.releaseBindGroup(bindGroupSolid); gradientData.release(context);
context.layouts.releaseBindGroup(bindGroupGradient);
context.releaseBuffer(bufferGroupSolid);
context.releaseBuffer(bufferGroupGradient);
context.releaseBuffer(bufferGroupTransfromGrad);
context.releaseTexture(texGradient);
context.releaseTextureView(texViewGradient);
}; };
//*********************************************************************** //***********************************************************************
@ -291,26 +242,10 @@ void WgRenderSettings::release(WgContext& context)
void WgRenderDataPaint::release(WgContext& context) void WgRenderDataPaint::release(WgContext& context)
{ {
context.layouts.releaseBindGroup(bindGroupPaint);
context.releaseBuffer(bufferModelMat);
context.releaseBuffer(bufferBlendSettings);
clips.clear(); clips.clear();
}; };
void WgRenderDataPaint::update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity)
{
WgShaderTypeMat4x4f modelMat(transform);
WgShaderTypeVec4f blendSettings(cs, opacity);
bool bufferModelMatChanged = context.allocateBufferUniform(bufferModelMat, &modelMat, sizeof(modelMat));
bool bufferBlendSettingsChanged = context.allocateBufferUniform(bufferBlendSettings, &blendSettings, sizeof(blendSettings));
if (bufferModelMatChanged || bufferBlendSettingsChanged) {
context.layouts.releaseBindGroup(bindGroupPaint);
bindGroupPaint = context.layouts.createBindGroupBuffer2Un(bufferModelMat, bufferBlendSettings);
}
}
void WgRenderDataPaint::updateClips(tvg::Array<tvg::RenderData> &clips) { void WgRenderDataPaint::updateClips(tvg::Array<tvg::RenderData> &clips) {
this->clips.clear(); this->clips.clear();
ARRAY_FOREACH(p, clips) { ARRAY_FOREACH(p, clips) {
@ -322,24 +257,24 @@ void WgRenderDataPaint::updateClips(tvg::Array<tvg::RenderData> &clips) {
// WgRenderDataShape // WgRenderDataShape
//*********************************************************************** //***********************************************************************
void WgRenderDataShape::appendShape(WgContext& context, const WgVertexBuffer& vertexBuffer) void WgRenderDataShape::appendShape(const WgVertexBuffer& vertexBuffer)
{ {
if (vertexBuffer.count < 3) return; if (vertexBuffer.count < 3) return;
Point pmin{}, pmax{}; Point pmin{}, pmax{};
vertexBuffer.getMinMax(pmin, pmax); vertexBuffer.getMinMax(pmin, pmax);
meshGroupShapes.append(context, vertexBuffer); meshGroupShapes.append(vertexBuffer);
meshGroupShapesBBox.append(context, pmin, pmax); meshGroupShapesBBox.append(pmin, pmax);
updateBBox(pmin, pmax); updateBBox(pmin, pmax);
} }
void WgRenderDataShape::appendStroke(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd) void WgRenderDataShape::appendStroke(const WgIndexedVertexBuffer& vertexBufferInd)
{ {
if (vertexBufferInd.vcount < 3) return; if (vertexBufferInd.vcount < 3) return;
Point pmin{}, pmax{}; Point pmin{}, pmax{};
vertexBufferInd.getMinMax(pmin, pmax); vertexBufferInd.getMinMax(pmin, pmax);
meshGroupStrokes.append(context, vertexBufferInd); meshGroupStrokes.append(vertexBufferInd);
meshGroupStrokesBBox.append(context, pmin, pmax); meshGroupStrokesBBox.append(pmin, pmax);
updateBBox(pmin, pmax); updateBBox(pmin, pmax);
} }
@ -363,9 +298,9 @@ void WgRenderDataShape::updateAABB(const Matrix& tr) {
} }
void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rshape, const Matrix& tr, WgGeometryBufferPool* pool) void WgRenderDataShape::updateMeshes(const RenderShape &rshape, const Matrix& tr, WgGeometryBufferPool* pool)
{ {
releaseMeshes(context); releaseMeshes();
strokeFirst = rshape.strokeFirst(); strokeFirst = rshape.strokeFirst();
// get object scale // get object scale
@ -375,8 +310,8 @@ void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rsha
auto pbuff = pool->reqVertexBuffer(scale); auto pbuff = pool->reqVertexBuffer(scale);
pbuff->decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) { pbuff->decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) {
appendShape(context, path_buff); appendShape(path_buff);
if ((rshape.stroke) && (rshape.stroke->width > 0)) proceedStrokes(context, rshape.stroke, path_buff, pool); if ((rshape.stroke) && (rshape.stroke->width > 0)) proceedStrokes(rshape.stroke, path_buff, pool);
}, rshape.trimpath()); }, rshape.trimpath());
// update shapes bbox (with empty path handling) // update shapes bbox (with empty path handling)
@ -384,31 +319,31 @@ void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rsha
(this->meshGroupStrokesBBox.meshes.count > 0)) { (this->meshGroupStrokesBBox.meshes.count > 0)) {
updateAABB(tr); updateAABB(tr);
} else aabb = {{0, 0}, {0, 0}}; } else aabb = {{0, 0}, {0, 0}};
meshDataBBox.bbox(context, pMin, pMax); meshDataBBox.bbox(pMin, pMax);
pool->retVertexBuffer(pbuff); pool->retVertexBuffer(pbuff);
} }
void WgRenderDataShape::proceedStrokes(WgContext& context, const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool) void WgRenderDataShape::proceedStrokes(const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool)
{ {
assert(rstroke); assert(rstroke);
auto strokesGenerator = pool->reqIndexedVertexBuffer(buff.scale); auto strokesGenerator = pool->reqIndexedVertexBuffer(buff.scale);
if (rstroke->dash.count == 0) strokesGenerator->appendStrokes(buff, rstroke); if (rstroke->dash.count == 0) strokesGenerator->appendStrokes(buff, rstroke);
else strokesGenerator->appendStrokesDashed(buff, rstroke); else strokesGenerator->appendStrokesDashed(buff, rstroke);
appendStroke(context, *strokesGenerator); appendStroke(*strokesGenerator);
pool->retIndexedVertexBuffer(strokesGenerator); pool->retIndexedVertexBuffer(strokesGenerator);
} }
void WgRenderDataShape::releaseMeshes(WgContext& context) void WgRenderDataShape::releaseMeshes()
{ {
meshGroupStrokesBBox.release(context); meshGroupStrokesBBox.release();
meshGroupStrokes.release(context); meshGroupStrokes.release();
meshGroupShapesBBox.release(context); meshGroupShapesBBox.release();
meshGroupShapes.release(context); meshGroupShapes.release();
pMin = {FLT_MAX, FLT_MAX}; pMin = {FLT_MAX, FLT_MAX};
pMax = {0.0f, 0.0f}; pMax = {0.0f, 0.0f};
aabb = {{0, 0}, {0, 0}}; aabb = {{0, 0}, {0, 0}};
@ -418,8 +353,7 @@ void WgRenderDataShape::releaseMeshes(WgContext& context)
void WgRenderDataShape::release(WgContext& context) void WgRenderDataShape::release(WgContext& context)
{ {
releaseMeshes(context); releaseMeshes();
meshDataBBox.release(context);
renderSettingsStroke.release(context); renderSettingsStroke.release(context);
renderSettingsShape.release(context); renderSettingsShape.release(context);
WgRenderDataPaint::release(context); WgRenderDataPaint::release(context);
@ -445,10 +379,10 @@ WgRenderDataShape* WgRenderDataShapePool::allocate(WgContext& context)
void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* renderData) void WgRenderDataShapePool::free(WgContext& context, WgRenderDataShape* renderData)
{ {
renderData->meshGroupShapes.release(context); renderData->meshGroupShapes.release();
renderData->meshGroupShapesBBox.release(context); renderData->meshGroupShapesBBox.release();
renderData->meshGroupStrokes.release(context); renderData->meshGroupStrokes.release();
renderData->meshGroupStrokesBBox.release(context); renderData->meshGroupStrokesBBox.release();
renderData->clips.clear(); renderData->clips.clear();
mPool.push(renderData); mPool.push(renderData);
} }
@ -471,22 +405,16 @@ void WgRenderDataShapePool::release(WgContext& context)
void WgRenderDataPicture::updateSurface(WgContext& context, const RenderSurface* surface) void WgRenderDataPicture::updateSurface(WgContext& context, const RenderSurface* surface)
{ {
// upoate mesh data // upoate mesh data
meshData.imageBox(context, surface->w, surface->h); meshData.imageBox(surface->w, surface->h);
// update texture data // update texture data
imageData.update(context, surface); imageData.update(context, surface);
// update texture bind group
context.layouts.releaseBindGroup(bindGroupPicture);
bindGroupPicture = context.layouts.createBindGroupTexSampled(
context.samplerLinearRepeat, imageData.textureView
);
} }
void WgRenderDataPicture::release(WgContext& context) void WgRenderDataPicture::release(WgContext& context)
{ {
context.layouts.releaseBindGroup(bindGroupPicture); renderSettings.release(context);
imageData.release(context); imageData.release(context);
meshData.release(context);
WgRenderDataPaint::release(context); WgRenderDataPaint::release(context);
} }
@ -681,3 +609,85 @@ void WgRenderDataEffectParamsPool::release(WgContext& context)
mPool.clear(); mPool.clear();
mList.clear(); mList.clear();
} }
//***********************************************************************
// WgStageBufferGeometry
//***********************************************************************
void WgStageBufferGeometry::append(WgMeshData* meshData)
{
assert(meshData);
uint32_t vsize = meshData->vbuffer.count * sizeof(meshData->vbuffer[0]);
uint32_t tsize = meshData->tbuffer.count * sizeof(meshData->tbuffer[0]);
uint32_t isize = meshData->ibuffer.count * sizeof(meshData->ibuffer[0]);
vmaxcount = std::max(vmaxcount, meshData->vbuffer.count);
// append vertex data
if (vbuffer.reserved < vbuffer.count + vsize)
vbuffer.grow(std::max(vsize, vbuffer.reserved));
if (meshData->vbuffer.count > 0) {
meshData->voffset = vbuffer.count;
memcpy(vbuffer.data + vbuffer.count, meshData->vbuffer.data, vsize);
vbuffer.count += vsize;
}
// append tex coords data
if (vbuffer.reserved < vbuffer.count + tsize)
vbuffer.grow(std::max(tsize, vbuffer.reserved));
if (meshData->tbuffer.count > 0) {
meshData->toffset = vbuffer.count;
memcpy(vbuffer.data + vbuffer.count, meshData->tbuffer.data, tsize);
vbuffer.count += tsize;
}
// append index data
if (ibuffer.reserved < ibuffer.count + isize)
ibuffer.grow(std::max(isize, ibuffer.reserved));
if (meshData->ibuffer.count > 0) {
meshData->ioffset = ibuffer.count;
memcpy(ibuffer.data + ibuffer.count, meshData->ibuffer.data, isize);
ibuffer.count += isize;
}
}
void WgStageBufferGeometry::append(WgMeshDataGroup* meshDataGroup)
{
ARRAY_FOREACH(p, meshDataGroup->meshes) append(*p);
}
void WgStageBufferGeometry::append(WgRenderDataShape* renderDataShape)
{
append(&renderDataShape->meshGroupShapes);
append(&renderDataShape->meshGroupShapesBBox);
append(&renderDataShape->meshGroupStrokes);
append(&renderDataShape->meshGroupStrokesBBox);
append(&renderDataShape->meshDataBBox);
}
void WgStageBufferGeometry::append(WgRenderDataPicture* renderDataPicture)
{
append(&renderDataPicture->meshData);
}
void WgStageBufferGeometry::release(WgContext& context)
{
context.releaseBuffer(vbuffer_gpu);
context.releaseBuffer(ibuffer_gpu);
}
void WgStageBufferGeometry::clear()
{
vbuffer.clear();
ibuffer.clear();
vmaxcount = 0;
}
void WgStageBufferGeometry::flush(WgContext& context)
{
context.allocateBufferVertex(vbuffer_gpu, (float *)vbuffer.data, vbuffer.count);
context.allocateBufferIndex(ibuffer_gpu, (uint32_t *)ibuffer.data, ibuffer.count);
context.allocateBufferIndexFan(vmaxcount);
}

View file

@ -28,49 +28,36 @@
#include "tvgWgShaderTypes.h" #include "tvgWgShaderTypes.h"
struct WgMeshData { struct WgMeshData {
WGPUBuffer bufferPosition{}; Array<Point> vbuffer;
WGPUBuffer bufferTexCoord{}; Array<Point> tbuffer;
WGPUBuffer bufferIndex{}; Array<uint32_t> ibuffer;
size_t vertexCount{}; size_t voffset{};
size_t indexCount{}; size_t toffset{};
size_t ioffset{};
void draw(WgContext& context, WGPURenderPassEncoder renderPassEncoder); void update(const WgVertexBuffer& vertexBuffer);
void drawFan(WgContext& context, WGPURenderPassEncoder renderPassEncoder); void update(const WgIndexedVertexBuffer& vertexBufferInd);
void drawImage(WgContext& context, WGPURenderPassEncoder renderPassEncoder); void bbox(const Point pmin, const Point pmax);
void imageBox(float w, float h);
void update(WgContext& context, const WgVertexBuffer& vertexBuffer); void blitBox();
void update(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd);
void bbox(WgContext& context, const Point pmin, const Point pmax);
void imageBox(WgContext& context, float w, float h);
void blitBox(WgContext& context);
void release(WgContext& context);
};
class WgMeshDataPool {
private:
Array<WgMeshData*> mPool;
Array<WgMeshData*> mList;
public:
static WgMeshDataPool* gMeshDataPool;
WgMeshData* allocate(WgContext& context);
void free(WgContext& context, WgMeshData* meshData);
void release(WgContext& context);
}; };
struct WgMeshDataGroup { struct WgMeshDataGroup {
Array<WgMeshData*> meshes{}; Array<WgMeshData*> meshes{};
void append(WgContext& context, const WgVertexBuffer& vertexBuffer); void append(const WgVertexBuffer& vertexBuffer);
void append(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd); void append(const WgIndexedVertexBuffer& vertexBufferInd);
void append(WgContext& context, const Point pmin, const Point pmax); void append(const Point pmin, const Point pmax);
void release(WgContext& context); void release();
}; };
struct WgImageData { struct WgImageData {
WGPUTexture texture{}; WGPUTexture texture{};
WGPUTextureView textureView{}; WGPUTextureView textureView{};
WGPUBindGroup bindGroup{};
void update(WgContext& context, const RenderSurface* surface); void update(WgContext& context, const RenderSurface* surface);
void update(WgContext& context, const Fill* fill);
void release(WgContext& context); void release(WgContext& context);
}; };
@ -79,37 +66,29 @@ enum class WgRenderRasterType { Solid = 0, Gradient, Image };
struct WgRenderSettings struct WgRenderSettings
{ {
WGPUBuffer bufferGroupSolid{}; uint32_t bindGroupInd{};
WGPUBindGroup bindGroupSolid{}; WgShaderTypePaintSettings settings;
WGPUTexture texGradient{}; WgImageData gradientData;
WGPUTextureView texViewGradient{};
WGPUBuffer bufferGroupGradient{};
WGPUBuffer bufferGroupTransfromGrad{};
WGPUBindGroup bindGroupGradient{};
WgRenderSettingsType fillType{}; WgRenderSettingsType fillType{};
WgRenderRasterType rasterType{}; WgRenderRasterType rasterType{};
bool skip{}; bool skip{};
void updateFill(WgContext& context, const Fill* fill); void update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity);
void updateColor(WgContext& context, const RenderColor& c); void update(WgContext& context, const Fill* fill);
void update(WgContext& context, const RenderColor& c);
void release(WgContext& context); void release(WgContext& context);
}; };
struct WgRenderDataPaint struct WgRenderDataPaint
{ {
WGPUBuffer bufferModelMat{};
WGPUBuffer bufferBlendSettings{};
WGPUBindGroup bindGroupPaint{};
RenderRegion viewport{};
BBox aabb{{},{}}; BBox aabb{{},{}};
float opacity{}; RenderRegion viewport{};
Array<WgRenderDataPaint*> clips; Array<WgRenderDataPaint*> clips;
virtual ~WgRenderDataPaint() {}; virtual ~WgRenderDataPaint() {};
virtual void release(WgContext& context); virtual void release(WgContext& context);
virtual Type type() { return Type::Undefined; }; virtual Type type() { return Type::Undefined; };
void update(WgContext& context, const tvg::Matrix& transform, tvg::ColorSpace cs, uint8_t opacity);
void updateClips(tvg::Array<tvg::RenderData> &clips); void updateClips(tvg::Array<tvg::RenderData> &clips);
}; };
@ -127,13 +106,13 @@ struct WgRenderDataShape: public WgRenderDataPaint
bool strokeFirst{}; bool strokeFirst{};
FillRule fillRule{}; FillRule fillRule{};
void appendShape(WgContext& context, const WgVertexBuffer& vertexBuffer); void appendShape(const WgVertexBuffer& vertexBuffer);
void appendStroke(WgContext& context, const WgIndexedVertexBuffer& vertexBufferInd); void appendStroke(const WgIndexedVertexBuffer& vertexBufferInd);
void updateBBox(Point pmin, Point pmax); void updateBBox(Point pmin, Point pmax);
void updateAABB(const Matrix& tr); void updateAABB(const Matrix& tr);
void updateMeshes(WgContext& context, const RenderShape& rshape, const Matrix& tr, WgGeometryBufferPool* pool); void updateMeshes(const RenderShape& rshape, const Matrix& tr, WgGeometryBufferPool* pool);
void proceedStrokes(WgContext& context, const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool); void proceedStrokes(const RenderStroke* rstroke, const WgVertexBuffer& buff, WgGeometryBufferPool* pool);
void releaseMeshes(WgContext& context); void releaseMeshes();
void release(WgContext& context) override; void release(WgContext& context) override;
Type type() override { return Type::Shape; }; Type type() override { return Type::Shape; };
}; };
@ -150,7 +129,7 @@ public:
struct WgRenderDataPicture: public WgRenderDataPaint struct WgRenderDataPicture: public WgRenderDataPaint
{ {
WGPUBindGroup bindGroupPicture{}; WgRenderSettings renderSettings{};
WgImageData imageData{}; WgImageData imageData{};
WgMeshData meshData{}; WgMeshData meshData{};
@ -224,4 +203,69 @@ public:
void release(WgContext& context); void release(WgContext& context);
}; };
class WgStageBufferGeometry {
private:
Array<uint8_t> vbuffer;
Array<uint8_t> ibuffer;
uint32_t vmaxcount{};
public:
WGPUBuffer vbuffer_gpu{};
WGPUBuffer ibuffer_gpu{};
void append(WgMeshData* meshData);
void append(WgMeshDataGroup* meshDataGroup);
void append(WgRenderDataShape* renderDataShape);
void append(WgRenderDataPicture* renderDataPicture);
void initialize(WgContext& context){};
void release(WgContext& context);
void clear();
void flush(WgContext& context);
};
// typed uniform stage buffer with related bind groups handling
template<typename T>
class WgStageBufferUniform {
private:
Array<T> ubuffer;
WGPUBuffer ubuffer_gpu{};
Array<WGPUBindGroup> bbuffer;
public:
// append uniform data to cpu staged buffer and return related bind group index
uint32_t append(const T& value) {
ubuffer.push(value);
return ubuffer.count - 1;
}
void flush(WgContext& context) {
// flush data to gpu buffer from cpu memory including reserved data to prevent future gpu buffer reallocations
bool bufferChanged = context.allocateBufferUniform(ubuffer_gpu, (void*)ubuffer.data, ubuffer.reserved*sizeof(T));
// if gpu buffer handle was changed we must to remove all created binding groups
if (bufferChanged) releaseBindGroups(context);
// allocate bind groups for all new data items
for (uint32_t i = bbuffer.count; i < ubuffer.count; i++)
bbuffer.push(context.layouts.createBindGroupBuffer1Un(ubuffer_gpu, i*sizeof(T), sizeof(T)));
assert(bbuffer.count >= ubuffer.count);
}
// please, use index that was returned from append method
WGPUBindGroup operator[](const uint32_t index) const {
return bbuffer[index];
}
void clear() {
ubuffer.clear();
}
void release(WgContext& context) {
context.releaseBuffer(ubuffer_gpu);
releaseBindGroups(context);
}
void releaseBindGroups(WgContext& context) {
ARRAY_FOREACH(p, bbuffer)
context.layouts.releaseBindGroup(*p);
bbuffer.clear();
}
};
#endif // _TVG_WG_RENDER_DATA_H_ #endif // _TVG_WG_RENDER_DATA_H_

View file

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

View file

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

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2025 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgWgRenderTask.h"
#include <iostream>
//***********************************************************************
// WgPaintTask
//***********************************************************************
void WgPaintTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
if (renderData->type() == tvg::Type::Shape)
compositor.renderShape(context, (WgRenderDataShape*)renderData, blendMethod);
if (renderData->type() == tvg::Type::Picture)
compositor.renderImage(context, (WgRenderDataPicture*)renderData, blendMethod);
else assert(true);
}
//***********************************************************************
// WgSceneTask
//***********************************************************************
void WgSceneTask::run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
// begin the render pass for the current scene and clear the target content
WGPUColor color{};
if ((compose->method == MaskMethod::None) && (compose->blend != BlendMethod::Normal)) color = { 1.0, 1.0, 1.0, 0.0 };
compositor.beginRenderPass(encoder, renderTarget, true, color);
// run all childs (scenes and shapes)
runChildren(context, compositor, encoder);
// we must to end current render pass for current scene
compositor.endRenderPass();
// we must to apply effect for current scene
if (effect)
runEffect(context, compositor, encoder);
// there's no point in continuing if the scene has no destination target (e.g., the root scene)
if (!renderTargetDst) return;
// apply scene blending
if (compose->method == MaskMethod::None) {
compositor.beginRenderPass(encoder, renderTargetDst, false);
compositor.renderScene(context, renderTarget, compose);
// apply scene composition (for scenes, that have a handle to mask)
} else if (renderTargetMsk) {
compositor.beginRenderPass(encoder, renderTargetDst, false);
compositor.composeScene(context, renderTarget, renderTargetMsk, compose);
}
}
void WgSceneTask::runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
ARRAY_FOREACH(task, children) {
WgRenderTask* renderTask = *task;
// we need to restore current render pass without clear
compositor.beginRenderPass(encoder, renderTarget, false);
// run children (shape or scene)
renderTask->run(context, compositor, encoder);
}
}
void WgSceneTask::runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder)
{
assert(effect);
switch (effect->type) {
case SceneEffect::GaussianBlur: compositor.gaussianBlur(context, renderTarget, (RenderEffectGaussianBlur*)effect, compose); break;
case SceneEffect::DropShadow: compositor.dropShadow(context, renderTarget, (RenderEffectDropShadow*)effect, compose); break;
case SceneEffect::Fill: compositor.fillEffect(context, renderTarget, (RenderEffectFill*)effect, compose); break;
case SceneEffect::Tint: compositor.tintEffect(context, renderTarget, (RenderEffectTint*)effect, compose); break;
case SceneEffect::Tritone : compositor.tritoneEffect(context, renderTarget, (RenderEffectTritone*)effect, compose); break;
default: break;
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2025 the ThorVG project. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef _TVG_WG_RENDER_TASK_H_
#define _TVG_WG_RENDER_TASK_H_
#include "tvgWgCompositor.h"
// base class for any renderable objects
struct WgRenderTask {
virtual ~WgRenderTask() {}
virtual void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) = 0;
};
// task for sinlge shape rendering
struct WgPaintTask: public WgRenderTask {
// shape render properties
WgRenderDataPaint* renderData{};
BlendMethod blendMethod{};
WgPaintTask(WgRenderDataPaint* renderData, BlendMethod blendMethod) :
renderData(renderData), blendMethod(blendMethod) {}
// apply shape execution, including custom blending and clipping
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
};
// task for scene rendering with blending, composition and effect
struct WgSceneTask: public WgRenderTask {
public:
// parent scene (nullptr for root scene)
WgSceneTask* parent{};
// childs can be shapes or scenes tesks
Array<WgRenderTask*> children;
// scene blend/compose targets
WgRenderTarget* renderTarget{};
WgRenderTarget* renderTargetMsk{};
WgRenderTarget* renderTargetDst{};
// scene blend/compose properties
WgCompose* compose{};
// scene effect properties
const RenderEffect* effect{};
WgSceneTask(WgRenderTarget* renderTarget, WgCompose* compose, WgSceneTask* parent) :
parent(parent), renderTarget(renderTarget), compose(compose) {}
// run all, including all shapes drawing, blending, composition and effect
void run(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder) override;
private:
void runChildren(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
void runEffect(WgContext& context, WgCompositor& compositor, WGPUCommandEncoder encoder);
};
#endif // _TVG_WG_RENDER_TASK_H_

View file

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

View file

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

View file

@ -23,8 +23,6 @@
#include "tvgWgShaderSrc.h" #include "tvgWgShaderSrc.h"
#include <string> #include <string>
#define WG_SHADER_SOURCE(...) #__VA_ARGS__
//************************************************************************ //************************************************************************
// graphics shader source: stencil // graphics shader source: stencil
//************************************************************************ //************************************************************************
@ -32,14 +30,15 @@
const char* cShaderSrc_Stencil = R"( const char* cShaderSrc_Stencil = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f }; struct VertexOutput { @builtin(position) position: vec4f };
struct PaintSettings { transform: mat4x4f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
return out; return out;
} }
@ -56,15 +55,16 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
const char* cShaderSrc_Depth = R"( const char* cShaderSrc_Depth = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f }; struct VertexOutput { @builtin(position) position: vec4f };
struct PaintSettings { transform: mat4x4f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(2) @binding(0) var<uniform> uDepth : f32; @group(2) @binding(0) var<uniform> uDepth : f32;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.position.z = uDepth; out.position.z = uDepth;
return out; return out;
} }
@ -82,23 +82,22 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
const char* cShaderSrc_Solid = R"( const char* cShaderSrc_Solid = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f }; struct VertexOutput { @builtin(position) position: vec4f };
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var<uniform> uSolidColor : vec4f;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
return out; return out;
} }
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f { fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let Sc = uSolidColor; let Sc = uPaintSettings.color;
let So = uBlendSettings.a; let So = uPaintSettings.options.a;
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
} }
)"; )";
@ -110,34 +109,32 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
const char* cShaderSrc_Linear = R"( const char* cShaderSrc_Linear = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f }; struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f };
struct GradSettings { settings: vec4f, focal: vec4f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
// uniforms // uniforms
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(0) var uSamplerGrad : sampler;
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>; @group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
@group(2) @binding(2) var<uniform> uSettingGrad : GradSettings;
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
return out; return out;
} }
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f { fn fs_main(in: VertexOutput) -> @location(0) vec4f {
let pos = in.vGradCoord.xy; let pos = in.vGradCoord.xy;
let st = uSettingGrad.settings.xy; let st = uPaintSettings.gradient.coords.xy;
let ed = uSettingGrad.settings.zw; let ed = uPaintSettings.gradient.coords.zw;
let ba = ed - st; let ba = ed - st;
let t = dot(pos - st, ba) / dot(ba, ba); let t = dot(pos - st, ba) / dot(ba, ba);
let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5)); let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5));
let So = uBlendSettings.a; let So = uPaintSettings.options.a;
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
} }
)"; )";
@ -149,37 +146,35 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
const char* cShaderSrc_Radial = R"( const char* cShaderSrc_Radial = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f }; struct VertexOutput { @builtin(position) position : vec4f, @location(0) vGradCoord : vec4f };
struct GradSettings { settings: vec4f, focal: vec4f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(0) var uSamplerGrad : sampler;
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>; @group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
@group(2) @binding(2) var<uniform> uSettingGrad : GradSettings;
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
return out; return out;
} }
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f { fn fs_main(in: VertexOutput) -> @location(0) vec4f {
// orignal data // orignal data
let d0 = in.vGradCoord.xy - uSettingGrad.settings.xy; let d0 = in.vGradCoord.xy - uPaintSettings.gradient.coords.xy;
let d1 = uSettingGrad.settings.xy - uSettingGrad.focal.xy; let d1 = uPaintSettings.gradient.coords.xy - uPaintSettings.gradient.focal.xy;
let r0 = uSettingGrad.settings.z; let r0 = uPaintSettings.gradient.coords.z;
let rd = uSettingGrad.focal.z - uSettingGrad.settings.z; let rd = uPaintSettings.gradient.focal.z - uPaintSettings.gradient.coords.z;
let a = 1.0*dot(d1, d1) - 1.0*rd*rd; let a = 1.0*dot(d1, d1) - 1.0*rd*rd;
let b = 2.0*dot(d0, d1) - 2.0*r0*rd; let b = 2.0*dot(d0, d1) - 2.0*r0*rd;
let c = 1.0*dot(d0, d0) - 1.0*r0*r0; let c = 1.0*dot(d0, d0) - 1.0*r0*r0;
let t = (-b + sqrt(b*b - 4*a*c))/(2*a); let t = (-b + sqrt(b*b - 4*a*c))/(2*a);
let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(1.0 - t, 0.5)); let Sc = textureSample(uTextureGrad, uSamplerGrad, vec2f(1.0 - t, 0.5));
let So = uBlendSettings.a; let So = uPaintSettings.options.a;
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
} }
)"; )";
@ -191,17 +186,17 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
const char* cShaderSrc_Image = R"( const char* cShaderSrc_Image = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord: vec2f };
struct PaintSettings { transform: mat4x4f, options: vec4f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSampler : sampler; @group(2) @binding(0) var uSampler : sampler;
@group(2) @binding(1) var uTextureView : texture_2d<f32>; @group(2) @binding(1) var uTextureView : texture_2d<f32>;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.position = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); out.position = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.vTexCoord = in.texCoord; out.vTexCoord = in.texCoord;
return out; return out;
} }
@ -209,7 +204,7 @@ fn vs_main(in: VertexInput) -> VertexOutput {
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4f { fn fs_main(in: VertexOutput) -> @location(0) vec4f {
var Sc: vec4f = textureSample(uTextureView, uSampler, in.vTexCoord.xy); var Sc: vec4f = textureSample(uTextureView, uSampler, in.vTexCoord.xy);
let So: f32 = uBlendSettings.a; let So: f32 = uPaintSettings.options.a;
return vec4f(Sc.rgb * Sc.a * So, Sc.a * So); return vec4f(Sc.rgb * Sc.a * So, Sc.a * So);
}; };
)"; )";
@ -248,18 +243,18 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4f {
const char* cShaderSrc_Solid_Blend = R"( const char* cShaderSrc_Solid_Blend = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(1) vScrCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(1) vScrCoord: vec2f };
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f; // @group(2) - empty
@group(2) @binding(0) var<uniform> uSolidColor : vec4f;
@group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(0) var uSamplerDst : sampler;
@group(3) @binding(1) var uTextureDst : texture_2d<f32>; @group(3) @binding(1) var uTextureDst : texture_2d<f32>;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos; out.position = pos;
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
return out; return out;
@ -268,13 +263,13 @@ fn vs_main(in: VertexInput) -> VertexOutput {
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 }; struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData { fn getFragData(in: VertexOutput) -> FragData {
// get source data // get source data
let colorSrc = uSolidColor; let colorSrc = uPaintSettings.color;
let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy); let colorDst = textureSample(uTextureDst, uSamplerDst, in.vScrCoord.xy);
// fill fragment data // fill fragment data
var data: FragData; var data: FragData;
data.Sc = colorSrc.rgb; data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a; data.Sa = colorSrc.a;
data.So = uBlendSettings.a; data.So = uPaintSettings.options.a;
data.Dc = colorDst.rgb; data.Dc = colorDst.rgb;
data.Da = colorDst.a; data.Da = colorDst.a;
data.Sc = data.Sa * data.So * data.Sc; data.Sc = data.Sa * data.So * data.Sc;
@ -288,24 +283,22 @@ fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
const char* cShaderSrc_Linear_Blend = R"( const char* cShaderSrc_Linear_Blend = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f };
struct GradSettings { settings: vec4f, focal: vec4f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(0) var uSamplerGrad : sampler;
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>; @group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
@group(2) @binding(2) var<uniform> uSettingGrad : vec4f;
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
@group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(0) var uSamplerDst : sampler;
@group(3) @binding(1) var uTextureDst : texture_2d<f32>; @group(3) @binding(1) var uTextureDst : texture_2d<f32>;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos; out.position = pos;
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
return out; return out;
} }
@ -314,8 +307,8 @@ struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData { fn getFragData(in: VertexOutput) -> FragData {
// get source data // get source data
let pos = in.vGradCoord.xy; let pos = in.vGradCoord.xy;
let st = uSettingGrad.xy; let st = uPaintSettings.gradient.coords.xy;
let ed = uSettingGrad.zw; let ed = uPaintSettings.gradient.coords.zw;
let ba = ed - st; let ba = ed - st;
let t = dot(pos - st, ba) / dot(ba, ba); let t = dot(pos - st, ba) / dot(ba, ba);
let colorSrc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5)); let colorSrc = textureSample(uTextureGrad, uSamplerGrad, vec2f(t, 0.5));
@ -324,7 +317,7 @@ fn getFragData(in: VertexOutput) -> FragData {
var data: FragData; var data: FragData;
data.Sc = colorSrc.rgb; data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a; data.Sa = colorSrc.a;
data.So = uBlendSettings.a; data.So = uPaintSettings.options.a;
data.Dc = colorDst.rgb; data.Dc = colorDst.rgb;
data.Da = colorDst.a; data.Da = colorDst.a;
data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So); data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So);
@ -338,34 +331,32 @@ fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
const char* cShaderSrc_Radial_Blend = R"( const char* cShaderSrc_Radial_Blend = R"(
struct VertexInput { @location(0) position: vec2f }; struct VertexInput { @location(0) position: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vGradCoord : vec4f, @location(1) vScrCoord: vec2f };
struct GradSettings { settings: vec4f, focal: vec4f }; struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerGrad : sampler; @group(2) @binding(0) var uSamplerGrad : sampler;
@group(2) @binding(1) var uTextureGrad : texture_2d<f32>; @group(2) @binding(1) var uTextureGrad : texture_2d<f32>;
@group(2) @binding(2) var<uniform> uSettingGrad : GradSettings;
@group(2) @binding(3) var<uniform> uTransformGrad : mat4x4f;
@group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(0) var uSamplerDst : sampler;
@group(3) @binding(1) var uTextureDst : texture_2d<f32>; @group(3) @binding(1) var uTextureDst : texture_2d<f32>;
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos; out.position = pos;
out.vGradCoord = uTransformGrad * vec4f(in.position.xy, 0.0, 1.0); out.vGradCoord = uPaintSettings.gradient.transform * vec4f(in.position.xy, 0.0, 1.0);
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
return out; return out;
} }
struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 }; struct FragData { Sc: vec3f, Sa: f32, So: f32, Dc: vec3f, Da: f32 };
fn getFragData(in: VertexOutput) -> FragData { fn getFragData(in: VertexOutput) -> FragData {
let d0 = in.vGradCoord.xy - uSettingGrad.settings.xy; let d0 = in.vGradCoord.xy - uPaintSettings.gradient.coords.xy;
let d1 = uSettingGrad.settings.xy - uSettingGrad.focal.xy; let d1 = uPaintSettings.gradient.coords.xy - uPaintSettings.gradient.focal.xy;
let r0 = uSettingGrad.settings.z; let r0 = uPaintSettings.gradient.coords.z;
let rd = uSettingGrad.focal.z - uSettingGrad.settings.z; let rd = uPaintSettings.gradient.focal.z - uPaintSettings.gradient.coords.z;
let a = 1.0*dot(d1, d1) - 1.0*rd*rd; let a = 1.0*dot(d1, d1) - 1.0*rd*rd;
let b = 2.0*dot(d0, d1) - 2.0*r0*rd; let b = 2.0*dot(d0, d1) - 2.0*r0*rd;
let c = 1.0*dot(d0, d0) - 1.0*r0*r0; let c = 1.0*dot(d0, d0) - 1.0*r0*r0;
@ -376,7 +367,7 @@ fn getFragData(in: VertexOutput) -> FragData {
var data: FragData; var data: FragData;
data.Sc = colorSrc.rgb; data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a; data.Sa = colorSrc.a;
data.So = uBlendSettings.a; data.So = uPaintSettings.options.a;
data.Dc = colorDst.rgb; data.Dc = colorDst.rgb;
data.Da = colorDst.a; data.Da = colorDst.a;
data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So); data.Sc = mix(data.Dc, data.Sc, data.Sa * data.So);
@ -390,10 +381,10 @@ fn postProcess(d: FragData, R: vec4f) -> vec4f { return R; };
const char* cShaderSrc_Image_Blend = R"( const char* cShaderSrc_Image_Blend = R"(
struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f }; struct VertexInput { @location(0) position: vec2f, @location(1) texCoord: vec2f };
struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord : vec2f, @location(1) vScrCoord: vec2f }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord : vec2f, @location(1) vScrCoord: vec2f };
struct PaintSettings { transform: mat4x4f, options: vec4f };
@group(0) @binding(0) var<uniform> uViewMat : mat4x4f; @group(0) @binding(0) var<uniform> uViewMat : mat4x4f;
@group(1) @binding(0) var<uniform> uModelMat : mat4x4f; @group(1) @binding(0) var<uniform> uPaintSettings : PaintSettings;
@group(1) @binding(1) var<uniform> uBlendSettings : vec4f;
@group(2) @binding(0) var uSamplerSrc : sampler; @group(2) @binding(0) var uSamplerSrc : sampler;
@group(2) @binding(1) var uTextureSrc : texture_2d<f32>; @group(2) @binding(1) var uTextureSrc : texture_2d<f32>;
@group(3) @binding(0) var uSamplerDst : sampler; @group(3) @binding(0) var uSamplerDst : sampler;
@ -402,7 +393,7 @@ struct VertexOutput { @builtin(position) position: vec4f, @location(0) vTexCoord
@vertex @vertex
fn vs_main(in: VertexInput) -> VertexOutput { fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
let pos = uViewMat * uModelMat * vec4f(in.position.xy, 0.0, 1.0); let pos = uViewMat * uPaintSettings.transform * vec4f(in.position.xy, 0.0, 1.0);
out.position = pos; out.position = pos;
out.vTexCoord = in.texCoord; out.vTexCoord = in.texCoord;
out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5); out.vScrCoord = vec2f(0.5 + pos.x * 0.5, 0.5 - pos.y * 0.5);
@ -418,7 +409,7 @@ fn getFragData(in: VertexOutput) -> FragData {
var data: FragData; var data: FragData;
data.Sc = colorSrc.rgb; data.Sc = colorSrc.rgb;
data.Sa = colorSrc.a; data.Sa = colorSrc.a;
data.So = uBlendSettings.a; data.So = uPaintSettings.options.a;
data.Dc = colorDst.rgb; data.Dc = colorDst.rgb;
data.Da = colorDst.a; data.Da = colorDst.a;
data.Sc = data.Sc * data.So; data.Sc = data.Sc * data.So;

View file

@ -118,42 +118,41 @@ void WgShaderTypeVec4f::update(const RenderColor& c)
void WgShaderTypeVec4f::update(const RenderRegion& r) void WgShaderTypeVec4f::update(const RenderRegion& r)
{ {
vec[0] = r.x; // left vec[0] = r.min.x;
vec[1] = r.y; // top vec[1] = r.min.y;
vec[2] = r.x + r.w - 1; // right vec[2] = r.max.x - 1;
vec[3] = r.y + r.h - 1; // bottom vec[3] = r.max.y - 1;
} }
//************************************************************************ //************************************************************************
// WgShaderTypeGradient // WgShaderTypeGradSettings
//************************************************************************ //************************************************************************
void WgShaderTypeGradient::update(const LinearGradient* linearGradient) void WgShaderTypeGradSettings::update(const Fill* fill)
{ {
// update gradient data assert(fill);
// update transform matrix
Matrix invTransform;
if (inverse(&fill->transform(), &invTransform))
transform.update(invTransform);
else transform.identity();
// update gradient base points
if (fill->type() == Type::LinearGradient)
((LinearGradient*)fill)->linear(&coords.vec[0], &coords.vec[1], &coords.vec[2], &coords.vec[3]);
else if (fill->type() == Type::RadialGradient)
((RadialGradient*)fill)->radial(&coords.vec[0], &coords.vec[1], &coords.vec[2], &focal.vec[0], &focal.vec[1], &focal.vec[2]);
}
//************************************************************************
// WgShaderTypeGradientData
//************************************************************************
void WgShaderTypeGradientData::update(const Fill* fill)
{
if (!fill) return;
const Fill::ColorStop* stops = nullptr; const Fill::ColorStop* stops = nullptr;
auto stopCnt = linearGradient->colorStops(&stops); auto stopCnt = fill->colorStops(&stops);
updateTexData(stops, stopCnt);
// update base points
linearGradient->linear(&settings[0], &settings[1], &settings[2], &settings[3]);
};
void WgShaderTypeGradient::update(const RadialGradient* radialGradient)
{
// update gradient data
const Fill::ColorStop* stops = nullptr;
auto stopCnt = radialGradient->colorStops(&stops);
updateTexData(stops, stopCnt);
// update base points
radialGradient->radial(&settings[0], &settings[1], &settings[2], &settings[4], &settings[5], &settings[6]);
};
void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t stopCnt)
{
if (stopCnt == 0) return; if (stopCnt == 0) return;
static Array<Fill::ColorStop> sstops(stopCnt); static Array<Fill::ColorStop> sstops(stopCnt);
sstops.clear(); sstops.clear();
sstops.push(stops[0]); sstops.push(stops[0]);
@ -167,10 +166,10 @@ void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t
uint32_t range_s = 0; uint32_t range_s = 0;
uint32_t range_e = uint32_t(sstops[0].offset * (WG_TEXTURE_GRADIENT_SIZE-1)); uint32_t range_e = uint32_t(sstops[0].offset * (WG_TEXTURE_GRADIENT_SIZE-1));
for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) { for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) {
texData[ti * 4 + 0] = sstops[0].r; data[ti * 4 + 0] = sstops[0].r;
texData[ti * 4 + 1] = sstops[0].g; data[ti * 4 + 1] = sstops[0].g;
texData[ti * 4 + 2] = sstops[0].b; data[ti * 4 + 2] = sstops[0].b;
texData[ti * 4 + 3] = sstops[0].a; data[ti * 4 + 3] = sstops[0].a;
} }
// body // body
for (uint32_t di = 1; di < sstops.count; di++) { for (uint32_t di = 1; di < sstops.count; di++) {
@ -179,10 +178,10 @@ void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t
float delta = 1.0f/(range_e - range_s); float delta = 1.0f/(range_e - range_s);
for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) { for (uint32_t ti = range_s; (ti < range_e) && (ti < WG_TEXTURE_GRADIENT_SIZE); ti++) {
float t = (ti - range_s) * delta; float t = (ti - range_s) * delta;
texData[ti * 4 + 0] = tvg::lerp(sstops[di-1].r, sstops[di].r, t); data[ti * 4 + 0] = tvg::lerp(sstops[di-1].r, sstops[di].r, t);
texData[ti * 4 + 1] = tvg::lerp(sstops[di-1].g, sstops[di].g, t); data[ti * 4 + 1] = tvg::lerp(sstops[di-1].g, sstops[di].g, t);
texData[ti * 4 + 2] = tvg::lerp(sstops[di-1].b, sstops[di].b, t); data[ti * 4 + 2] = tvg::lerp(sstops[di-1].b, sstops[di].b, t);
texData[ti * 4 + 3] = tvg::lerp(sstops[di-1].a, sstops[di].a, t); data[ti * 4 + 3] = tvg::lerp(sstops[di-1].a, sstops[di].a, t);
} }
} }
// tail // tail
@ -190,10 +189,10 @@ void WgShaderTypeGradient::updateTexData(const Fill::ColorStop* stops, uint32_t
range_s = uint32_t(colorStopLast.offset * (WG_TEXTURE_GRADIENT_SIZE-1)); range_s = uint32_t(colorStopLast.offset * (WG_TEXTURE_GRADIENT_SIZE-1));
range_e = WG_TEXTURE_GRADIENT_SIZE; range_e = WG_TEXTURE_GRADIENT_SIZE;
for (uint32_t ti = range_s; ti < range_e; ti++) { for (uint32_t ti = range_s; ti < range_e; ti++) {
texData[ti * 4 + 0] = colorStopLast.r; data[ti * 4 + 0] = colorStopLast.r;
texData[ti * 4 + 1] = colorStopLast.g; data[ti * 4 + 1] = colorStopLast.g;
texData[ti * 4 + 2] = colorStopLast.b; data[ti * 4 + 2] = colorStopLast.b;
texData[ti * 4 + 3] = colorStopLast.a; data[ti * 4 + 3] = colorStopLast.a;
} }
} }

View file

@ -55,16 +55,44 @@ struct WgShaderTypeVec4f
void update(const RenderRegion& r); void update(const RenderRegion& r);
}; };
// sampler, texture, vec4f // WGSL: struct GradSettings { transform: mat4x4f, coords: vec4f, focal: vec4f };
#define WG_TEXTURE_GRADIENT_SIZE 512 struct WgShaderTypeGradSettings
struct WgShaderTypeGradient
{ {
float settings[4+4]{}; // WGSL: struct GradSettings { settings: vec4f, focal: vec4f; transform: mat4f }; // gradient transform matrix
uint8_t texData[WG_TEXTURE_GRADIENT_SIZE * 4]; WgShaderTypeMat4x4f transform;
// linear: [0] - x1, [1] - y1, [2] - x2, [3] - y2
// radial: [0] - cx, [1] - cy, [2] - cr
WgShaderTypeVec4f coords;
// radial: [0] - fx, [1] - fy, [2] - fr
WgShaderTypeVec4f focal;
void update(const LinearGradient* linearGradient); void update(const Fill* fill);
void update(const RadialGradient* radialGradient); };
void updateTexData(const Fill::ColorStop* stops, uint32_t stopCnt);
// WGSL: struct PaintSettings { transform: mat4x4f, options: vec4f, color: vec4f, gradient: GradSettings };
struct WgShaderTypePaintSettings
{
// paint transform matrix (must be at offset 0)
WgShaderTypeMat4x4f transform;
// [0] - color space, [3] - opacity
WgShaderTypeVec4f options;
// solid color
WgShaderTypeVec4f color;
// gradient settings (linear/radial)
WgShaderTypeGradSettings gradient;
// align to 256 bytes (see webgpu spec: minUniformBufferOffsetAlignment)
uint8_t _padding[256 - sizeof(transform) - sizeof(options) - sizeof(color) - sizeof(gradient)];
};
// see webgpu spec: 3.6.2. Limits - minUniformBufferOffsetAlignment (256)
static_assert(sizeof(WgShaderTypePaintSettings) == 256, "Uniform shader data type size must be aligned to 256 bytes");
// gradient color map
#define WG_TEXTURE_GRADIENT_SIZE 512
struct WgShaderTypeGradientData
{
uint8_t data[WG_TEXTURE_GRADIENT_SIZE * 4];
void update(const Fill* fill);
}; };
// gaussian params: sigma, scale, extend // gaussian params: sigma, scale, extend

View file

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