Loading...
Searching...
No Matches
TextureLoader.hpp
1#pragma once
2#include <score_plugin_gfx_export.h>
3
4#include <QImage>
5#include <QString>
6
7#include <cstdint>
8#include <optional>
9#include <unordered_map>
10
11class QRhi;
12class QRhiTexture;
13class QRhiResourceUpdateBatch;
14class QByteArray;
15
16namespace score::gfx
17{
18
19// =============================================================================
20// CPU-side decode result.
21//
22// The default implementation produces RGBA8888 data via QImageReader. The
23// `srgb` flag is metadata only — it does NOT alter the pixel bytes, it just
24// records whether the caller intends those bytes to be interpreted as sRGB
25// when the texture is sampled (set the QRhiTexture format to RGBA8 vs sRGB8A8
26// at upload time accordingly).
27//
28// Future swap-in candidates: OIIO (HDR/EXR), KTX2 (transcoded BCn), AVIF.
29// =============================================================================
31{
32 QImage image; // QImage::Format_RGBA8888 (no premul)
33 QString debug_name; // For QRhiTexture::setName()
34};
35
36// =============================================================================
37// Decode helpers — synchronous, called on the render thread.
38//
39// Both variants decode directly with QImage; cross-output dedup is handled at
40// the TextureCache (per-renderer GPU side) and AssetTable (content-hash
41// keyed) layers. We don't share a CPU-side cache here — reusing
42// Gfx::ImageCache leaks every decoded path for the program lifetime.
43// =============================================================================
44
45SCORE_PLUGIN_GFX_EXPORT
46std::optional<DecodedImage> decodeImageFromPath(const QString& path);
47
48SCORE_PLUGIN_GFX_EXPORT
49std::optional<DecodedImage> decodeImageFromMemory(
50 const QByteArray& bytes, const QString& mime_hint);
51
52// Colour-bleed a straight-alpha RGBA8888 image whose alpha is coverage (a
53// cutout or blended base colour): the RGB under every alpha-0 texel is
54// replaced by the colour of the covered texels around it, alpha untouched.
55// Filtering and mip generation average RGB and alpha independently, so the
56// arbitrary (often black) RGB authored under alpha 0 would otherwise bleed
57// into the surviving edge as a dark or off-colour fringe. Direct neighbours
58// of covered texels take their alpha-weighted mean; the rest are filled
59// pull-push from alpha-weighted 2x2 block averages, which is what a box mip
60// chain averages them into. Linear in the texel count. Returns false, leaving
61// the image as it is, when it has no alpha-0 texel or no covered one.
62SCORE_PLUGIN_GFX_EXPORT
63bool bleedTransparentColor(QImage& image);
64
65// =============================================================================
66// GPU upload — pure RHI, no I/O. Allocates a freshly-sized QRhiTexture
67// (RGBA8 or sRGB8_ALPHA8 depending on `srgb`), records the upload into
68// `batch`. Caller owns the returned pointer (delete via deleteLater()).
69//
70// Returns nullptr on QRhi allocation failure.
71// =============================================================================
72
73SCORE_PLUGIN_GFX_EXPORT
74QRhiTexture* uploadImageToTexture(
75 QRhi& rhi, QRhiResourceUpdateBatch& batch, const QImage& img, bool srgb,
76 const QString& debug_name = {});
77
78// =============================================================================
79// One-shot decode + upload helpers. Convenience for callers that don't need
80// to reuse the decoded CPU bytes.
81// =============================================================================
82
83SCORE_PLUGIN_GFX_EXPORT
84QRhiTexture* loadAndUploadTexture(
85 QRhi& rhi, QRhiResourceUpdateBatch& batch, const QString& path, bool srgb);
86
87SCORE_PLUGIN_GFX_EXPORT
88QRhiTexture* loadAndUploadTexture(
89 QRhi& rhi, QRhiResourceUpdateBatch& batch, const QByteArray& bytes,
90 const QString& mime_hint, bool srgb);
91
92// =============================================================================
93// Per-renderer GPU texture cache.
94//
95// QRhiTexture* is bound to one QRhi instance, so this cache MUST live on the
96// render-side node (e.g. ScenePreprocessorNode), not as a global singleton. Owns
97// the textures it returns; clear() (also runs in the dtor) schedules each via
98// deleteLater().
99//
100// Keys: a file path OR a stable content hash (for embedded glTF/FBX blobs).
101// Two entries with the same origin but different sRGB flags coexist.
102// =============================================================================
103class SCORE_PLUGIN_GFX_EXPORT TextureCache
104{
105public:
106 TextureCache() = default;
108
109 TextureCache(const TextureCache&) = delete;
110 TextureCache& operator=(const TextureCache&) = delete;
111 TextureCache(TextureCache&&) noexcept = default;
112 TextureCache& operator=(TextureCache&&) noexcept = default;
113
114 // First call decodes + uploads via `batch`; later calls hit the cache.
115 // Returns nullptr if the file can't be decoded.
116 QRhiTexture* acquireFromPath(
117 QRhi& rhi, QRhiResourceUpdateBatch& batch, const QString& path, bool srgb);
118
119 // Same, for embedded blobs. `content_hash` is supplied by the caller — its
120 // identity (not its value) is what guards re-upload.
121 QRhiTexture* acquireFromMemory(
122 QRhi& rhi, QRhiResourceUpdateBatch& batch, const QByteArray& bytes,
123 const QString& mime_hint, uint64_t content_hash, bool srgb);
124
125 // Schedule deleteLater() on every owned texture and drop the map.
126 void clear();
127
128 std::size_t size() const noexcept { return m_textures.size(); }
129
130private:
131 struct Key
132 {
133 QString origin; // file path, or "blob:<hash-hex>" for memory blobs
134 bool srgb{};
135 bool operator==(const Key&) const noexcept = default;
136 };
137 struct KeyHash
138 {
139 std::size_t operator()(const Key& k) const noexcept;
140 };
141
142 std::unordered_map<Key, QRhiTexture*, KeyHash> m_textures;
143};
144
145} // namespace score::gfx
Definition TextureLoader.hpp:104
Graphics rendering pipeline for ossia score.
Definition Filter/PreviewWidget.hpp:11
Definition TextureLoader.hpp:31
Definition Uniforms.hpp:5