pixelkiln 0.86.3 → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROVIDERS.md +10 -10
- package/README.md +4 -4
- package/dist/cli.js +144 -18
- package/dist/cli.js.map +1 -1
- package/dist/client/gallery.js +3 -3
- package/dist/index.cjs +113 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +116 -28
- package/dist/index.d.ts +116 -28
- package/dist/index.js +107 -16
- package/dist/index.js.map +1 -1
- package/docs/CHARACTERS.md +2 -2
- package/docs/CLI.md +36 -5
- package/docs/ENDPOINTS.md +88 -11
- package/docs/GENERATORS.md +12 -12
- package/docs/MANIFEST.md +1 -1
- package/docs/PIXELLAB.md +6 -6
- package/docs/REVISIONS.md +2 -2
- package/package.json +1 -1
- package/skills/pixelkiln/SKILL.md +5 -2
- package/skills/pixelkiln/references/pixellab-roadmap.md +30 -4
- package/skills/pixelkiln/references/pixellab.md +18 -13
- package/skills/pixelkiln/references/revisions.md +2 -2
package/PROVIDERS.md
CHANGED
|
@@ -134,7 +134,7 @@ background. That distinction matters more than raw canvas size.
|
|
|
134
134
|
|
|
135
135
|
| Asset type | PixelLab | Retro Diffusion | ComfyUI | Scenario |
|
|
136
136
|
|---|---|---|---|---|
|
|
137
|
-
| Isolated house, building, mountain, or landmark | Start with `map`: arbitrary dimensions up to 400×400 and a measured one-generation cost. Live benchmark outputs had opaque backgrounds, so plan for cleanup. Use `1dir` only when references or candidate variety justify
|
|
137
|
+
| Isolated house, building, mountain, or landmark | Start with `map`: arbitrary dimensions up to 400×400 and a measured one-generation cost. Live benchmark outputs had opaque backgrounds, so plan for cleanup. Use `1dir` only when references or candidate variety justify 10–25 generations and a square canvas. | Start with `rd_plus__topdown_asset`, `rd_plus__isometric_asset`, or `rd_tile__scene_object`, depending on perspective. `rd_tile__scene_object` is intended for 64–384px objects placed on tile maps. | Choose a checkpoint or LoRA trained for the intended perspective, then keep background removal or segmentation in the workflow. For the tested Pixel Art XL stack, target 48–128px native components even though the adapter accepts larger working canvases. | Raw model output is opaque and anti-aliased (the BFL smoke keep used 19,619 colors), but a chain of render at four times the size, Pixelate to the target grid, Birefnet, and `refine` took a 960 px render of an alpine lodge to a 240×208 native grid at high confidence with 63 colors, for 18 CU (a 1 CU render can be nearly as good). Details and the Pixelate grid caveat are in [Set up Scenario](./docs/SCENARIO.md#large-and-illustrative-art-render-big-pixelate-refine). One sample per subject. |
|
|
138
138
|
| Full scenic background | Use `pixflux` with `noBackground: false` when an exact palette matters, or `map` for a simple scene; both top out at 400×400. `imagePro` reaches 792 wide or 688 tall for a flat 40 generations. | `rd_plus__environment` targets one-point-perspective scenes; `rd_plus__topdown_map` targets 3/4 top-down maps. These styles support up to 384×384. | Use composition controls only to establish the scene. Recover and review native components, then compose them at 1× with one grid and palette. A large model canvas is not a large native pixel-art canvas. | GPT Image 2 at four times the target size, then Pixelate, turned a course-cover brief into a convincing pixel scene with 31 colors and legible title text. It has not been put through the shared benchmark. A large model canvas is not native pixel resolution: pixelate to the grid you need and check it with `refine`. |
|
|
139
139
|
| Style consistency across a set | `1dir` accepts a style reference and returns size-dependent candidates, but it is more expensive and capped at the square-object range. | RD Pro accepts up to nine references and has stronger prompt following, but its common styles top out at 256×256 and cost $0.18 per image. Environment-specific RD Plus styles trade references for a larger 384px canvas. | LoRAs, reference adapters, ControlNet, and shared latent settings can live in the committed workflow. Reproducibility also depends on external model and custom-node versions. | Scenario's project models and LoRAs are the main reason to use it for a set. PixelKiln can pin the model ID and parameters, but its first live run covers only the public BFL profile. |
|
|
140
140
|
| Very large final scene | Generate reusable objects, terrain, and background layers separately; assemble them deterministically and integer-upscale the result. | Use the same layered approach. The API has a 512px overall ceiling, but the useful environment and scene-object styles currently cap at 384px. | The graph can tile, upscale, or composite beyond hosted-provider limits, but memory and seam quality become workflow concerns. Prefer reusable layers unless the scene truly needs one render. | Scenario can request a larger raster from a compatible model, but the same rule applies: generate reusable layers at their useful native detail, compose at 1×, and integer-upscale only the final scene. |
|
|
@@ -206,20 +206,20 @@ conversion to dollars:
|
|
|
206
206
|
|---|---:|
|
|
207
207
|
| `map` | 1 generation |
|
|
208
208
|
| `pixflux` | 1 generation |
|
|
209
|
-
| `1dir` |
|
|
210
|
-
| `imagePro` |
|
|
209
|
+
| `1dir` | 10–25 generations |
|
|
210
|
+
| `imagePro` | 10–25 generations by canvas (a flat 40 before October 2026) |
|
|
211
211
|
| `imageProFlash` | 5–9 generations (provisional quote) |
|
|
212
|
-
| `tiles` |
|
|
213
|
-
| `terrain` |
|
|
212
|
+
| `tiles` | 10–25 generations |
|
|
213
|
+
| `terrain` | 10–25 generations (unmeasured) |
|
|
214
214
|
| `isometricTile` | 1 generation (measured once) |
|
|
215
|
-
| `character` base | 1 (standard), 2–9 (v3),
|
|
216
|
-
| `character` state or portrait |
|
|
217
|
-
| `character` animation | 1 (template), 2–4 (`skeleton-v3`, budgeted at 4), 1 at 64px from text,
|
|
215
|
+
| `character` base | 1 (standard), 2–9 (v3), 10–25 (pro), 6–17 (pro-flash; rotations only from a reference) |
|
|
216
|
+
| `character` state or portrait | 10–25 generations |
|
|
217
|
+
| `character` animation | 1 (template), 2–4 (`skeleton-v3`, budgeted at 4), 1 at 64px from text, 10–25 (pro) |
|
|
218
218
|
| `character` outfit | 20 generations (one 2-frame, 92×92 job) |
|
|
219
219
|
| `objectPro` base | 6 at 64px (unmeasured) |
|
|
220
220
|
| `uiAsset` | 20 generations (measured once, at 256×192) |
|
|
221
|
-
| `uiElement` |
|
|
222
|
-
| `image-to-image` / `inpaint` revision |
|
|
221
|
+
| `uiElement` | 10–25 generations (unmeasured) |
|
|
222
|
+
| `image-to-image` / `inpaint` revision | 10–25 generations; 5–9 with `engine: "pro-flash"` (provisional quote) |
|
|
223
223
|
| `reduce-colors` / `correct-pixelart` revision | 0.1 generations (measured at 32×32) |
|
|
224
224
|
|
|
225
225
|
Retro Diffusion publishes USD formulas and fixed prices. Examples relevant to
|
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ provenance and no long-lived npm token.
|
|
|
55
55
|
| Hand edits | Touch-ups in your own editor or a pinned in-browser Pixelorama, kept beside the generated art with the generation still the record; frame and tile sets member by member. |
|
|
56
56
|
| Controlled inputs | Hashed revision lineage (image-to-image and inpaint; on PixelLab also palette reduction, pixel cleanup, animation, skeleton posing, interpolation, and animation edits), fail-closed parent approval, source-versus-candidate review, and content-addressed per-asset ComfyUI bindings. |
|
|
57
57
|
| Characters | A base in 4 or 8 directions from a prompt or your own sprite (four PixelLab engines), poses, loops, portraits, and outfit transfers as dependent assets generated in waves, free mirrored directions (one `directions` list declares a loop and mirrors the rest), account adoption and salvage, and SpriteFrames export. |
|
|
58
|
-
| PixelLab utilities | `unzoom` returns upscaled reference art to its native grid, `font` generates a `.ttf` and glyph atlas, `estimate-skeleton` starts a keypoints file for skeleton animation, and `skeleton-preview` draws its poses over the sprite for free. |
|
|
58
|
+
| PixelLab utilities | `unzoom` returns upscaled reference art to its native grid, `pixelate` turns a photo or render into pixel art (Image to Pixel Art Pro Flash, 6 generations), `font` generates a `.ttf` and glyph atlas, `estimate-skeleton` starts a keypoints file for skeleton animation, and `skeleton-preview` draws its poses over the sprite for free. |
|
|
59
59
|
| Existing-art onboarding | Manifest scaffolding, exact-hash account adoption, and prompt recovery. |
|
|
60
60
|
| Recovery | Safe stale-output replacement, validated caches, durable references, resumable paid jobs, and per-asset generation history with free restores. |
|
|
61
61
|
| Shared-account safety | Cross-project claim files or a registered workspace catalog, sibling-style exclusion, reviewed salvage, keep/discard tags, separate confirmed purge. |
|
|
@@ -255,9 +255,9 @@ Measured PixelLab economics vary by 40×:
|
|
|
255
255
|
|---|---|---:|
|
|
256
256
|
| Standalone arbitrary-size prop/icon | `map` (default) | 1 generation |
|
|
257
257
|
| Exact closed palette | `pixflux` | 1 generation |
|
|
258
|
-
| Candidate variety/reference anchoring/future animation | `1dir` |
|
|
259
|
-
| Independent or connectable ground tiles | `tiles` |
|
|
260
|
-
| A character in 4 or 8 directions, its poses, its loops | `character` | 1 per base,
|
|
258
|
+
| Candidate variety/reference anchoring/future animation | `1dir` | 10–25 generations |
|
|
259
|
+
| Independent or connectable ground tiles | `tiles` | 10–25 generations |
|
|
260
|
+
| A character in 4 or 8 directions, its poses, its loops | `character` | 1 per base, 10–25 per pose, 1 per template loop per direction; a `mirror` of a loop is free |
|
|
261
261
|
|
|
262
262
|
Start with the required capability, not the most expensive endpoint. Forty `map`
|
|
263
263
|
re-rolls cost the same as one 64×64 `1dir` call; conversely, `map` cannot replace a
|
package/dist/cli.js
CHANGED
|
@@ -162,6 +162,7 @@ var COMMANDS = [
|
|
|
162
162
|
"estimate-skeleton",
|
|
163
163
|
"skeleton-preview",
|
|
164
164
|
"unzoom",
|
|
165
|
+
"pixelate",
|
|
165
166
|
"font",
|
|
166
167
|
"help",
|
|
167
168
|
"--help",
|
|
@@ -499,8 +500,12 @@ Commands
|
|
|
499
500
|
add/remove/list/status/claims. Offline.
|
|
500
501
|
unzoom PixelLab: shrink an upscaled image (--from) back to its native
|
|
501
502
|
pixel grid before using it as a style or reference image.
|
|
503
|
+
pixelate PixelLab: convert a photo, painting, or render (--from) into
|
|
504
|
+
pixel art with Image to Pixel Art Pro Flash; PixelLab picks the
|
|
505
|
+
output size. Optional --description steers the style. 6
|
|
506
|
+
generations; asks first.
|
|
502
507
|
font PixelLab: generate a pixel font (--description, --out); writes a
|
|
503
|
-
.ttf and a glyph atlas PNG.
|
|
508
|
+
.ttf and a glyph atlas PNG. 15 generations; asks first.
|
|
504
509
|
|
|
505
510
|
Options
|
|
506
511
|
--columns <n> pack/export: sprites or tiles per row (default: near-square)
|
|
@@ -523,7 +528,8 @@ Options
|
|
|
523
528
|
--model-root <dir> recipe verify: also hash required local model files
|
|
524
529
|
--generation <n|hash> restore: the previous generation to bring back (1 = newest)
|
|
525
530
|
--quantize <n> unzoom: 0 auto palette (default), -1 keep all colors, 2-256 exact
|
|
526
|
-
--description <text> font: the style to draw, e.g. "warm orange arcade font"
|
|
531
|
+
--description <text> font: the style to draw, e.g. "warm orange arcade font";
|
|
532
|
+
pixelate: an optional style steer, e.g. "light dithering"
|
|
527
533
|
--weight <w> font: Bold or Regular (default)
|
|
528
534
|
--glyph-px <n> font: native glyph size, 8, 16 (default), 32, or 64
|
|
529
535
|
--prune cache: remove invalid/unreferenced local cache data
|
|
@@ -583,6 +589,7 @@ Examples
|
|
|
583
589
|
pixelkiln quality snapshot --inputs quality-inputs.json --out pixelkiln.quality.json
|
|
584
590
|
pixelkiln quality check --from pixelkiln.quality.json
|
|
585
591
|
pixelkiln unzoom --from refs/knight-512.png --out refs/knight.png
|
|
592
|
+
pixelkiln pixelate --from renders/lodge.png --out art/lodge.png
|
|
586
593
|
pixelkiln font --description "warm orange arcade font" --weight Bold --out fonts/arcade
|
|
587
594
|
pixelkiln workspace add ../other-game/pixelkiln.manifest.json
|
|
588
595
|
pixelkiln workspace status --json
|
|
@@ -849,17 +856,24 @@ function candidateCount(size) {
|
|
|
849
856
|
}
|
|
850
857
|
function generationCost(width, height, generator = "map") {
|
|
851
858
|
if (generator === "map" || generator === "pixflux") return 1;
|
|
852
|
-
if (generator === "imagePro") return
|
|
859
|
+
if (generator === "imagePro") return proTierCost(width, height);
|
|
853
860
|
const px = width * height;
|
|
854
|
-
if (px <= 1024) return
|
|
855
|
-
if (px <= 2048) return
|
|
856
|
-
return
|
|
861
|
+
if (px <= 1024) return PRO_TIER_PRICES[0];
|
|
862
|
+
if (px <= 2048) return PRO_TIER_PRICES[1];
|
|
863
|
+
return PRO_TIER_PRICES[2];
|
|
864
|
+
}
|
|
865
|
+
var PRO_TIER_PRICES = [10, 15, 25];
|
|
866
|
+
function proTierCost(width, height) {
|
|
867
|
+
const px = width * height;
|
|
868
|
+
if (px <= 65536) return PRO_TIER_PRICES[0];
|
|
869
|
+
if (px <= 102400) return PRO_TIER_PRICES[1];
|
|
870
|
+
return PRO_TIER_PRICES[2];
|
|
857
871
|
}
|
|
858
872
|
function tilesCost(tileSize, variations) {
|
|
859
873
|
const px = tileSize * tileSize * Math.max(1, variations);
|
|
860
|
-
if (px <= 1024) return
|
|
861
|
-
if (px <= 2048) return
|
|
862
|
-
return
|
|
874
|
+
if (px <= 1024) return PRO_TIER_PRICES[0];
|
|
875
|
+
if (px <= 2048) return PRO_TIER_PRICES[1];
|
|
876
|
+
return PRO_TIER_PRICES[2];
|
|
863
877
|
}
|
|
864
878
|
function isometricTileCost() {
|
|
865
879
|
return 1;
|
|
@@ -1375,7 +1389,7 @@ var MEMBER_SET_REVISION_MODES = ["reduce-colors", "correct-pixelart", "edit-anim
|
|
|
1375
1389
|
var StyleObjectSchema = z.object({
|
|
1376
1390
|
/** Generation backend for this style. Omit to inherit the manifest default. */
|
|
1377
1391
|
provider: z.string().min(1).optional(),
|
|
1378
|
-
// `map` is the default because it is
|
|
1392
|
+
// `map` is the default because it is 10-25x cheaper and correct for any
|
|
1379
1393
|
// asset that is not going to be rotated or animated.
|
|
1380
1394
|
generator: GeneratorSchema.default("map"),
|
|
1381
1395
|
/** Square edge length. The active provider owns its exact limit. */
|
|
@@ -6820,7 +6834,8 @@ var PixelLabClient = class {
|
|
|
6820
6834
|
* A full-body sprite in, a bust portrait out (or the reverse, unused
|
|
6821
6835
|
* today). Job-based like a state or `1dir`, not the couple-generations
|
|
6822
6836
|
* "conversion" the name suggests: the smallest `resultSize` (16px) billed
|
|
6823
|
-
* 20 generations live (docs/ENDPOINTS.md, "Characters, measured")
|
|
6837
|
+
* 20 generations live (docs/ENDPOINTS.md, "Characters, measured"), the
|
|
6838
|
+
* Pro floor before PixelLab's October 2026 cut put it at 10.
|
|
6824
6839
|
*/
|
|
6825
6840
|
async createPortraitCharacterPro(args) {
|
|
6826
6841
|
const raw = await this.request("/portrait-character-pro", {
|
|
@@ -6859,7 +6874,8 @@ var PixelLabClient = class {
|
|
|
6859
6874
|
* everything else here, but the completed job returns the result frames
|
|
6860
6875
|
* inline as base64 (`last_response.quantized_images`), not as storage
|
|
6861
6876
|
* URLs — the one PixelLab character tool that works this way. A live
|
|
6862
|
-
* 2-frame, 92x92 job billed 20 generations (docs/ENDPOINTS.md)
|
|
6877
|
+
* 2-frame, 92x92 job billed 20 generations (docs/ENDPOINTS.md), before
|
|
6878
|
+
* PixelLab's October 2026 Pro cut halved that floor to 10.
|
|
6863
6879
|
*/
|
|
6864
6880
|
async transferOutfitV2(args) {
|
|
6865
6881
|
const image = (img) => ({
|
|
@@ -6909,7 +6925,9 @@ var PixelLabClient = class {
|
|
|
6909
6925
|
* far higher than this tiering assumes; 352x352 (123904px²), 384x384
|
|
6910
6926
|
* (147456px²), and 512x512 (262144px², the tiering ceiling) all billed
|
|
6911
6927
|
* 40. Both breakpoints are now tightly bracketed: 20->25 in
|
|
6912
|
-
* (65536px², 82944px²], 25->40 in (102400px², 123904px²].
|
|
6928
|
+
* (65536px², 82944px²], 25->40 in (102400px², 123904px²]. Those prices
|
|
6929
|
+
* predate PixelLab's October 2026 Pro cut to 10/15/25; `proTierCost`
|
|
6930
|
+
* keeps the breakpoints with the new prices.
|
|
6913
6931
|
* `editImagesV2` below returns the same fields under `images`, plural
|
|
6914
6932
|
* and array-wrapped, not `image` — do not assume the two endpoints share
|
|
6915
6933
|
* one response shape. See docs/ENDPOINTS.md and docs/REVISIONS.md for
|
|
@@ -6962,8 +6980,9 @@ var PixelLabClient = class {
|
|
|
6962
6980
|
);
|
|
6963
6981
|
}
|
|
6964
6982
|
/**
|
|
6965
|
-
* PixelLab's Pro image tier, `/generate-image-v2`:
|
|
6966
|
-
*
|
|
6983
|
+
* PixelLab's Pro image tier, `/generate-image-v2`: 10 to 25 generations by
|
|
6984
|
+
* canvas since PixelLab's October 2026 Pro cut, a flat 40 before
|
|
6985
|
+
* (docs/ENDPOINTS.md, "Single-image generators, measured"), for real style
|
|
6967
6986
|
* transfer and non-square canvases up to 792x688, where `pixflux` is
|
|
6968
6987
|
* limited to 400x400 and no style reference. Like a revision, this hands
|
|
6969
6988
|
* back a plain background job with no resource of its own; unlike a
|
|
@@ -7049,6 +7068,24 @@ var PixelLabClient = class {
|
|
|
7049
7068
|
"inpaint-image-pro-flash"
|
|
7050
7069
|
);
|
|
7051
7070
|
}
|
|
7071
|
+
/**
|
|
7072
|
+
* `/image-to-pixelart-pro-flash`: a photo, painting, or render in, pixel
|
|
7073
|
+
* art out, with no size to pass: PixelLab detects the native pixel scale
|
|
7074
|
+
* and picks the output size (a 256x256 render came back 108x109, on a
|
|
7075
|
+
* transparent background). A flat 6 generations, billed exactly that live
|
|
7076
|
+
* in October 2026. Sources over 2048px on the longer side are scaled down
|
|
7077
|
+
* upstream. The finished job carries the image at `last_response.images[0]`.
|
|
7078
|
+
*/
|
|
7079
|
+
async imageToPixelartProFlash(args) {
|
|
7080
|
+
const body = { image: args.image };
|
|
7081
|
+
if (args.description) body.description = args.description;
|
|
7082
|
+
if (args.seed != null) body.seed = args.seed;
|
|
7083
|
+
return validateResponse(
|
|
7084
|
+
ProFlashImageSubmitSchema,
|
|
7085
|
+
await this.request("/image-to-pixelart-pro-flash", { method: "POST", body: JSON.stringify(body) }),
|
|
7086
|
+
"image-to-pixelart-pro-flash"
|
|
7087
|
+
);
|
|
7088
|
+
}
|
|
7052
7089
|
/**
|
|
7053
7090
|
* `/generate-ui-v2` ("Generate UI (Pro)"): one UI element (a button, a
|
|
7054
7091
|
* health bar, an inventory slot, a dialogue box) from a description,
|
|
@@ -7443,7 +7480,7 @@ function characterCost(spec) {
|
|
|
7443
7480
|
}
|
|
7444
7481
|
if (character.kind === "state" || character.kind === "portrait") return generationCost(spec.width, spec.height, "1dir");
|
|
7445
7482
|
if (character.kind === "outfit") {
|
|
7446
|
-
return
|
|
7483
|
+
return PRO_TIER_PRICES[0];
|
|
7447
7484
|
}
|
|
7448
7485
|
if (character.mode === "standard") return 1;
|
|
7449
7486
|
if (character.mode === "v3") {
|
|
@@ -7695,7 +7732,7 @@ var PixelLabProvider = class _PixelLabProvider {
|
|
|
7695
7732
|
if (spec.revision) {
|
|
7696
7733
|
const width = spec.revision.sourceWidth ?? spec.width;
|
|
7697
7734
|
const height = spec.revision.sourceHeight ?? spec.height;
|
|
7698
|
-
return { unit: "generations", amount:
|
|
7735
|
+
return { unit: "generations", amount: proTierCost(width, height), candidates: 1 };
|
|
7699
7736
|
}
|
|
7700
7737
|
if (spec.generator === "tiles" || spec.generator === "terrain" || spec.generator === "isometricTile") {
|
|
7701
7738
|
return { unit: "generations", amount: spec.cost, candidates: spec.candidates };
|
|
@@ -20667,7 +20704,7 @@ async function unzoomFile(client2, input, opts = {}) {
|
|
|
20667
20704
|
}
|
|
20668
20705
|
var FONT_WEIGHTS = ["Bold", "Regular"];
|
|
20669
20706
|
var FONT_GLYPH_SIZES = [8, 16, 32, 64];
|
|
20670
|
-
var FONT_COST_GENERATIONS =
|
|
20707
|
+
var FONT_COST_GENERATIONS = 15;
|
|
20671
20708
|
function fontOutputPaths(out) {
|
|
20672
20709
|
const base = path43.resolve(out.replace(/\.(ttf|png)$/i, ""));
|
|
20673
20710
|
return { ttf: `${base}.ttf`, atlas: `${base}.png` };
|
|
@@ -20716,6 +20753,61 @@ async function generateFont(client2, opts) {
|
|
|
20716
20753
|
await sleep3(interval);
|
|
20717
20754
|
}
|
|
20718
20755
|
}
|
|
20756
|
+
var PIXELATE_COST_GENERATIONS = 6;
|
|
20757
|
+
var PIXELATE_MAX_SIDE = 2048;
|
|
20758
|
+
function defaultPixelateOut(input) {
|
|
20759
|
+
const ext = path43.extname(input);
|
|
20760
|
+
return `${ext ? input.slice(0, -ext.length) : input}.pixel.png`;
|
|
20761
|
+
}
|
|
20762
|
+
async function pixelateFile(client2, input, opts = {}) {
|
|
20763
|
+
const out = path43.resolve(opts.out ?? defaultPixelateOut(input));
|
|
20764
|
+
if (path43.resolve(input) === out) throw new Error("pixelate will not overwrite its own input; pass a different --out");
|
|
20765
|
+
if (existsSync33(out) && !opts.force) throw new Error(`${out} already exists; pass --force to replace it`);
|
|
20766
|
+
const bytes = await readFile29(input);
|
|
20767
|
+
const metadata = imageMetadata(bytes);
|
|
20768
|
+
if (!metadata) throw new Error(`${input} is not a readable PNG or JPEG`);
|
|
20769
|
+
const { width, height, format } = metadata;
|
|
20770
|
+
const sleep3 = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
20771
|
+
const interval = opts.intervalMs ?? 5e3;
|
|
20772
|
+
const timeout = opts.timeoutMs ?? 10 * 6e4;
|
|
20773
|
+
const submitted = await client2.imageToPixelartProFlash({
|
|
20774
|
+
image: { base64: bytes.toString("base64"), format },
|
|
20775
|
+
description: opts.description,
|
|
20776
|
+
seed: opts.seed
|
|
20777
|
+
});
|
|
20778
|
+
const jobId = submitted.background_job_id;
|
|
20779
|
+
const started = Date.now();
|
|
20780
|
+
for (; ; ) {
|
|
20781
|
+
const job = await client2.getBackgroundJob(jobId);
|
|
20782
|
+
opts.onProgress?.(job.status);
|
|
20783
|
+
if (job.status === "failed") throw new Error(`PixelLab pixelate job ${jobId} failed upstream`);
|
|
20784
|
+
if (job.status === "completed") {
|
|
20785
|
+
const images = job.last_response?.images;
|
|
20786
|
+
const first = Array.isArray(images) ? images[0] : void 0;
|
|
20787
|
+
if (!first || typeof first.base64 !== "string" || !first.base64) {
|
|
20788
|
+
throw new Error(`PixelLab pixelate job ${jobId} completed without an image at last_response.images[0]`);
|
|
20789
|
+
}
|
|
20790
|
+
const png = Buffer.from(first.base64, "base64");
|
|
20791
|
+
const size = imageMetadata(png);
|
|
20792
|
+
await mkdir13(path43.dirname(out), { recursive: true });
|
|
20793
|
+
await writeFile18(out, png);
|
|
20794
|
+
return {
|
|
20795
|
+
jobId,
|
|
20796
|
+
out,
|
|
20797
|
+
original: { width, height },
|
|
20798
|
+
pixelated: { width: size?.width ?? Number(first.width), height: size?.height ?? Number(first.height) },
|
|
20799
|
+
imageId: submitted.image_id,
|
|
20800
|
+
usage: job.usage ?? null
|
|
20801
|
+
};
|
|
20802
|
+
}
|
|
20803
|
+
if (Date.now() - started > timeout) {
|
|
20804
|
+
throw new Error(
|
|
20805
|
+
`PixelLab pixelate job ${jobId} is still ${job.status} after ${Math.round(timeout / 1e3)} s; it keeps running upstream, but this command has stopped waiting`
|
|
20806
|
+
);
|
|
20807
|
+
}
|
|
20808
|
+
await sleep3(interval);
|
|
20809
|
+
}
|
|
20810
|
+
}
|
|
20719
20811
|
|
|
20720
20812
|
// src/cli/commands/pixellab-utils.ts
|
|
20721
20813
|
function loadKeys(args) {
|
|
@@ -20783,6 +20875,39 @@ async function runFont(args) {
|
|
|
20783
20875
|
log(` wrote ${res.ttf}`);
|
|
20784
20876
|
log(` wrote ${res.atlas}`);
|
|
20785
20877
|
}
|
|
20878
|
+
async function runPixelate(args) {
|
|
20879
|
+
if (!args.from) throw new UsageError("pixelate needs --from <image>: the photo, painting, or render to convert");
|
|
20880
|
+
const out = args.out ?? defaultPixelateOut(args.from);
|
|
20881
|
+
log(` pixelate ${args.from} \u2192 ${out}${args.description ? ` ("${args.description}")` : ""}; PixelLab picks the output size`);
|
|
20882
|
+
log(` cost: ${PIXELATE_COST_GENERATIONS} generations (Image to Pixel Art Pro Flash, flat)`);
|
|
20883
|
+
if (args.budget !== void 0 && args.budget < PIXELATE_COST_GENERATIONS) {
|
|
20884
|
+
throw new Error(`--budget ${args.budget} is below this conversion's ${PIXELATE_COST_GENERATIONS}-generation cost`);
|
|
20885
|
+
}
|
|
20886
|
+
if (args.dryRun) {
|
|
20887
|
+
log(" dry run: nothing sent");
|
|
20888
|
+
return;
|
|
20889
|
+
}
|
|
20890
|
+
if (!await confirm(` Spend ${PIXELATE_COST_GENERATIONS} generations on this conversion?`, args.yes)) return;
|
|
20891
|
+
loadKeys(args);
|
|
20892
|
+
let last = "";
|
|
20893
|
+
const res = await pixelateFile(clientFromEnv(), args.from, {
|
|
20894
|
+
out,
|
|
20895
|
+
description: args.description,
|
|
20896
|
+
force: args.force,
|
|
20897
|
+
onProgress: (status) => {
|
|
20898
|
+
if (status !== last) log(` ${status}\u2026`);
|
|
20899
|
+
last = status;
|
|
20900
|
+
}
|
|
20901
|
+
});
|
|
20902
|
+
if (args.json) {
|
|
20903
|
+
log(JSON.stringify({ version: 1, ...res }, null, 2));
|
|
20904
|
+
return;
|
|
20905
|
+
}
|
|
20906
|
+
const scaled = Math.max(res.original.width, res.original.height) > PIXELATE_MAX_SIDE ? " (scaled down upstream first)" : "";
|
|
20907
|
+
log(
|
|
20908
|
+
` ${res.original.width}x${res.original.height}${scaled} \u2192 ${res.pixelated.width}x${res.pixelated.height}; wrote ${res.out}`
|
|
20909
|
+
);
|
|
20910
|
+
}
|
|
20786
20911
|
|
|
20787
20912
|
// src/cli/commands/quality.ts
|
|
20788
20913
|
import path46 from "path";
|
|
@@ -22215,6 +22340,7 @@ var RUNNERS = {
|
|
|
22215
22340
|
"estimate-skeleton": runEstimateSkeleton,
|
|
22216
22341
|
"skeleton-preview": runSkeletonPreview,
|
|
22217
22342
|
unzoom: runUnzoom,
|
|
22343
|
+
pixelate: runPixelate,
|
|
22218
22344
|
font: runFont
|
|
22219
22345
|
};
|
|
22220
22346
|
async function main(argv = process.argv.slice(2)) {
|