SprayPaint
Spray colored marks, PNG/JPG images and animated GIFs on GTFO surfaces. Includes presets, a color palette, galleries and optional multiplayer image sharing.Changelog
0.1.8
- Fixed overlapping decal flicker:
- Back face offset 3 mm.
- Overlap depth stagger 1.5 mm per layer, capped at 30 mm.
0.1.4
- Added a multiplayer master switch (
Bby default), action hotkey rebinding, per-player upload limits, remote GIF animation limits, remote spray size in meters, baked GIF atlas transfer and hidden teammate sprays. - The multiplayer master switch defaults to OFF, so a fresh install starts fully offline; press
Bto enable it. - ESC now returns/closes every menu level.
- Cache index now removes duplicate
index.tsventries instead of appending the same source/cache pair repeatedly. - Fixed orphan
.texturecachefiles not being removed after their source image was deleted. Orphan texture/BC7 caches are now detected by rebuilding the expected cache path set from the currentSpraysandReceivedimages. - BC7 cache writes are now registered in the cache index on both sync and async paths.
- Long GIFs are sampled evenly to preserve their full duration instead of truncating.
- Added a minimum GIF frame delay to stop zero-delay GIFs from playing at 50 FPS.
- Removed the old experimental Steam P2P, compression, host-policy, missing-list and remote cap logic from the transfer layer.
Retained transfer core (not new)
-
Source-image transfer, SHA-256 validation and missing-chunk ACK resend were kept from the old transfer layer. They are listed here as retained behavior, not as new 0.1.4 features.
-
PNG/JPG now use the managed StbImageSharp decoder on a background thread, then upload raw RGBA on the main thread. This is true background decode instead of main-thread
Texture2D.LoadImage. StbImageSharp is licensed under Unlicense OR MIT. -
GIF gallery previews are now asynchronous; the first frame appears after background decode/upload instead of blocking the menu.
-
Removed the in-game image deletion key; delete image files in Explorer instead. Missing-image caches are cleaned automatically on startup and gallery refresh.
-
GIF spray startup no longer decodes a preview synchronously; pending GIFs use a placeholder and fill in asynchronously. GIF file reads also happen off the main thread.
-
Fixed JPG/PNG not appearing after the async change on GTFO's IL2CPP build:
UnityWebRequestTexturerequires a missingDownloadHandlerTexture(bool)constructor. Fallback now uses async file download + main-threadLoadImage. -
Gallery refresh now cleans index entries for images deleted outside the menu.
-
PNG/JPG static material and preview loading is asynchronous; the image cache index also covers static/DXT/send caches.
-
PNG/JPG material and preview decoding is now asynchronous (UnityWebRequestTexture), so the main thread no longer performs image decode on first use.
-
Added a cache index (
Sprays/.cache/index.tsv). Deleting an image from the gallery deletes its GIF/static/DXT/send caches; missing images are cleaned on startup. Legacy unindexed caches are purged once. -
Network transfer compression now detects GIF/PNG/JPEG by file header, not just extension.
-
GIF frame dedupe now uses a 64-bit hash instead of a byte-by-byte compare.
-
GIF decode dimension limit raised to 4096; added
GifMaxFrames(default 120) to bound frame memory. -
Added a bounded in-memory GIF cache with LRU eviction; active sprays/materials are never evicted. New options:
GifMemoryCacheMB(default 256) andGifMemoryCacheCount(default 8). -
GIF animation now pauses while its renderer is off-screen (
GifPauseOffscreen, default true). -
GIF decoder now merges identical consecutive frames and extends the previous frame's delay, reducing texture count and memory use.
-
Image format detection now uses file headers instead of relying only on the extension. GIF content stored under a
.png/.jpgname is decoded as GIF; WebP content is reported as unsupported instead of silently failing. -
Horizontal flip is now applied consistently to PNG, JPG and GIF textures.
FlipStaticImageskeeps its config key but affects all image formats. -
Unsupported WebP images now log a clear warning.
-
Menu/README format notes updated.
0.1.3
- Data folders are now created next to
SprayPaint.dll, so Thunderstore/R2ModMan installs showSpraysunder the installed plugin folder instead of the global plugin root. - Old images in the global
BepInEx/plugins/SprayPaint/Spraysfolder are migrated automatically. - Images placed directly beside the DLL are migrated into the new
Spraysfolder. - Documentation updated with the new paths.
0.1.2
- Documentation only: clarify the
SpraysandSprays/Receivedfolders and move the image-folder instructions to the top of the README. - No gameplay changes.
0.1.1
- Added host-controlled multiplayer file transfer permission.
- Added independent received-image dimension limit.
- Added received spray size override.
- Received images are now stored only in
Sprays/Received. - Added menu X/Y position rows with screen clamping.
- Added preset delete confirmation with Backspace.
- Fixed ARGB/RGBA texture cache channel swap that caused reddish or oversaturated images.
- Fixed cache-hit logging when the target file already exists.
- Updated Thunderstore README and package metadata.
0.1.0
- Initial release.
- Color, image and animated GIF sprays.
- Presets, color palette, favorites and recent colors.
- Separate local and received image galleries.
- Optional multiplayer synchronization with chunked file transfer.
- Targeted requests, fallback broadcast, ACK-based resend and SHA-256 validation.
- Content hash cache and persistent hash index.
- Host-controlled file transfer permission.
- Receiver image size and pixel limits.
- Menu position adjustment with screen clamping.
- Keyboard-only menu operation.
