# TrapMap Managed PNG Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Remove TrapMap's platform-specific drawing dependency and produce the same bounded 300 DPI PNG with a managed encoder that runs inside Clumsy. **Architecture:** Keep `TrapMapImageExporter.ExportIfEnabled` and its file-reservation/publication behavior. Replace only the renderer with an internal RGBA raster surface, primitive drawing functions, a compact embedded bitmap font, and `StbImageWriteSharp` for PNG encoding; insert the 300 DPI `pHYs` chunk after encoding. **Tech Stack:** C# 10, .NET Standard 2.0, `StbImageWriteSharp` 1.16.7, PowerShell contract/PNG parsing tests. ## Global Constraints - Do not inspect or modify `TrajPlanner`. - Do not commit or stage any file. - Do not change grid construction, Painter behavior, movement behavior, switches, output location, naming, 300 DPI, 4 pixels per cell, 4000-pixel edge limit, or 50 MiB limit. - The only new image package is `StbImageWriteSharp` version 1.16.7; do not add native assets or another graphics package. - `TrapMapImageExporter` must have no runtime reference to `System.Drawing.Common` or `System.Drawing`. - Preserve collision-safe temporary-file reservation, encoded-size validation, atomic publication, and contained export failures. --- ### Task 1: Replace System.Drawing rendering with a managed Stb PNG encoder **Files:** - Modify: `ClumsyPilot/ClumsyPilot.csproj` - Modify: `ClumsyPilot/TrapMapImageExporter.cs` - Modify: `ClumsyPilot/tests/verify_trapmap_image.ps1` - Modify: `docs/superpowers/specs/2026-07-22-trap-map-image-and-console-design.md` - Modify: `docs/superpowers/plans/2026-07-22-trap-map-image-and-console.md` **Interfaces:** - Preserve: `TrapMapImageExporter.ExportIfEnabled(bool, TrapMapImageExportRequest)` and all public request/result properties and constants. - Add only private implementation units: `RgbaSurface`, integer drawing helpers, bitmap-font helpers, and `PngWriter`. - `RenderToTemporaryPng` continues to consume the existing request/dimensions and write to the already exclusively reserved stream. - [ ] **Step 1: Add dependency-removal and PNG-structure assertions** Update `verify_trapmap_image.ps1` before production code. Require that: ```powershell if ($project.PackageReference.Include -contains 'System.Drawing.Common') { throw 'TrapMap must not depend on System.Drawing.Common.' } if ($project.Target.Name -contains 'DeployFrameworkDrawingRuntime') { throw 'Legacy drawing-runtime deployment target remains.' } if (-not ($project.PackageReference | Where-Object { $_.Include -eq 'StbImageWriteSharp' -and $_.Version -eq '1.16.7' })) { throw 'Exact managed PNG package is missing.' } if ($exporterSource -match 'System\.Drawing|\bBitmap\b|\bGraphics\b|ImageFormat') { throw 'Exporter still uses the external drawing API.' } ``` Parse the generated PNG without loading a drawing assembly. Verify signature, one `IHDR`, one `pHYs`, one or more `IDAT`, and `IEND`; verify every chunk CRC. Assert `IHDR` width/height and RGBA8 fields and `pHYs` values `11811,11811,1`. Decode representative pixels with a test-only PNG decoder or the Stb package and reuse the existing color/Y-inversion assertions. Add a clean-output assertion after build: ```powershell $drawingDll = Join-Path (Split-Path -Parent $AssemblyPath) 'System.Drawing.Common.dll' if (Test-Path -LiteralPath $drawingDll) { throw 'System.Drawing.Common.dll must not be deployed for TrapMap.' } ``` - [ ] **Step 2: Run RED verification** ```powershell dotnet build ClumsyPilot\ClumsyPilot.csproj --no-restore -v:minimal powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_image.ps1 ``` Expected: the image test fails because the package, deployment target, `using System.Drawing`, and renderer still exist. - [ ] **Step 3: Remove the drawing package and deployment target** Delete the `System.Drawing.Common` `PackageReference` and the entire `DeployFrameworkDrawingRuntime` target. Add ``. Do not change the target framework or other references. Ensure a clean build cannot retain the old DLL: the verification command must remove only `ClumsyPilot/bin/Debug/netstandard2.0/System.Drawing.Common.dll` before rebuilding, after resolving and validating that exact path is under the project output directory. - [ ] **Step 4: Implement the RGBA raster surface** Replace drawing types with a private surface backed by `byte[]` in RGBA order. Required primitives and semantics: ```csharp SetPixel(int x, int y, byte r, byte g, byte b, byte a = 255); FillRectangle(int x, int y, int width, int height, Color32 color); DrawLine(int x0, int y0, int x1, int y1, Color32 color, int thickness); DrawRectangle(int x, int y, int width, int height, Color32 color, int thickness); DrawCircle(int centerX, int centerY, int radius, Color32 color, int thickness); FillCircle(int centerX, int centerY, int radius, Color32 color); FillPolygon(PointD[] points, Color32 color); ``` Clip every primitive to the surface. Use pre-clipped Bresenham lines, scale-normalized scanline polygon filling, and a canvas-clipped bounded circle scan whose work is proportional to visible rows/columns rather than radius. Preserve exact white, red, LightGray `(211,211,211)`, black, blue, and LimeGreen `(0,255,0)` colors. Convert vehicle/workstation world positions using the existing discrete-grid-aligned transform. - [ ] **Step 5: Implement deterministic bitmap text** Embed a private 5×7 ASCII glyph table for code points 32–126. Draw scaled glyphs using integer pixels; unsupported characters render as `?`. Use a 2× scale for header text and 1× scale for the workstation label. Keep all five header baselines inside `HeaderHeightPixels=140` with fixed non-overlapping line boxes. Continue building the same five metadata lines, including occupancy rate; sanitize only the exported header text, not logs. - [ ] **Step 6: Encode PNG and add 300 DPI metadata** Use `StbImageWriteSharp.ImageWriter.WritePng` to encode the RGBA buffer. Then insert: ```text pHYs: X=11811, Y=11811, unit=1 ``` Encode into a temporary `MemoryStream`, validate the PNG signature and first `IHDR` chunk, then copy the signature+IHDR, append the 13-byte `pHYs` chunk, and copy the remaining encoded chunks. Use big-endian integers and standard CRC-32 over `pHYs`+data. Do not close the caller-owned reserved stream before the existing file-size/atomic-move flow finishes. - [ ] **Step 7: Update documentation and run GREEN verification** Remove all claims that TrapMap deploys or requires `System.Drawing.Common`; document the pure C# encoder and BCL-only runtime. Run from a clean output state: ```powershell powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_inputs.ps1 dotnet build ClumsyPilot\ClumsyPilot.csproj --no-restore -v:minimal powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_grid.ps1 powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_lifecycle.ps1 powershell -ExecutionPolicy Bypass -File ClumsyPilot\tests\verify_trapmap_image.ps1 git diff --check $staged = git diff --cached --name-only; if ($staged) { throw "Unexpected staged files: $staged" } ``` Expected: build succeeds without a drawing DLL in output; all tests pass; PNG parser reports correct dimensions, CRCs, 300 DPI metadata, RGBA pixels, unique filenames, file size at or below 50 MiB, and no temporary files.