Skip to content

Commit 099f1e9

Browse files
jinwuwu001richardshan0614fengdyli
authored
Add WeChat MiniProgram support for PAGX viewer with rendering fixes and performance optimizations. (#3507)
Co-authored-by: richardshan <richardshan0614@gmail.com> Co-authored-by: 风笛 <1753894329@qq.com>
1 parent bef31de commit 099f1e9

65 files changed

Lines changed: 11759 additions & 77 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,5 @@ cli/npm/package-lock.json
121121
# PAGX verify artifacts
122122
*.layout.xml
123123

124+
# Local-only helper scripts (not part of the project)
125+
pagx/wechat/script/copy-to-cocraft.js
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/////////////////////////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Tencent is pleased to support the open source community by making libpag available.
4+
//
5+
// Copyright (C) 2026 Tencent. All rights reserved.
6+
//
7+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
8+
// except in compliance with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// unless required by applicable law or agreed to in writing, software distributed under the
13+
// license is distributed on an "as is" basis, without warranties or conditions of any kind,
14+
// either express or implied. see the license for the specific language governing permissions
15+
// and limitations under the license.
16+
//
17+
/////////////////////////////////////////////////////////////////////////////////////////////////
18+
19+
#pragma once
20+
21+
#include <memory>
22+
#include <string>
23+
24+
namespace tgfx {
25+
class Image;
26+
}
27+
28+
namespace pagx {
29+
30+
/**
31+
* Provides pre-decoded image resources to the rendering pipeline. Platform-specific implementations
32+
* can manage their own caching, eviction, and progressive loading strategies without coupling
33+
* decoded image state to the document model layer.
34+
*
35+
* The renderer queries this provider during layer building. When resolveImage() returns non-null,
36+
* the renderer uses that image directly and skips all codec-based decoding paths (embedded data,
37+
* data URI, file path). When it returns nullptr, the renderer falls back to the standard decoding
38+
* chain.
39+
*
40+
* Thread safety: the provider is accessed only on the render thread (inside draw()/flush()), so
41+
* implementations do not need internal synchronization unless they share state with other threads.
42+
*/
43+
class ImageResourceProvider {
44+
public:
45+
virtual ~ImageResourceProvider() = default;
46+
47+
/**
48+
* Resolves a pre-decoded image for the given external file path. The implementation decides
49+
* which quality level to return (full, thumbnail, or nullptr for fallback).
50+
* @param filePath the external file path as declared in the PAGX document's Image node.
51+
* @return A tgfx::Image ready for rendering, or nullptr to signal the renderer should decode
52+
* from embedded data / file path.
53+
*/
54+
virtual std::shared_ptr<tgfx::Image> resolveImage(const std::string& filePath) = 0;
55+
56+
/**
57+
* Returns true if the provider currently holds any decoded image (full or thumbnail) for the
58+
* given path. Used by getExternalFilePaths() to exclude paths that already have a decoded
59+
* counterpart, preventing redundant network fetches.
60+
* @param filePath the external file path to check.
61+
*/
62+
virtual bool hasImage(const std::string& filePath) const = 0;
63+
};
64+
65+
} // namespace pagx

include/pagx/PAGXDocument.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <unordered_set>
2525
#include <vector>
2626
#include "pagx/FontConfig.h"
27+
#include "pagx/ImageResourceProvider.h"
2728
#include "pagx/nodes/Animation.h"
2829
#include "pagx/nodes/Layer.h"
2930
#include "pagx/nodes/Node.h"
@@ -33,6 +34,8 @@ namespace pagx {
3334

3435
class LayoutContext;
3536
class PAGScene;
37+
class Image;
38+
class ImagePattern;
3639

3740
/**
3841
* PAGXDocument is the root container for a PAGX document.
@@ -126,7 +129,9 @@ class PAGXDocument : public Node {
126129

127130
/**
128131
* Returns a list of external file paths referenced by Image nodes or external composition layers
129-
* that have no embedded data. Data URIs (paths starting with "data:") are excluded.
132+
* that have no embedded data. Data URIs (paths starting with "data:") are excluded. Image nodes
133+
* for which the current ImageResourceProvider already holds a decoded counterpart are also
134+
* excluded so the same resource is not fetched twice.
130135
*/
131136
std::vector<std::string> getExternalFilePaths() const;
132137

@@ -143,6 +148,17 @@ class PAGXDocument : public Node {
143148
*/
144149
bool loadFileData(const std::string& filePath, std::shared_ptr<Data> data);
145150

151+
/**
152+
* Sets the image resource provider used by the rendering pipeline to resolve pre-decoded images.
153+
* When set, getExternalFilePaths() consults the provider to exclude paths that already have a
154+
* decoded counterpart. The renderer queries the provider during layer building via
155+
* resolveImage().
156+
*
157+
* Passing nullptr removes the provider; the renderer will use only embedded data / file paths.
158+
* @param provider the platform-specific image resource provider, or nullptr to remove.
159+
*/
160+
void setImageResourceProvider(std::shared_ptr<ImageResourceProvider> provider);
161+
146162
/**
147163
* Executes auto layout on the document, positioning layers according to their layout
148164
* constraints. Must be called before rendering or font embedding. Re-running layout on an
@@ -234,6 +250,9 @@ class PAGXDocument : public Node {
234250

235251
private:
236252
PAGXDocument() = default;
253+
254+
const std::vector<const Layer*>& findLayersByImageFilePath(const std::string& imageFilePath);
255+
237256
// Recursive layout worker. visited holds the documents on the current ancestor path so an
238257
// externalDoc cycle built directly through the API (bypassing loadFileData's own chain guard)
239258
// is detected and stops the recursion instead of overflowing the stack.
@@ -247,6 +266,7 @@ class PAGXDocument : public Node {
247266
void registerLiveScene(const std::shared_ptr<PAGScene>& scene);
248267
void unregisterLiveScene(PAGScene* scene);
249268

269+
std::shared_ptr<ImageResourceProvider> _imageResourceProvider = nullptr;
250270
FontConfig fontConfig;
251271
bool layoutApplied = false;
252272
std::unordered_map<std::string, Node*> nodeMap = {};
@@ -257,11 +277,20 @@ class PAGXDocument : public Node {
257277
// does not keep PAGScene alive; expired entries are pruned during notifyChange.
258278
std::vector<std::weak_ptr<PAGScene>> liveScenes = {};
259279

280+
// Lazily built index of Image node filePath -> pagx Layer list, used by
281+
// findLayersByImageFilePath(). Built on first query; invalidated by notifyChange() since edits
282+
// may alter the tree topology or Image node filePath values. Also freed when the document is
283+
// destroyed (on the next parsePAGX() call).
284+
std::unordered_map<std::string, std::vector<const Layer*>> layersByImageFilePath = {};
285+
bool layersByImageFilePathBuilt = false;
286+
260287
friend class PAGXImporter;
261288
friend class PAGXExporter;
262289
friend class TextLayoutContext;
263290
friend class PAGScene;
264291
friend class PAGComposition;
292+
friend class LayerBuilderContext;
293+
friend class LayerBuilderSession;
265294
};
266295

267296
} // namespace pagx

include/pagx/nodes/Image.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@ namespace pagx {
2727

2828
/**
2929
* Image represents an image resource that can be referenced by other nodes. The image source can
30-
* be a file path, a URL, or a base64-encoded data URI.
30+
* be a file path, a URL, or a base64-encoded data URI. Platform-specific decoded images are
31+
* managed externally via ImageResourceProvider, not stored on this node.
3132
*/
3233
class Image : public Node {
3334
public:
3435
/**
35-
* Image binary data (decoded from base64).
36+
* Image binary data (decoded from base64 or loaded via loadFileData).
3637
*/
3738
std::shared_ptr<Data> data = nullptr;
3839

pagx/wechat/.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Build outputs
2+
wasm-mt/
3+
wasm/
4+
dist/
5+
6+
# Build cache
7+
script/build-pagx-viewer/
8+
script/wasm-mt/
9+
script/.build.lock
10+
.*.md5
11+
package-lock.json
12+
ts/wasm
13+
types/
14+
lib/
15+
docs/

0 commit comments

Comments
 (0)