Tencent / libpag

The official rendering library for PAG (Portable Animated Graphics) files that renders After Effects animations natively across multiple platforms.

Home Page:https://pag.io

Repository from Github https://github.comTencent/libpagRepository from Github https://github.comTencent/libpag

Libpag generates invalid WebGL shader code with undefined variable

johncalvinroberts opened this issue · comments

你好👋 谢谢你们的很棒的库,我有一个bug想提一下

我先写英文 你们中文英文回答都可以

谢谢

Which Version of libpag are you using?

libpag 4.3.71

What Platform are you on?

Web

Expected Behavior

Libpag should render without breaking

Actual Behavior

With some pag files, libpag is generating invalid webGL shader code, which then crashes and fails to render anything at all.

The shader code is generated by tgfx (another Tencent library), and the effects/syntax itself fine, it is the last (transfer) step that is bugged.

The offending shader is the following:

#version 100

precision mediump float;
uniform vec4 ustart_Stage1;
uniform vec4 urightBorderColor_Stage1;
uniform vec4 uColor_Stage2;
uniform vec4 uleftBorderColor_Stage1;
uniform vec4 uColor_Stage0;
uniform vec4 uend_Stage1;

varying highp vec2 vTransformedCoords_0_Stage0;
varying highp float vCoverage_Stage0;

void main() {
    vec4 outputColor_Stage0;
    vec4 outputCoverage_Stage0;
    { // Stage 0 DefaultGeometryProcessor
        outputCoverage_Stage0 = vec4(vCoverage_Stage0);
        outputColor_Stage0 = uColor_Stage0;
    }
    vec4 output_Stage1;
    { // Stage 1 ClampedGradientEffect
        vec4 _child1;
        {
            // Child Index 1 (mangle: _c0): LinearGradientLayout
            float t = vTransformedCoords_0_Stage0.x + 1.0000000000000001e-05;
            _child1 = vec4(t, 1.0, 0.0, 0.0);
        }
        vec4 t = _child1;
        if (t.y < 0.0) {
            output_Stage1 = vec4(0.0);
        } else if (t.x <= 0.0) {
            output_Stage1 = uleftBorderColor_Stage1;
        } else if (t.x >= 1.0) {
            output_Stage1 = urightBorderColor_Stage1;
        } else {
            vec4 _child0;
            vec4 _childInput_c1 = t;
            {
                // Child Index 0 (mangle: _c1): SingleIntervalGradientColorizer
                float t = _childInput_c1.x;
                _child0 = (1.0 - t) * ustart_Stage1 + t * uend_Stage1;
            }
            output_Stage1 = _child0;
        }
    }
    vec4 output_Stage2;
    { // Stage 2 ConstColorProcessor
        output_Stage2 = uColor_Stage2;
        output_Stage2 *= output_Stage1;
    }
    { // Xfer Processor PorterDuffXferProcessor
        vec4 localOutputColor;
        localOutputColor.a = output_Stage2.a + (1.0 - output_Stage2.a) * _dstColor.a;
        localOutputColor.rgb = (1.0 - output_Stage2.a) * _dstColor.rgb + (1.0 - _dstColor.a) * output_Stage2.rgb + output_Stage2.rgb * _dstColor.rgb;
        localOutputColor = outputCoverage_Stage0 * localOutputColor + (vec4(1.0) - outputCoverage_Stage0) * _dstColor;
        gl_FragColor = localOutputColor;
    }
}

It is the section annotated with PorterDuffXferProcessor, which is referring to a _dstColor variables that was never declared

Browser emits this error when trying to execute this shader:

TypeError: Failed to execute 'getAttribLocation' on 'WebGLRenderingContext': parameter 1 is not of type 'WebGLProgram'.
    at _emscripten_glGetAttribLocation (libpag.js:9:132060)
    at libpag.wasm:0x1a952a
    at libpag.wasm:0x1d207f
    at libpag.wasm:0x71633
    at libpag.wasm:0x18effc
    at libpag.wasm:0x1bd7a1
    at libpag.wasm:0x459a1
    at libpag.wasm:0x6f926
    at libpag.wasm:0x224bbf
    at libpag.wasm:0x217960

Code Example

Here's a working JS fiddle with a reproduction of the error: link

Or, html file:

Click to expand/collapse bug repro HTML file
<!DOCTYPE html>
<html lang="en-US">
<head>
	<title>Play PAG File</title>
	<script src="https://unpkg.com/libpag@4.3.51/lib/libpag.min.js"></script>
	<!-- script src="https://greggman.github.io/webgl-lint/webgl-lint.js" data-gman-debug-helper='{ "maxDrawCalls": 0 }'></script -->
	<style lang="css">
	body {
	  margin: 1rem;
	  display: flex;
	  flex-direction: column;
	  place-items: center;
	  gap: 16px;
	}

	h1 {
	  font-size: 3.2em;
	  line-height: 1.1;
	}
	
	canvas {
	  border: 2px solid red;
	  max-width: 300px;
	  height: auto !important;
	}
	
	.invisible {
		display: none;
	}
	
	.error {
		color: red;
		font-weight: 600;
	}
	</style>
</head>
<body>
	<h1>Play PAG File</h1>
	<span class="invisible error" id="error-label"></span>
	<input type="file" id="pag-input" disabled />
	<canvas class="invisible" id="pag-canvas" width="480" height="640"/>
	
	<script>
	window.onload = async () => {
		const pagInput = document.getElementById("pag-input");
		const pagCanvas = document.getElementById("pag-canvas");
		const errorLabel = document.getElementById("error-label");
		
		function handleError(error) {
		  const message = typeof error === "object" && 'message' in error ? error.message : `${error}`;
		  console.error(error);
		  errorLabel.innerText = "ERROR: " + message;
		  errorLabel.classList.remove("invisible");
		}
		
		try {
			let pagFile, pagView;
			const PAG = await window.libpag.PAGInit();
			console.log("libpag initialized successfully");
			pagInput.disabled = false;
			pagInput.addEventListener("change", async (ev) => {
				const files = Array.from(ev.target.files ?? []);
				if (files.length != 1) {
					return;
				}
				try {
					pagView?.stop();
					pagView?.destroy();
					pagFile?.destroy();
					const pagFileData = await files[0].arrayBuffer();
					pagFile = await PAG.PAGFile.loadFromBuffer(pagFileData);
					pagCanvas.width = pagFile.width();
					pagCanvas.height = pagFile.height();
					pagView = await PAG.PAGView.init(pagFile, pagCanvas);
					pagCanvas.classList.remove("invisible");
					await pagView.play();
				} catch (error) {
					handleError(error);
				}
			});
		} catch (error) {
			handleError(error);
		}
	}
	</script>
</body>
</html>

Please use the pag file attached below (compressed to .zip).

PAG File

clarity_hook_frame_1_IN.pag.zip

This issue was present in the v4.3 release but has been fixed in the latest v4.4 release. Please check with the v4.4.25 version.

Indeed, we updated to 4.4.x and bug is resolved. Thank you! @Hparty