# Inspect transparent sprite edges and compare PNG/JPEG exports

This original runnable project supports two distinct jobs: diagnose an edge-color halo without confusing it with smoothing (T03), and choose a transparent pixel-art export based on actual decoded PNG/JPEG output (T09). The source is one 16 × 16 CC0 character, with partial-alpha boundary pixels, opaque detail and transparent padding. No uploaded image, remote script, installation or account is required.

Extract the downloadable ZIP and open `index.html`. The fixture data is embedded locally so the canvas example also runs from a `file:` URL. The source PNGs remain in `assets/` for inspection. The page creates actual browser PNG and JPEG downloads, then decodes them to display measured differences. For reproducibility from the QuickUpscale repository:

```sh
node --test examples/tutorials/png-diagnostics/fixture.test.mjs
node examples/tutorials/png-diagnostics/generate.mjs
node examples/tutorials/png-diagnostics/generate.mjs --check
```

Generated `assets/` and `dist/` are ignored caches. The generator uses tracked original source and the existing PNG/stored-ZIP helpers; repeated equal generation does not rewrite files. The ZIP includes HTML, CSS, JavaScript, three source PNGs, locally embedded PNG data, their hash manifest, this README and the CC0 license.

## T03: diagnose white or dark edge halos

**Start:** a transparent sprite looks too pale on dark backgrounds, or dark around the boundary on a light background. **Desired result:** identify whether the wrong color is already in partial-alpha source pixels or is introduced by resampling before choosing a correction.

1. Inspect the clean, white-matte and dark-matte source variants over both black and white in the first grid. All previews use exactly the same 8× nearest-neighbor enlargement, so smoothing cannot explain differences between these six panels.
2. Inspect source pixel `(3, 5)`. Its intended RGBA is `(224, 80, 48, 128)`. The white-matte variant has `(239, 167, 151, 128)`; the dark-matte variant has `(112, 40, 24, 128)`. Their alpha is identical. Only partial-alpha RGB has been changed, simulating color already mixed with a previous matte while alpha was incorrectly retained.
3. Compare the white-matte variant on black and the dark-matte variant on white. The pale/dark edge follows the altered source color. Normal source-over compositing mixes the source color with the current background again. For an opaque destination, the conceptual relationship is `out = source × alpha + background × (1 − alpha)`, with channel conversion and rounding determined by the renderer.
4. Compare the separate nearest/smoothed pair. Both use the clean input; only `imageSmoothingEnabled` changes. Smoothing introduces interpolated boundary samples. Turning smoothing off preserves hard pixel blocks, but cannot repair an already contaminated edge color.
5. Correct the source export or alpha/color handling where the contamination was introduced. Export again from an unflattened source with the intended edge RGB and alpha. For deliberately hard pixel art, disable unwanted resampling. Reinspect both dark and light backgrounds after changing the actual cause.

**Important boundary:** a semitransparent pixel is not automatically a defect. The clean source deliberately has soft-alpha edges; forcing every alpha value to 0 or 255 would change the artwork. Fully transparent RGB cannot be diagnosed by visible compositing alone. This fixture includes invisible magenta at `(0, 0)` to expose the separate decoded-canvas boundary; the browser may discard it. This is a controlled mechanism demonstration, not an automatic universal halo fixer. Texture filtering, atlas bleed, premultiplied/straight-alpha mismatches and color management can produce other edge problems.

## T09: choose PNG over JPEG for this transparent sprite

**Start:** a crisp, transparent sprite needs a reusable image export. **Desired result:** retain alpha and pixel edges, while understanding why a JPEG converted to PNG remains damaged.

1. Open the same clean source used above. Click **Re-export locally** if needed. The transparent PNG retains an alpha channel; compare it against the checkerboard behind the preview.
2. For an isolated codec comparison, the demo first draws that same sprite onto an explicit white matte. It exports this identical opaque canvas as PNG and JPEG. JPEG uses `canvas.toBlob(..., "image/jpeg", 0.92)`, with no resize. The white-matte PNG is the reference, so alpha flattening is separated from JPEG's lossy RGB changes.
3. Download both outputs and compare the decoded pixels at their original 16 × 16 dimensions. The page reports the number of changed pixels, maximum channel error and RGB root-mean-square error. Do not use compressed byte size or the enlarged preview alone as evidence of pixel equality.
4. The demo decodes the JPEG, draws its decoded image to a new canvas and saves PNG. Compare **JPEG → PNG** against both the decoded JPEG and the opaque reference. Saving the damaged JPEG pixels losslessly cannot reconstruct the original detail or transparency. Return to the original PNG or source artwork to recover the intended export.

**Important boundary:** JPEG quality 0.92 is a browser encoder request, not a cross-encoder guarantee. A solid flat opaque image might show little or no pixel change at some settings. This original fixture deliberately includes saturated edges and fine detail; its observed numbers apply only to this fixture, version and encoder. JPEG is useful for other image types; this example establishes why it fails this transparent pixel-art job. It also does not claim that every PNG from every canvas retains invisible original RGB or arbitrary color profiles.

## Executed results and limits

Executed in ego-browser Chromium 154.0.8037.44 on macOS, with an sRGB canvas and no export resizing. Actual browser downloads were independently decoded with Pillow 12.3.0 / libjpeg-turbo 3.1.4.1. The JPEG requested quality 0.92 on the same explicit white-matte surface as the opaque PNG reference.

| Comparison | Changed pixels | Maximum channel error | RGB RMSE |
|---|---:|---:|---:|
| Opaque PNG versus input canvas | 0 / 256 | 0 | 0 |
| JPEG versus opaque PNG reference | 253 / 256 | 184 | 24.8656 |
| JPEG resaved as PNG versus decoded JPEG | 0 / 256 | 0 | 0 |
| JPEG resaved as PNG versus opaque reference | 253 / 256 | 184 | 24.8656 |

The PNG retained alpha values 0, 128 and 255; JPEG and its PNG resave were fully opaque. Independent decoding also found a real browser boundary: 32 partial-alpha source pixels changed red from 224 to 223, and the single invisible magenta pixel became transparent black. These are 33 source-file RGBA differences in the transparent browser export, even though export versus its decoded canvas surface had zero differences. The visible soft edge remains present; the example does not promise preservation of hidden source-file RGB.

At source coordinate `(3, 5)`, the clean and white-contaminated variants render over black as `(112, 40, 24)` and `(120, 84, 76)` respectively. Over white, the clean and dark-contaminated variants render as `(239, 167, 151)` and `(183, 147, 139)`. Thus the halo persists with smoothing disabled. Separately, enabling smoothing on the clean 8× preview changed 6,292 / 16,384 displayed pixels in this browser.

Exported files, independent decoded measurements and screenshots are recorded in the dated T03/T09 proof document. The on-page measurements are decoded browser/canvas values. Canvas may round partial-alpha channels or erase alpha-zero RGB because of its internal alpha/color representation. The PNG codec is lossless for the pixels it receives; that does not make a browser decode/draw/export boundary a universal source-file-byte-preserving operation.

## Sources and reuse

- [MDN getImageData](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getImageData): decoded RGBA, color space and pixel access (catalog R03).
- [MDN imageSmoothingEnabled](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled): independent resizing control.
- [MDN toBlob](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob): actual export type/quality and failure behavior.
- [W3C PNG transparency](https://www.w3.org/TR/png-3/#6AlphaRepresentation): PNG alpha is not premultiplied with color.
- [W3C Compositing and Blending](https://www.w3.org/TR/compositing-1/#simplealphacompositing): source-over alpha compositing.

E10 and E03 are the catalog's broader alpha-bound/sheet-export task evidence. They do not establish this project's exact halo mechanism or JPEG differences; the primary platform sources and executed fixture do. No search-demand or uniqueness claim follows from this example.

This sample and original artwork are CC0; see `LICENSE.txt`.
