Appearance
kids.kapish.imports.psd
unity
PSD layer extraction and sprite generation. Thin Unity shim over Carrot.Imports.Psd NetStandard 2.1 implementation.
Architecture
The package is a thin Unity integration layer over the Carrot.Imports.Psd NetStandard 2.1 library (shipped as a precompiled DLL). The heavy lifting -- PSD binary parsing, layer extraction, channel decompression -- is handled by the DLL. The Unity-side code focuses on atlas packing and sprite/sub-asset creation.
Import Flow
The import uses AssetPostprocessor (not ScriptedImporter) to hook into Unity's native .psd texture import:
OnPreprocessTexture-- forcesisReadable = trueon theTextureImporterso the texture data is accessible after import.OnPostprocessTexture-- runs the full PSD pipeline: a. Opens the PSD file and callsPsdFile.Read()to parse layers. b. Filters to layers with pixel data (Rgba != null, non-zero dimensions). c. Packs layers into atlas textures viaPsdAtlasPacker.Pack(). d. CreatesSpritesub-assets namedpsd_{layerId}_{name}for each layer. e. Creates a composite (flattened)Texture2Dsub-asset fromPsdFile.CompositeRgba.
Atlas Packing
PsdAtlasPacker uses a shelf/row algorithm:
- Sort layers by height descending for tighter packing.
- Estimate initial atlas size from total pixel area, rounding up to next power-of-two (max 4096).
- Place layers left-to-right on shelves. When a layer doesn't fit horizontally, start a new row. When it doesn't fit vertically, overflow to a new atlas.
- Blit layer RGBA data into atlas pixel buffers (top-down during packing).
- Flip vertically when creating
Texture2D(Unity's texture space is bottom-up).
The packer supports multiple output atlases for large PSD files where layers exceed a single 4096x4096 texture.
Sprite Naming Convention
Sprites are named psd_{layerId}_{name} (e.g., psd_42_LeftArm). This convention is consumed by MohoPsdResolver in the kids.kapish.imports.moho package, which parses the layer ID from the sprite name to match Moho image layers to their PSD sources.
Runtime Layer Map
PsdLayerMap is a ScriptableObject that can be serialised alongside PSD files for runtime sprite creation. It stores atlas texture references and PsdLayerEntry records (layer ID, name, atlas index, rect). CreateSprite() generates sprites on demand without requiring the full PSD to be re-parsed.
Precompiled DLLs
| DLL | Purpose |
|---|---|
Carrot.Imports.Psd.dll | NetStandard 2.1 PSD parser. Reads PSD binary format, extracts layers with RGBA pixel data, dimensions, IDs, and names. Provides PsdFile.Read(Stream) and PsdFile.CompositeRgba. |
Carrot.IO.Binary.dll | Binary reader/writer utilities used by the PSD parser for endian-aware I/O. |
Both DLLs are referenced as precompiled references in both the Editor and Runtime assembly definitions, making them available at both edit-time and runtime.
Assemblies
| Assembly | Namespace | Platform | References |
|---|---|---|---|
Carrot.Imports.Psd.Editor | Carrot.Imports.Psd.Editor | Editor only | Carrot, Carrot.Imports.Psd.Runtime, precompiled: Carrot.Imports.Psd.dll, Carrot.IO.Binary.dll |
Carrot.Imports.Psd.Runtime | Carrot.Imports.Psd | All platforms | Precompiled: Carrot.Imports.Psd.dll, Carrot.IO.Binary.dll |
Key Design Decisions
- AssetPostprocessor, not ScriptedImporter. Unity already has a native PSD importer that creates a
Texture2D. This package hooks into that pipeline viaOnPostprocessTextureto add per-layer sprites without replacing the native importer. - Precompiled NetStandard 2.1 DLLs. The PSD parser is platform-agnostic and ships as a DLL so it can be shared with non-Unity projects (e.g., build tools, CI pipelines). This also avoids pulling the full parser source into every Unity project.
- Shelf packing. Simple and fast, good enough for cutout puppet sprites where layers are roughly similar sizes. The algorithm favours height-sorted input for tighter packing.
- Flattened composite. The composite texture sub-asset provides a quick preview of the full PSD without requiring layer compositing at runtime.
- Convention-based sprite naming. The
psd_{id}_{name}format is a deliberate API contract between this package andkids.kapish.imports.moho. Changing it requires updatingMohoPsdResolver.
Usage Guide
PSD layer extraction and sprite generation. Thin Unity shim over Carrot.Imports.Psd NetStandard 2.1 implementation.
Setup
- Add
kids.kapish.imports.psdto your Unity project manifest. This will pull inkids.kapishas a dependency. - Drop
.psdfiles into your Assets folder.
Import Workflow
When a .psd file is imported, Unity's native texture importer runs first, then PsdPostprocessor hooks in to:
- Parse the PSD and extract individual layers with pixel data.
- Pack all layers into power-of-two atlas textures (up to 4096x4096, multiple atlases if needed).
- Create a named
Spritesub-asset for each layer:psd_{layerId}_{layerName}. - Create a composite (flattened)
Texture2Dsub-asset representing the full PSD.
After import, expand the .psd asset in the Project window to see all sub-assets.
Accessing Sprites by Layer ID
At Edit Time
Sprites are sub-assets of the PSD. Reference them directly via the Inspector, or load them in editor scripts:
csharp
using UnityEditor;
using UnityEngine;
public static class PsdSpriteLoader
{
public static Sprite LoadLayerSprite(string psdAssetPath, int layerId)
{
Object[] subAssets = AssetDatabase.LoadAllAssetsAtPath(psdAssetPath);
foreach (Object asset in subAssets)
{
if (asset is Sprite sprite && sprite.name.StartsWith($"psd_{layerId}_"))
return sprite;
}
return null;
}
}At Runtime via PsdLayerMap
PsdLayerMap is a ScriptableObject that maps layer IDs to atlas rects for on-demand sprite creation:
csharp
using Carrot.Imports.Psd;
using UnityEngine;
public class RuntimeSpriteLoader : MonoBehaviour
{
public PsdLayerMap layerMap;
public SpriteRenderer target;
void Start()
{
Sprite sprite = layerMap.CreateSprite(layerId: 42, pixelsPerUnit: 100f);
if (sprite != null)
target.sprite = sprite;
}
}Looking Up Layer Entries
csharp
using Carrot.Imports.Psd;
using UnityEngine;
public class LayerInfo : MonoBehaviour
{
public PsdLayerMap layerMap;
void Start()
{
PsdLayerEntry entry = layerMap.FindEntry(layerId: 42);
if (entry != null)
{
Debug.Log($"Layer '{entry.layerName}' at atlas[{entry.atlasIndex}] " +
$"rect({entry.atlasX}, {entry.atlasY}, {entry.width}, {entry.height})");
}
}
}Integration with Moho Importer
This package is a dependency of kids.kapish.imports.moho. The Moho importer references PSD files by relative path and resolves sprites using the psd_{layerId}_{name} naming convention. When both packages are installed:
- Place
.psdfiles at the relative paths used in the Moho project. - The Moho importer registers PSD artifact dependencies so they import first.
MohoPsdResolverloads PSD sub-assets and matches sprites to Moho image layers by layer ID.
Sub-Asset Summary
After importing a .psd file, the following sub-assets are created:
| Sub-Asset | Naming | Description |
|---|---|---|
| Atlas textures | {name}_Atlas or {name}_Atlas_{n} | Power-of-two RGBA textures containing packed layer pixels. |
| Layer sprites | psd_{layerId}_{layerName} | One sprite per layer with pixel data, referencing the atlas texture. |
| Composite texture | {name}_Composite | Full flattened PSD as a single RGBA texture. |
Tips
- Layers without pixel data are skipped. Empty layers, adjustment layers, and groups do not produce sprites.
- Large PSDs may produce multiple atlases. If layers exceed a single 4096x4096 texture, additional atlas textures are created automatically.
- Composite uses PSD's pre-rendered composite. It does not re-composite layers -- it reads the stored composite image data from the PSD file.
- Sprites are centre-pivoted. All generated sprites use
pivot = (0.5, 0.5)at 100 pixels per unit.