Edit Colors: convert embedded images to indexed color

Edit > Edit Colors > Convert to Indexed Color... (edit.colors.toIndexed)
stores the embedded images in the selection as indexed-color PNGs: a
palette of at most 256 RGB colors, each pixel the index of one. The
dialog previews live and OK is one undo step.

- The palette is chosen as for a PNG-8 export (selective, perceptual,
  adaptive, web, gray, black and white), as evenly spaced levels of red,
  green and blue (uniform), or from the document's swatches. Dithering
  is diffusion, pattern, noise or none, with an amount.
- Pixels under half opacity become one transparent entry; the others
  become opaque in their own color. Linked images, colorized images and
  color glyph images keep their pixels.
- An image that is already an indexed PNG and would show the same pixels
  is left as it is, and no undo step is recorded when no image changes.
- An image of more than 134,217,728 pixels (33,554,432 in the web app)
  is refused, read from its header before decoding.
- With Select All, locked and hidden objects that hold an image that is
  not an indexed PNG yet are counted in notChanged, without converting
  them.
- The quantizer gains quantize_to, which maps pixels to a given palette.

Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-11-13 09:49:00 -08:00
parent f520e6f8c5
commit 81d19f1718
44 changed files with 770 additions and 23 deletions
+26 -5
View File
@@ -3763,11 +3763,32 @@ with one gray channel, so a PDF writes it in gray.
`edit.colors.saturate` needs `intensity`, a number from -100 to 100 (a number outside the range counts as its nearest
end). Without one, or with `null`, it answers `` missing number `intensity` ``; with a value that is not a number, such as
`"40"`, it answers `` `intensity` must be a number, not "40" ``. Either way it changes nothing.
The `edit.colors.*` commands, `edit.colors.overprintBlack` and `addon.color.filter` answer a `fill`, `stroke`,
`includeImages`, `includePatterns`, `convert`, `remove`, `includeCmyBlacks` or `includeSpotBlacks` that is not true or
false with `` `fill` must be true or false ``, naming the key, a `percentage` that is not a number with
`` `percentage` must be a number ``, and a `mode` that is not a string with `` `mode` must be a string ``. They change
nothing and record no undo step. A null counts as a key left out.
The `edit.colors.*` commands other than `edit.colors.toIndexed`, `edit.colors.overprintBlack` and
`addon.color.filter` answer a `fill`, `stroke`, `includeImages`, `includePatterns`, `convert`, `remove`,
`includeCmyBlacks` or `includeSpotBlacks` that is not true or false with `` `fill` must be true or false ``, naming the
key, a `percentage` that is not a number with `` `percentage` must be a number ``, and a `mode` that is not a string
with `` `mode` must be a string ``. They change nothing and record no undo step. A null counts as a key left out.
`edit.colors.toIndexed` (Edit › Edit Colors › Convert to Indexed Color…) stores each embedded image in the selection (or
`ids`), in groups too, as an indexed-color PNG: a palette of at most 256 RGB colors, with each pixel the index of one.
`reduction` chooses the palette: `selective` (the default), `perceptual`, `adaptive`, `web`, `gray` and `blackWhite`
choose it as a PNG-8 export does; `uniform` takes evenly spaced levels of red, green and blue (2 to 6 levels, the most
whose cube fits in `colors` with one entry left for transparency); `document` takes the colors of the document's
swatches (at most 255). `colors` (2 to 256, default 256) is the most palette entries, the transparent one included;
`uniform` needs at least 9, and `blackWhite` and `document` refuse it. `dither` is `diffusion` (the default), `pattern`,
`noise` or `none`, and `ditherAmount` is 0 to 100 (default 100). Reduction and dither ids are read as a PNG-8 export
reads them, ignoring case. Pixels under half opacity become one transparent entry and the others become opaque in their
own color. Linked images, colorized image fills and the images of color glyphs in outlined type keep their pixels. Each
image that changes becomes a new image, in one undo step for all of them. An image that is already an indexed PNG and
would show the same pixels is left as it is, and no undo step is recorded when no image changes. `notChanged` counts the
locked and hidden objects Select All left out that hold an image that is not an indexed PNG yet. An image of more than
134,217,728 pixels (33,554,432 in the web app) is refused with an error, read from the file's header before any pixel is
decoded, and nothing changes. A value out of range or of the wrong type, such as `"colors": "8"`, is refused with an
error and changes nothing.
```json
{"name":"run_command","arguments":{"command":"edit.colors.toIndexed","params":{"reduction":"adaptive","colors":32,"dither":"pattern"}}}
```
The reply of `recolor.apply` and of every command above except the Blend commands has `notChanged: {locked, hidden}`.
When the selection is exactly what `select.all` takes (in isolation mode, what it takes inside the isolated group), it