smart-downscaler 0.6.1 → 0.6.3
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/README.md +148 -31
- package/package.json +1 -5
- package/smart_downscaler.d.ts +47 -120
- package/smart_downscaler.js +216 -119
- package/smart_downscaler_bg.wasm +0 -0
package/README.md
CHANGED
|
@@ -35,7 +35,10 @@ A high-performance Rust library for intelligent image downscaling with pixel art
|
|
|
35
35
|
| **Edge-Aware Processing** | Sobel/Scharr detection preserves boundaries |
|
|
36
36
|
| **Spatial Coherence** | Neighbor and region voting for smooth results |
|
|
37
37
|
| **K-Centroid Tile Logic** | Advanced dominant color extraction per tile |
|
|
38
|
-
| **Rare-Color Preservation** | Saliency weighting + slot reservation keeps small
|
|
38
|
+
| **Rare-Color Preservation** | Saliency weighting + slot reservation keeps small important colors (lips, eyes) |
|
|
39
|
+
| **Chroma Recovery** | Restores saturation lost to color merging, by perceptual difference |
|
|
40
|
+
| **Skin-Tone Isolation** | Keeps skin and non-skin colors in separate palette domains |
|
|
41
|
+
| **Reusable Preprocessing** | Prepare an image once, downscale to many sizes cheaply |
|
|
39
42
|
| **Performance Preprocessing** | Resolution capping and color pre-quantization |
|
|
40
43
|
| **WebAssembly Support** | Full browser compatibility with near-native speed |
|
|
41
44
|
|
|
@@ -171,10 +174,12 @@ println!("Palette: {} colors", result.palette.len());
|
|
|
171
174
|
| **Tile Processing** |||||
|
|
172
175
|
| `k_centroid` | `usize` | `1` | `1`, `2`, `3`, `4` | Tile color extraction mode |
|
|
173
176
|
| `k_centroid_iterations` | `usize` | `0` | `0-10` | K-Means iterations for tile color |
|
|
174
|
-
| **Rare-Color
|
|
175
|
-
| `color_rarity` | `f32` | `0.0` | `0.0-1.0` | Damps frequency vote so
|
|
176
|
-
| `detail_boost` | `f32` | `0.0` | `0.0-2.0` | Boosts colors in detail-rich regions via
|
|
177
|
-
| `reserve_colors` | `usize` | `0` | `0 - palette_size` | Reserve N
|
|
177
|
+
| **Rare-Color & Saturation** |||||
|
|
178
|
+
| `color_rarity` | `f32` | `0.0` | `0.0-1.0` | Damps frequency vote so flat areas stop dominating (`0`=area, `1`=`count^0.5`) |
|
|
179
|
+
| `detail_boost` | `f32` | `0.0` | `0.0-2.0` | Boosts colors in detail-rich regions via local-contrast saliency (`0`=off) |
|
|
180
|
+
| `reserve_colors` | `usize` | `0` | `0 - palette_size` | Reserve N slots for distinct, important, under-represented source colors (`0`=off) |
|
|
181
|
+
| `chroma_recovery` | `f32` | `0.0` | `0.0-1.0+` | Restore chroma lost to merging toward source mean chroma, constant hue, gamut-clamped (`0`=off) |
|
|
182
|
+
| `skin_protection` | `f32` | `0.0` | `0.0-1.0` | Isolate skin vs non-skin into separate palette domains + quantization penalty (`0`=off) |
|
|
178
183
|
|
|
179
184
|
---
|
|
180
185
|
|
|
@@ -231,7 +236,7 @@ Controls how each source tile is reduced to a single representative color:
|
|
|
231
236
|
| **Foremost** | `3` | K-Means (k=3), finer dominant detection | Complex textures, detailed sprites |
|
|
232
237
|
| **Salient** | `4` | K-Means (k=2), keeps a distinctly-more-chromatic minority | Thin colorful features (lips, eyes, makeup) |
|
|
233
238
|
|
|
234
|
-
> **Note:** Mode `2` (Dominant) snaps a mixed tile to its *larger* cluster,
|
|
239
|
+
> **Note:** Mode `2` (Dominant) snaps a mixed tile to its *larger* cluster, discarding thin minority colors — a tile that is 60% skin / 40% lip becomes pure skin. For faces and artwork with small colorful features, prefer mode `4`, which keeps the colorful minority when it is non-trivial (≥22% of the tile) and clearly more chromatic than the majority.
|
|
235
240
|
|
|
236
241
|
```javascript
|
|
237
242
|
// Mode 1: Average (default) - smooth results
|
|
@@ -323,6 +328,36 @@ const result = downscale_with_palette(
|
|
|
323
328
|
|
|
324
329
|
---
|
|
325
330
|
|
|
331
|
+
### Prepare / Reuse Functions
|
|
332
|
+
|
|
333
|
+
#### `prepare(data, width, height, config?)` / `prepare_rgba(data, width, height, config?)`
|
|
334
|
+
|
|
335
|
+
Run target-independent work (resolution cap, color pre-quantization, segmentation)
|
|
336
|
+
once and return a reusable handle. `prepare` takes `Uint8Array`; `prepare_rgba` takes
|
|
337
|
+
`Uint8ClampedArray` (from canvas `getImageData`).
|
|
338
|
+
|
|
339
|
+
```javascript
|
|
340
|
+
const prepared = prepare_rgba(imageData.data, imageData.width, imageData.height, config);
|
|
341
|
+
// prepared.width / prepared.height — dimensions after any resolution cap
|
|
342
|
+
prepared.free(); // release WASM memory when finished
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### `downscale_prepared(prepared, targetWidth, targetHeight, config?)`
|
|
346
|
+
|
|
347
|
+
Downscale a prepared image. The optional `config` overrides per-target knobs
|
|
348
|
+
(`palette_size`, `k_centroid`, `chroma_recovery`, `skin_protection`, …); preprocess
|
|
349
|
+
and segmentation settings always come from prepare time.
|
|
350
|
+
|
|
351
|
+
```javascript
|
|
352
|
+
const result = downscale_prepared(prepared, 128, 128, sizeConfig);
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
#### `downscale_prepared_with_palette(prepared, targetWidth, targetHeight, palette, config?)`
|
|
356
|
+
|
|
357
|
+
As above, with a caller-supplied `Uint8Array` RGB palette.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
326
361
|
### Palette Functions
|
|
327
362
|
|
|
328
363
|
#### `extract_palette_from_image(data, width, height, numColors, iterations, strategy?)`
|
|
@@ -508,58 +543,140 @@ const exact = WasmDownscaleConfig.exact_colors();
|
|
|
508
543
|
|
|
509
544
|
### Preset Comparison
|
|
510
545
|
|
|
511
|
-
| Preset | Palette | K-Means | Segmentation | K-Centroid | Rare
|
|
512
|
-
|
|
546
|
+
| Preset | Palette | K-Means | Segmentation | K-Centroid | Rare/Sat/Skin¹ | Speed |
|
|
547
|
+
|--------|---------|---------|--------------|------------|----------------|-------|
|
|
513
548
|
| `fast()` | 16 | 3 | none | 1 (avg) | off | ⚡⚡⚡ |
|
|
514
549
|
| `default` | 16 | 5 | hierarchy_fast | 1 (avg) | off | ⚡⚡ |
|
|
515
|
-
| `vibrant()` | 24 | 8 | hierarchy_fast | 2 (dom) |
|
|
516
|
-
| `quality()` | 32 | 10 | hierarchy | 2 (dom) |
|
|
550
|
+
| `vibrant()` | 24 | 8 | hierarchy_fast | 2 (dom) | reserve+rarity+chroma(0.7) | ⚡ |
|
|
551
|
+
| `quality()` | 32 | 10 | hierarchy | 2 (dom) | reserve+rarity+chroma(0.6)+skin(0.5) | 🐢 |
|
|
517
552
|
| `exact_colors()` | 16 | 0 | hierarchy_fast | 1 (avg) | off | ⚡⚡ |
|
|
518
553
|
|
|
519
|
-
¹
|
|
554
|
+
¹ For the strongest thin-feature retention on faces, additionally set `k_centroid = 4`.
|
|
520
555
|
|
|
521
556
|
---
|
|
522
557
|
|
|
523
558
|
## Advanced Usage
|
|
524
559
|
|
|
560
|
+
### Reusing Preprocessing Across Sizes (Performance)
|
|
561
|
+
|
|
562
|
+
Resolution capping, color pre-quantization, and region segmentation depend only on
|
|
563
|
+
the **source** image, not the target size. When you downscale the same source to
|
|
564
|
+
several sizes (e.g. multiple previews), repeating that work each time is wasted.
|
|
565
|
+
`prepare` runs it **once**; `downscale_prepared` then only does palette extraction
|
|
566
|
+
and tiling per size.
|
|
567
|
+
|
|
568
|
+
```javascript
|
|
569
|
+
import init, { prepare_rgba, downscale_prepared, WasmDownscaleConfig } from 'smart-downscaler';
|
|
570
|
+
await init();
|
|
571
|
+
|
|
572
|
+
const base = new WasmDownscaleConfig();
|
|
573
|
+
base.segmentation_method = 'hierarchy_fast';
|
|
574
|
+
base.max_resolution_mp = 2.048;
|
|
575
|
+
base.max_color_preprocess = 4096;
|
|
576
|
+
|
|
577
|
+
// Once per source image:
|
|
578
|
+
const prepared = prepare_rgba(imageData.data, imageData.width, imageData.height, base);
|
|
579
|
+
|
|
580
|
+
// Many times, cheaply — palette_size and target size may differ each call:
|
|
581
|
+
for (const { w, h, colors } of sizes) {
|
|
582
|
+
const cfg = new WasmDownscaleConfig();
|
|
583
|
+
cfg.palette_size = colors;
|
|
584
|
+
cfg.k_centroid = 4;
|
|
585
|
+
cfg.reserve_colors = Math.round(colors / 8);
|
|
586
|
+
cfg.chroma_recovery = 0.6;
|
|
587
|
+
cfg.skin_protection = 0.5;
|
|
588
|
+
const result = downscale_prepared(prepared, w, h, cfg);
|
|
589
|
+
// ... use result ...
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
prepared.free(); // release WASM memory when done (also freed on GC)
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Contract:** the prepared image fixes `max_resolution_mp`, `max_color_preprocess`,
|
|
596
|
+
and `segmentation_*` at prepare time. `downscale_prepared` overlays per-target knobs
|
|
597
|
+
(`palette_size`, `k_centroid`, `chroma_recovery`, `skin_protection`, …) but never
|
|
598
|
+
re-runs preprocessing or segmentation. Change any of the prepare-time settings →
|
|
599
|
+
call `prepare` again. `prepare` / `prepare_rgba` and `downscale_prepared` /
|
|
600
|
+
`downscale_prepared_with_palette` mirror the one-shot functions.
|
|
601
|
+
|
|
602
|
+
In Rust:
|
|
603
|
+
|
|
604
|
+
```rust
|
|
605
|
+
use smart_downscaler::{prepare_image, smart_downscale_prepared, DownscaleConfig};
|
|
606
|
+
|
|
607
|
+
let prepared = prepare_image(&pixels, w, h, &config); // once
|
|
608
|
+
let small = smart_downscale_prepared(&prepared, 128, 128, &config_s); // reuse
|
|
609
|
+
let large = smart_downscale_prepared(&prepared, 256, 256, &config_l); // reuse
|
|
610
|
+
```
|
|
611
|
+
|
|
525
612
|
### Preserving Rare / Important Colors
|
|
526
613
|
|
|
527
|
-
Palette extraction is **area-weighted**: a color
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
skin) at low palette sizes (16–64). Three knobs counteract this:
|
|
614
|
+
Palette extraction is **area-weighted**: a color covering a large region gets many
|
|
615
|
+
"votes", so small but important colors — lips, eyes, a logo, a highlight — easily
|
|
616
|
+
merge into dominant tones at low palette sizes (16–64). Counteract this with:
|
|
531
617
|
|
|
532
618
|
| Knob | What it does | When to raise it |
|
|
533
619
|
|------|--------------|------------------|
|
|
534
|
-
| `reserve_colors` | **Hard guarantee.** Reserves N slots
|
|
535
|
-
| `detail_boost` | Weights extraction toward
|
|
620
|
+
| `reserve_colors` | **Hard guarantee.** Reserves N slots filled with exact source colors that are far from the rest of the palette *and* important. Most reliable fix. | Always, for portraits. ~`palette_size / 8`. |
|
|
621
|
+
| `detail_boost` | Weights extraction toward **detail-rich regions** via a local-contrast saliency map (measured at the downscale radius, so it targets features thin enough to be averaged away). | When the important color is in a high-detail area. `0.8–1.0`. |
|
|
536
622
|
| `color_rarity` | Damps the frequency vote (`count^p`, `p = 1 − 0.5·rarity`) so large flat regions stop monopolizing the palette. | Mild assist alongside the above. `0.3–0.4`. |
|
|
537
623
|
|
|
538
|
-
Pair these with `k_centroid = 4` (Salient)
|
|
539
|
-
|
|
540
|
-
|
|
624
|
+
Pair these with `k_centroid = 4` (Salient) so the *tile* stage doesn't discard the
|
|
625
|
+
same minority colors a correct palette preserved.
|
|
626
|
+
|
|
627
|
+
### Restoring Saturation (Chroma Recovery)
|
|
628
|
+
|
|
629
|
+
Averaging colors in Oklab during median-cut/k-means pulls the result *inward* — the
|
|
630
|
+
mean of two hues is less colorful than either. `chroma_recovery` undoes this **by
|
|
631
|
+
perceptual difference**: each palette color is pushed back toward the count-weighted
|
|
632
|
+
**mean chroma of the source colors it represents**, at constant hue and lightness,
|
|
633
|
+
then clamped to the sRGB gamut. It only ever *increases* saturation, never reduces it.
|
|
634
|
+
|
|
635
|
+
```javascript
|
|
636
|
+
config.chroma_recovery = 0.6; // 0 = off, ~0.6 natural, 1.0 = full restoration, >1 = punchy
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
This is a global de-wash that fixes the "muddy" look of low-palette output; it is
|
|
640
|
+
independent of (and composes with) the rare-color knobs above.
|
|
641
|
+
|
|
642
|
+
### Isolating Skin Tones
|
|
643
|
+
|
|
644
|
+
Skin clusters tightly in the YCbCr chroma plane (`80 ≤ Cb ≤ 120`, `133 ≤ Cr ≤ 173`).
|
|
645
|
+
With `skin_protection > 0`, skin and non-skin colors are extracted in **separate
|
|
646
|
+
domains** — a single palette bucket never mixes the two, so skin never picks up a
|
|
647
|
+
muddy blend with hair/background and vice-versa. Palette slots are split between the
|
|
648
|
+
domains by population (each guaranteed at least one). The same value also acts as a
|
|
649
|
+
**quantization penalty**: a skin tile prefers skin palette colors, a non-skin tile
|
|
650
|
+
prefers non-skin.
|
|
651
|
+
|
|
652
|
+
```javascript
|
|
653
|
+
config.skin_protection = 0.5; // 0 = off, ~0.5 typical
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
> Lips are reddish and usually pass the skin test, so they live in the *skin* domain
|
|
657
|
+
> — `skin_protection` won't separate lips from skin. Use `reserve_colors` /
|
|
658
|
+
> `k_centroid = 4` for that; they operate *within* the skin domain, so the two
|
|
659
|
+
> features compose: skin gets its own budget, and a distinct lip is preserved inside it.
|
|
660
|
+
|
|
661
|
+
### Recommended Config for Faces / Portraits
|
|
541
662
|
|
|
542
663
|
```javascript
|
|
543
|
-
// Recommended for faces / portraits at 16–64 colors
|
|
544
664
|
const config = new WasmDownscaleConfig();
|
|
545
665
|
config.palette_size = 32;
|
|
546
|
-
config.palette_strategy = 'oklab'; // honors color_rarity / detail_boost
|
|
547
|
-
config.k_centroid = 4; // salient tile color
|
|
666
|
+
config.palette_strategy = 'oklab'; // honors color_rarity / detail_boost
|
|
667
|
+
config.k_centroid = 4; // salient tile color
|
|
548
668
|
config.k_centroid_iterations = 2;
|
|
549
669
|
config.reserve_colors = 4; // ~palette_size / 8 — the guarantee
|
|
550
670
|
config.detail_boost = 0.9; // saliency targeting
|
|
551
671
|
config.color_rarity = 0.35; // frequency damping
|
|
672
|
+
config.chroma_recovery = 0.6; // restore saturation lost to merging
|
|
673
|
+
config.skin_protection = 0.5; // separate skin / non-skin
|
|
552
674
|
config.neighbor_weight = 0.18; // lower = less erosion of thin features
|
|
553
675
|
```
|
|
554
676
|
|
|
555
|
-
> **
|
|
556
|
-
>
|
|
557
|
-
>
|
|
558
|
-
>
|
|
559
|
-
> **Strategy note:** `bitmask` ignores frequency weights during its initial
|
|
560
|
-
> split, so with `bitmask` only `reserve_colors` is active. Use `oklab` (or
|
|
561
|
-
> `saturation`) if you also want `color_rarity` / `detail_boost` to shape the
|
|
562
|
-
> main palette.
|
|
677
|
+
> **Strategy note:** `bitmask` ignores frequency weights during its initial split, so
|
|
678
|
+
> with `bitmask` only `reserve_colors` is active. Use `oklab` (or `saturation`) if you
|
|
679
|
+
> also want `color_rarity` / `detail_boost` to shape the main palette.
|
|
563
680
|
|
|
564
681
|
### Custom Palette Workflow
|
|
565
682
|
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smart-downscaler",
|
|
3
|
-
"type": "module",
|
|
4
3
|
"collaborators": [
|
|
5
4
|
"Pixagram"
|
|
6
5
|
],
|
|
7
6
|
"description": "Intelligent pixel art downscaler with region-aware color quantization",
|
|
8
|
-
"version": "0.6.
|
|
7
|
+
"version": "0.6.3",
|
|
9
8
|
"license": "MIT",
|
|
10
9
|
"repository": {
|
|
11
10
|
"type": "git",
|
|
@@ -18,9 +17,6 @@
|
|
|
18
17
|
],
|
|
19
18
|
"main": "smart_downscaler.js",
|
|
20
19
|
"types": "smart_downscaler.d.ts",
|
|
21
|
-
"sideEffects": [
|
|
22
|
-
"./snippets/*"
|
|
23
|
-
],
|
|
24
20
|
"keywords": [
|
|
25
21
|
"pixel-art",
|
|
26
22
|
"image-processing",
|
package/smart_downscaler.d.ts
CHANGED
|
@@ -33,6 +33,10 @@ export class WasmDownscaleConfig {
|
|
|
33
33
|
constructor();
|
|
34
34
|
static quality(): WasmDownscaleConfig;
|
|
35
35
|
static vibrant(): WasmDownscaleConfig;
|
|
36
|
+
/**
|
|
37
|
+
* Restore chroma lost to merging (constant hue, gamut-clamped). 0.0 = off, ~0.6 natural.
|
|
38
|
+
*/
|
|
39
|
+
chroma_recovery: number;
|
|
36
40
|
/**
|
|
37
41
|
* Rare-color preservation: 0.0 = pure area weighting, 1.0 = strong (count^0.5).
|
|
38
42
|
*/
|
|
@@ -57,6 +61,10 @@ export class WasmDownscaleConfig {
|
|
|
57
61
|
* Reserve N palette slots for distinct, important, under-represented colors. 0 = off.
|
|
58
62
|
*/
|
|
59
63
|
reserve_colors: number;
|
|
64
|
+
/**
|
|
65
|
+
* Isolate skin tones from non-skin (separate domains + mismatch penalty). 0.0 = off, ~0.5.
|
|
66
|
+
*/
|
|
67
|
+
skin_protection: number;
|
|
60
68
|
slic_compactness: number;
|
|
61
69
|
slic_superpixels: number;
|
|
62
70
|
two_pass_refinement: boolean;
|
|
@@ -77,6 +85,22 @@ export class WasmDownscaleResult {
|
|
|
77
85
|
readonly width: number;
|
|
78
86
|
}
|
|
79
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Opaque handle holding the prepared (preprocessed + segmented) source image.
|
|
90
|
+
* Build with [`prepare`] / [`prepare_rgba`], reuse with [`downscale_prepared`].
|
|
91
|
+
*
|
|
92
|
+
* The segmentation is fixed at prepare time from the config you pass. Palette
|
|
93
|
+
* size and target dimensions may differ on every [`downscale_prepared`] call.
|
|
94
|
+
* If you change resolution / color-preprocess / segmentation settings, prepare again.
|
|
95
|
+
*/
|
|
96
|
+
export class WasmPreparedImage {
|
|
97
|
+
private constructor();
|
|
98
|
+
free(): void;
|
|
99
|
+
[Symbol.dispose](): void;
|
|
100
|
+
readonly height: number;
|
|
101
|
+
readonly width: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
80
104
|
/**
|
|
81
105
|
* Analyze colors — FIX: uses HashMap<u32, usize> for O(1) lookup (was O(n) linear scan)
|
|
82
106
|
*/
|
|
@@ -86,6 +110,19 @@ export function color_distance(r1: number, g1: number, b1: number, r2: number, g
|
|
|
86
110
|
|
|
87
111
|
export function downscale(image_data: Uint8Array, width: number, height: number, target_width: number, target_height: number, config?: WasmDownscaleConfig | null): WasmDownscaleResult;
|
|
88
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Downscale a prepared image. Pass an optional `config` to override per-target
|
|
115
|
+
* knobs (palette_size, k_centroid, chroma_recovery, skin_protection, …). The
|
|
116
|
+
* preprocess/segmentation settings always come from prepare time. When `config`
|
|
117
|
+
* is omitted, the prepare-time config is reused as-is.
|
|
118
|
+
*/
|
|
119
|
+
export function downscale_prepared(prepared: WasmPreparedImage, target_width: number, target_height: number, config?: WasmDownscaleConfig | null): WasmDownscaleResult;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Downscale a prepared image with a caller-supplied palette.
|
|
123
|
+
*/
|
|
124
|
+
export function downscale_prepared_with_palette(prepared: WasmPreparedImage, target_width: number, target_height: number, palette_data: Uint8Array, config?: WasmDownscaleConfig | null): WasmDownscaleResult;
|
|
125
|
+
|
|
89
126
|
export function downscale_rgba(image_data: Uint8ClampedArray, width: number, height: number, target_width: number, target_height: number, config?: WasmDownscaleConfig | null): WasmDownscaleResult;
|
|
90
127
|
|
|
91
128
|
export function downscale_simple(image_data: Uint8Array, width: number, height: number, target_width: number, target_height: number, num_colors: number): WasmDownscaleResult;
|
|
@@ -107,130 +144,20 @@ export function log(message: string): void;
|
|
|
107
144
|
export function oklab_to_rgb(l: number, a: number, b: number): Uint8Array;
|
|
108
145
|
|
|
109
146
|
/**
|
|
110
|
-
*
|
|
147
|
+
* Prepare from a `Uint8Array` (RGBA).
|
|
111
148
|
*/
|
|
112
|
-
export function
|
|
113
|
-
|
|
114
|
-
export function rgb_to_oklab(r: number, g: number, b: number): Float32Array;
|
|
115
|
-
|
|
116
|
-
export function version(): string;
|
|
117
|
-
|
|
118
|
-
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
|
119
|
-
|
|
120
|
-
export interface InitOutput {
|
|
121
|
-
readonly memory: WebAssembly.Memory;
|
|
122
|
-
readonly __wbg_coloranalysisresult_free: (a: number, b: number) => void;
|
|
123
|
-
readonly __wbg_colorentry_free: (a: number, b: number) => void;
|
|
124
|
-
readonly __wbg_get_wasmdownscaleconfig_color_rarity: (a: number) => number;
|
|
125
|
-
readonly __wbg_get_wasmdownscaleconfig_detail_boost: (a: number) => number;
|
|
126
|
-
readonly __wbg_get_wasmdownscaleconfig_edge_weight: (a: number) => number;
|
|
127
|
-
readonly __wbg_get_wasmdownscaleconfig_hierarchy_min_size: (a: number) => number;
|
|
128
|
-
readonly __wbg_get_wasmdownscaleconfig_hierarchy_threshold: (a: number) => number;
|
|
129
|
-
readonly __wbg_get_wasmdownscaleconfig_k_centroid: (a: number) => number;
|
|
130
|
-
readonly __wbg_get_wasmdownscaleconfig_k_centroid_iterations: (a: number) => number;
|
|
131
|
-
readonly __wbg_get_wasmdownscaleconfig_kmeans_iterations: (a: number) => number;
|
|
132
|
-
readonly __wbg_get_wasmdownscaleconfig_max_color_preprocess: (a: number) => number;
|
|
133
|
-
readonly __wbg_get_wasmdownscaleconfig_max_resolution_mp: (a: number) => number;
|
|
134
|
-
readonly __wbg_get_wasmdownscaleconfig_neighbor_weight: (a: number) => number;
|
|
135
|
-
readonly __wbg_get_wasmdownscaleconfig_palette_size: (a: number) => number;
|
|
136
|
-
readonly __wbg_get_wasmdownscaleconfig_refinement_iterations: (a: number) => number;
|
|
137
|
-
readonly __wbg_get_wasmdownscaleconfig_region_weight: (a: number) => number;
|
|
138
|
-
readonly __wbg_get_wasmdownscaleconfig_reserve_colors: (a: number) => number;
|
|
139
|
-
readonly __wbg_get_wasmdownscaleconfig_slic_compactness: (a: number) => number;
|
|
140
|
-
readonly __wbg_get_wasmdownscaleconfig_slic_superpixels: (a: number) => number;
|
|
141
|
-
readonly __wbg_get_wasmdownscaleconfig_two_pass_refinement: (a: number) => number;
|
|
142
|
-
readonly __wbg_set_wasmdownscaleconfig_color_rarity: (a: number, b: number) => void;
|
|
143
|
-
readonly __wbg_set_wasmdownscaleconfig_detail_boost: (a: number, b: number) => void;
|
|
144
|
-
readonly __wbg_set_wasmdownscaleconfig_edge_weight: (a: number, b: number) => void;
|
|
145
|
-
readonly __wbg_set_wasmdownscaleconfig_hierarchy_min_size: (a: number, b: number) => void;
|
|
146
|
-
readonly __wbg_set_wasmdownscaleconfig_hierarchy_threshold: (a: number, b: number) => void;
|
|
147
|
-
readonly __wbg_set_wasmdownscaleconfig_k_centroid: (a: number, b: number) => void;
|
|
148
|
-
readonly __wbg_set_wasmdownscaleconfig_k_centroid_iterations: (a: number, b: number) => void;
|
|
149
|
-
readonly __wbg_set_wasmdownscaleconfig_kmeans_iterations: (a: number, b: number) => void;
|
|
150
|
-
readonly __wbg_set_wasmdownscaleconfig_max_color_preprocess: (a: number, b: number) => void;
|
|
151
|
-
readonly __wbg_set_wasmdownscaleconfig_max_resolution_mp: (a: number, b: number) => void;
|
|
152
|
-
readonly __wbg_set_wasmdownscaleconfig_neighbor_weight: (a: number, b: number) => void;
|
|
153
|
-
readonly __wbg_set_wasmdownscaleconfig_palette_size: (a: number, b: number) => void;
|
|
154
|
-
readonly __wbg_set_wasmdownscaleconfig_refinement_iterations: (a: number, b: number) => void;
|
|
155
|
-
readonly __wbg_set_wasmdownscaleconfig_region_weight: (a: number, b: number) => void;
|
|
156
|
-
readonly __wbg_set_wasmdownscaleconfig_reserve_colors: (a: number, b: number) => void;
|
|
157
|
-
readonly __wbg_set_wasmdownscaleconfig_slic_compactness: (a: number, b: number) => void;
|
|
158
|
-
readonly __wbg_set_wasmdownscaleconfig_slic_superpixels: (a: number, b: number) => void;
|
|
159
|
-
readonly __wbg_set_wasmdownscaleconfig_two_pass_refinement: (a: number, b: number) => void;
|
|
160
|
-
readonly __wbg_wasmdownscaleconfig_free: (a: number, b: number) => void;
|
|
161
|
-
readonly __wbg_wasmdownscaleresult_free: (a: number, b: number) => void;
|
|
162
|
-
readonly analyze_colors: (a: any, b: number, c: number, d: number) => [number, number, number];
|
|
163
|
-
readonly color_distance: (a: number, b: number, c: number, d: number, e: number, f: number) => number;
|
|
164
|
-
readonly coloranalysisresult_color_count: (a: number) => number;
|
|
165
|
-
readonly coloranalysisresult_get_color: (a: number, b: number) => number;
|
|
166
|
-
readonly coloranalysisresult_get_colors_flat: (a: number) => any;
|
|
167
|
-
readonly coloranalysisresult_success: (a: number) => number;
|
|
168
|
-
readonly coloranalysisresult_to_json: (a: number) => [number, number, number];
|
|
169
|
-
readonly coloranalysisresult_total_pixels: (a: number) => number;
|
|
170
|
-
readonly colorentry_b: (a: number) => number;
|
|
171
|
-
readonly colorentry_count: (a: number) => number;
|
|
172
|
-
readonly colorentry_g: (a: number) => number;
|
|
173
|
-
readonly colorentry_hex: (a: number) => [number, number];
|
|
174
|
-
readonly colorentry_percentage: (a: number) => number;
|
|
175
|
-
readonly colorentry_r: (a: number) => number;
|
|
176
|
-
readonly downscale: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
|
|
177
|
-
readonly downscale_rgba: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
|
|
178
|
-
readonly downscale_simple: (a: any, b: number, c: number, d: number, e: number, f: number) => [number, number, number];
|
|
179
|
-
readonly downscale_with_palette: (a: any, b: number, c: number, d: number, e: number, f: any, g: number) => [number, number, number];
|
|
180
|
-
readonly extract_palette_from_image: (a: any, b: number, c: number, d: number, e: number, f: number, g: number) => [number, number, number];
|
|
181
|
-
readonly get_chroma: (a: number, b: number, c: number) => number;
|
|
182
|
-
readonly get_lightness: (a: number, b: number, c: number) => number;
|
|
183
|
-
readonly get_palette_strategies: () => any;
|
|
184
|
-
readonly init: () => void;
|
|
185
|
-
readonly log: (a: number, b: number) => void;
|
|
186
|
-
readonly oklab_to_rgb: (a: number, b: number, c: number) => any;
|
|
187
|
-
readonly quantize_to_palette: (a: any, b: number, c: number, d: any) => [number, number, number];
|
|
188
|
-
readonly rgb_to_oklab: (a: number, b: number, c: number) => any;
|
|
189
|
-
readonly version: () => [number, number];
|
|
190
|
-
readonly wasmdownscaleconfig_exact_colors: () => number;
|
|
191
|
-
readonly wasmdownscaleconfig_fast: () => number;
|
|
192
|
-
readonly wasmdownscaleconfig_new: () => number;
|
|
193
|
-
readonly wasmdownscaleconfig_palette_strategy: (a: number) => [number, number];
|
|
194
|
-
readonly wasmdownscaleconfig_quality: () => number;
|
|
195
|
-
readonly wasmdownscaleconfig_segmentation_method: (a: number) => [number, number];
|
|
196
|
-
readonly wasmdownscaleconfig_set_palette_strategy: (a: number, b: number, c: number) => void;
|
|
197
|
-
readonly wasmdownscaleconfig_set_segmentation_method: (a: number, b: number, c: number) => void;
|
|
198
|
-
readonly wasmdownscaleconfig_vibrant: () => number;
|
|
199
|
-
readonly wasmdownscaleresult_data: (a: number) => any;
|
|
200
|
-
readonly wasmdownscaleresult_height: (a: number) => number;
|
|
201
|
-
readonly wasmdownscaleresult_indices: (a: number) => any;
|
|
202
|
-
readonly wasmdownscaleresult_palette: (a: number) => any;
|
|
203
|
-
readonly wasmdownscaleresult_palette_size: (a: number) => number;
|
|
204
|
-
readonly wasmdownscaleresult_rgb_data: (a: number) => any;
|
|
205
|
-
readonly wasmdownscaleresult_width: (a: number) => number;
|
|
206
|
-
readonly __wbindgen_exn_store: (a: number) => void;
|
|
207
|
-
readonly __externref_table_alloc: () => number;
|
|
208
|
-
readonly __wbindgen_externrefs: WebAssembly.Table;
|
|
209
|
-
readonly __wbindgen_malloc: (a: number, b: number) => number;
|
|
210
|
-
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
|
|
211
|
-
readonly __externref_table_dealloc: (a: number) => void;
|
|
212
|
-
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
|
|
213
|
-
readonly __wbindgen_start: () => void;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
export type SyncInitInput = BufferSource | WebAssembly.Module;
|
|
149
|
+
export function prepare(image_data: Uint8Array, width: number, height: number, config?: WasmDownscaleConfig | null): WasmPreparedImage;
|
|
217
150
|
|
|
218
151
|
/**
|
|
219
|
-
*
|
|
220
|
-
* a precompiled `WebAssembly.Module`.
|
|
221
|
-
*
|
|
222
|
-
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
|
|
223
|
-
*
|
|
224
|
-
* @returns {InitOutput}
|
|
152
|
+
* Prepare from a `Uint8ClampedArray` (canvas `getImageData`).
|
|
225
153
|
*/
|
|
226
|
-
export function
|
|
154
|
+
export function prepare_rgba(image_data: Uint8ClampedArray, width: number, height: number, config?: WasmDownscaleConfig | null): WasmPreparedImage;
|
|
227
155
|
|
|
228
156
|
/**
|
|
229
|
-
*
|
|
230
|
-
* for everything else, calls `WebAssembly.instantiate` directly.
|
|
231
|
-
*
|
|
232
|
-
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
|
|
233
|
-
*
|
|
234
|
-
* @returns {Promise<InitOutput>}
|
|
157
|
+
* Quantize to palette — FIX: compute Oklab once per pixel (was twice)
|
|
235
158
|
*/
|
|
236
|
-
export
|
|
159
|
+
export function quantize_to_palette(image_data: Uint8Array, width: number, height: number, palette_data: Uint8Array): WasmDownscaleResult;
|
|
160
|
+
|
|
161
|
+
export function rgb_to_oklab(r: number, g: number, b: number): Float32Array;
|
|
162
|
+
|
|
163
|
+
export function version(): string;
|
package/smart_downscaler.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* @ts-self-types="./smart_downscaler.d.ts" */
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
class ColorAnalysisResult {
|
|
4
4
|
static __wrap(ptr) {
|
|
5
5
|
ptr = ptr >>> 0;
|
|
6
6
|
const obj = Object.create(ColorAnalysisResult.prototype);
|
|
@@ -66,8 +66,9 @@ export class ColorAnalysisResult {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
if (Symbol.dispose) ColorAnalysisResult.prototype[Symbol.dispose] = ColorAnalysisResult.prototype.free;
|
|
69
|
+
exports.ColorAnalysisResult = ColorAnalysisResult;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
class ColorEntry {
|
|
71
72
|
static __wrap(ptr) {
|
|
72
73
|
ptr = ptr >>> 0;
|
|
73
74
|
const obj = Object.create(ColorEntry.prototype);
|
|
@@ -137,8 +138,9 @@ export class ColorEntry {
|
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
if (Symbol.dispose) ColorEntry.prototype[Symbol.dispose] = ColorEntry.prototype.free;
|
|
141
|
+
exports.ColorEntry = ColorEntry;
|
|
140
142
|
|
|
141
|
-
|
|
143
|
+
class WasmDownscaleConfig {
|
|
142
144
|
static __wrap(ptr) {
|
|
143
145
|
ptr = ptr >>> 0;
|
|
144
146
|
const obj = Object.create(WasmDownscaleConfig.prototype);
|
|
@@ -156,6 +158,14 @@ export class WasmDownscaleConfig {
|
|
|
156
158
|
const ptr = this.__destroy_into_raw();
|
|
157
159
|
wasm.__wbg_wasmdownscaleconfig_free(ptr, 0);
|
|
158
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Restore chroma lost to merging (constant hue, gamut-clamped). 0.0 = off, ~0.6 natural.
|
|
163
|
+
* @returns {number}
|
|
164
|
+
*/
|
|
165
|
+
get chroma_recovery() {
|
|
166
|
+
const ret = wasm.__wbg_get_wasmdownscaleconfig_chroma_recovery(this.__wbg_ptr);
|
|
167
|
+
return ret;
|
|
168
|
+
}
|
|
159
169
|
/**
|
|
160
170
|
* Rare-color preservation: 0.0 = pure area weighting, 1.0 = strong (count^0.5).
|
|
161
171
|
* @returns {number}
|
|
@@ -264,6 +274,14 @@ export class WasmDownscaleConfig {
|
|
|
264
274
|
const ret = wasm.__wbg_get_wasmdownscaleconfig_reserve_colors(this.__wbg_ptr);
|
|
265
275
|
return ret >>> 0;
|
|
266
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Isolate skin tones from non-skin (separate domains + mismatch penalty). 0.0 = off, ~0.5.
|
|
279
|
+
* @returns {number}
|
|
280
|
+
*/
|
|
281
|
+
get skin_protection() {
|
|
282
|
+
const ret = wasm.__wbg_get_wasmdownscaleconfig_skin_protection(this.__wbg_ptr);
|
|
283
|
+
return ret;
|
|
284
|
+
}
|
|
267
285
|
/**
|
|
268
286
|
* @returns {number}
|
|
269
287
|
*/
|
|
@@ -285,6 +303,13 @@ export class WasmDownscaleConfig {
|
|
|
285
303
|
const ret = wasm.__wbg_get_wasmdownscaleconfig_two_pass_refinement(this.__wbg_ptr);
|
|
286
304
|
return ret !== 0;
|
|
287
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Restore chroma lost to merging (constant hue, gamut-clamped). 0.0 = off, ~0.6 natural.
|
|
308
|
+
* @param {number} arg0
|
|
309
|
+
*/
|
|
310
|
+
set chroma_recovery(arg0) {
|
|
311
|
+
wasm.__wbg_set_wasmdownscaleconfig_chroma_recovery(this.__wbg_ptr, arg0);
|
|
312
|
+
}
|
|
288
313
|
/**
|
|
289
314
|
* Rare-color preservation: 0.0 = pure area weighting, 1.0 = strong (count^0.5).
|
|
290
315
|
* @param {number} arg0
|
|
@@ -378,6 +403,13 @@ export class WasmDownscaleConfig {
|
|
|
378
403
|
set reserve_colors(arg0) {
|
|
379
404
|
wasm.__wbg_set_wasmdownscaleconfig_reserve_colors(this.__wbg_ptr, arg0);
|
|
380
405
|
}
|
|
406
|
+
/**
|
|
407
|
+
* Isolate skin tones from non-skin (separate domains + mismatch penalty). 0.0 = off, ~0.5.
|
|
408
|
+
* @param {number} arg0
|
|
409
|
+
*/
|
|
410
|
+
set skin_protection(arg0) {
|
|
411
|
+
wasm.__wbg_set_wasmdownscaleconfig_skin_protection(this.__wbg_ptr, arg0);
|
|
412
|
+
}
|
|
381
413
|
/**
|
|
382
414
|
* @param {number} arg0
|
|
383
415
|
*/
|
|
@@ -478,8 +510,9 @@ export class WasmDownscaleConfig {
|
|
|
478
510
|
}
|
|
479
511
|
}
|
|
480
512
|
if (Symbol.dispose) WasmDownscaleConfig.prototype[Symbol.dispose] = WasmDownscaleConfig.prototype.free;
|
|
513
|
+
exports.WasmDownscaleConfig = WasmDownscaleConfig;
|
|
481
514
|
|
|
482
|
-
|
|
515
|
+
class WasmDownscaleResult {
|
|
483
516
|
static __wrap(ptr) {
|
|
484
517
|
ptr = ptr >>> 0;
|
|
485
518
|
const obj = Object.create(WasmDownscaleResult.prototype);
|
|
@@ -548,6 +581,51 @@ export class WasmDownscaleResult {
|
|
|
548
581
|
}
|
|
549
582
|
}
|
|
550
583
|
if (Symbol.dispose) WasmDownscaleResult.prototype[Symbol.dispose] = WasmDownscaleResult.prototype.free;
|
|
584
|
+
exports.WasmDownscaleResult = WasmDownscaleResult;
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Opaque handle holding the prepared (preprocessed + segmented) source image.
|
|
588
|
+
* Build with [`prepare`] / [`prepare_rgba`], reuse with [`downscale_prepared`].
|
|
589
|
+
*
|
|
590
|
+
* The segmentation is fixed at prepare time from the config you pass. Palette
|
|
591
|
+
* size and target dimensions may differ on every [`downscale_prepared`] call.
|
|
592
|
+
* If you change resolution / color-preprocess / segmentation settings, prepare again.
|
|
593
|
+
*/
|
|
594
|
+
class WasmPreparedImage {
|
|
595
|
+
static __wrap(ptr) {
|
|
596
|
+
ptr = ptr >>> 0;
|
|
597
|
+
const obj = Object.create(WasmPreparedImage.prototype);
|
|
598
|
+
obj.__wbg_ptr = ptr;
|
|
599
|
+
WasmPreparedImageFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
600
|
+
return obj;
|
|
601
|
+
}
|
|
602
|
+
__destroy_into_raw() {
|
|
603
|
+
const ptr = this.__wbg_ptr;
|
|
604
|
+
this.__wbg_ptr = 0;
|
|
605
|
+
WasmPreparedImageFinalization.unregister(this);
|
|
606
|
+
return ptr;
|
|
607
|
+
}
|
|
608
|
+
free() {
|
|
609
|
+
const ptr = this.__destroy_into_raw();
|
|
610
|
+
wasm.__wbg_wasmpreparedimage_free(ptr, 0);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* @returns {number}
|
|
614
|
+
*/
|
|
615
|
+
get height() {
|
|
616
|
+
const ret = wasm.wasmpreparedimage_height(this.__wbg_ptr);
|
|
617
|
+
return ret >>> 0;
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* @returns {number}
|
|
621
|
+
*/
|
|
622
|
+
get width() {
|
|
623
|
+
const ret = wasm.wasmpreparedimage_width(this.__wbg_ptr);
|
|
624
|
+
return ret >>> 0;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (Symbol.dispose) WasmPreparedImage.prototype[Symbol.dispose] = WasmPreparedImage.prototype.free;
|
|
628
|
+
exports.WasmPreparedImage = WasmPreparedImage;
|
|
551
629
|
|
|
552
630
|
/**
|
|
553
631
|
* Analyze colors — FIX: uses HashMap<u32, usize> for O(1) lookup (was O(n) linear scan)
|
|
@@ -556,7 +634,7 @@ if (Symbol.dispose) WasmDownscaleResult.prototype[Symbol.dispose] = WasmDownscal
|
|
|
556
634
|
* @param {string} sort_method
|
|
557
635
|
* @returns {ColorAnalysisResult}
|
|
558
636
|
*/
|
|
559
|
-
|
|
637
|
+
function analyze_colors(image_data, max_colors, sort_method) {
|
|
560
638
|
const ptr0 = passStringToWasm0(sort_method, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
561
639
|
const len0 = WASM_VECTOR_LEN;
|
|
562
640
|
const ret = wasm.analyze_colors(image_data, max_colors, ptr0, len0);
|
|
@@ -565,6 +643,7 @@ export function analyze_colors(image_data, max_colors, sort_method) {
|
|
|
565
643
|
}
|
|
566
644
|
return ColorAnalysisResult.__wrap(ret[0]);
|
|
567
645
|
}
|
|
646
|
+
exports.analyze_colors = analyze_colors;
|
|
568
647
|
|
|
569
648
|
/**
|
|
570
649
|
* @param {number} r1
|
|
@@ -575,10 +654,11 @@ export function analyze_colors(image_data, max_colors, sort_method) {
|
|
|
575
654
|
* @param {number} b2
|
|
576
655
|
* @returns {number}
|
|
577
656
|
*/
|
|
578
|
-
|
|
657
|
+
function color_distance(r1, g1, b1, r2, g2, b2) {
|
|
579
658
|
const ret = wasm.color_distance(r1, g1, b1, r2, g2, b2);
|
|
580
659
|
return ret;
|
|
581
660
|
}
|
|
661
|
+
exports.color_distance = color_distance;
|
|
582
662
|
|
|
583
663
|
/**
|
|
584
664
|
* @param {Uint8Array} image_data
|
|
@@ -589,7 +669,7 @@ export function color_distance(r1, g1, b1, r2, g2, b2) {
|
|
|
589
669
|
* @param {WasmDownscaleConfig | null} [config]
|
|
590
670
|
* @returns {WasmDownscaleResult}
|
|
591
671
|
*/
|
|
592
|
-
|
|
672
|
+
function downscale(image_data, width, height, target_width, target_height, config) {
|
|
593
673
|
let ptr0 = 0;
|
|
594
674
|
if (!isLikeNone(config)) {
|
|
595
675
|
_assertClass(config, WasmDownscaleConfig);
|
|
@@ -601,6 +681,57 @@ export function downscale(image_data, width, height, target_width, target_height
|
|
|
601
681
|
}
|
|
602
682
|
return WasmDownscaleResult.__wrap(ret[0]);
|
|
603
683
|
}
|
|
684
|
+
exports.downscale = downscale;
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Downscale a prepared image. Pass an optional `config` to override per-target
|
|
688
|
+
* knobs (palette_size, k_centroid, chroma_recovery, skin_protection, …). The
|
|
689
|
+
* preprocess/segmentation settings always come from prepare time. When `config`
|
|
690
|
+
* is omitted, the prepare-time config is reused as-is.
|
|
691
|
+
* @param {WasmPreparedImage} prepared
|
|
692
|
+
* @param {number} target_width
|
|
693
|
+
* @param {number} target_height
|
|
694
|
+
* @param {WasmDownscaleConfig | null} [config]
|
|
695
|
+
* @returns {WasmDownscaleResult}
|
|
696
|
+
*/
|
|
697
|
+
function downscale_prepared(prepared, target_width, target_height, config) {
|
|
698
|
+
_assertClass(prepared, WasmPreparedImage);
|
|
699
|
+
let ptr0 = 0;
|
|
700
|
+
if (!isLikeNone(config)) {
|
|
701
|
+
_assertClass(config, WasmDownscaleConfig);
|
|
702
|
+
ptr0 = config.__destroy_into_raw();
|
|
703
|
+
}
|
|
704
|
+
const ret = wasm.downscale_prepared(prepared.__wbg_ptr, target_width, target_height, ptr0);
|
|
705
|
+
if (ret[2]) {
|
|
706
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
707
|
+
}
|
|
708
|
+
return WasmDownscaleResult.__wrap(ret[0]);
|
|
709
|
+
}
|
|
710
|
+
exports.downscale_prepared = downscale_prepared;
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Downscale a prepared image with a caller-supplied palette.
|
|
714
|
+
* @param {WasmPreparedImage} prepared
|
|
715
|
+
* @param {number} target_width
|
|
716
|
+
* @param {number} target_height
|
|
717
|
+
* @param {Uint8Array} palette_data
|
|
718
|
+
* @param {WasmDownscaleConfig | null} [config]
|
|
719
|
+
* @returns {WasmDownscaleResult}
|
|
720
|
+
*/
|
|
721
|
+
function downscale_prepared_with_palette(prepared, target_width, target_height, palette_data, config) {
|
|
722
|
+
_assertClass(prepared, WasmPreparedImage);
|
|
723
|
+
let ptr0 = 0;
|
|
724
|
+
if (!isLikeNone(config)) {
|
|
725
|
+
_assertClass(config, WasmDownscaleConfig);
|
|
726
|
+
ptr0 = config.__destroy_into_raw();
|
|
727
|
+
}
|
|
728
|
+
const ret = wasm.downscale_prepared_with_palette(prepared.__wbg_ptr, target_width, target_height, palette_data, ptr0);
|
|
729
|
+
if (ret[2]) {
|
|
730
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
731
|
+
}
|
|
732
|
+
return WasmDownscaleResult.__wrap(ret[0]);
|
|
733
|
+
}
|
|
734
|
+
exports.downscale_prepared_with_palette = downscale_prepared_with_palette;
|
|
604
735
|
|
|
605
736
|
/**
|
|
606
737
|
* @param {Uint8ClampedArray} image_data
|
|
@@ -611,7 +742,7 @@ export function downscale(image_data, width, height, target_width, target_height
|
|
|
611
742
|
* @param {WasmDownscaleConfig | null} [config]
|
|
612
743
|
* @returns {WasmDownscaleResult}
|
|
613
744
|
*/
|
|
614
|
-
|
|
745
|
+
function downscale_rgba(image_data, width, height, target_width, target_height, config) {
|
|
615
746
|
let ptr0 = 0;
|
|
616
747
|
if (!isLikeNone(config)) {
|
|
617
748
|
_assertClass(config, WasmDownscaleConfig);
|
|
@@ -623,6 +754,7 @@ export function downscale_rgba(image_data, width, height, target_width, target_h
|
|
|
623
754
|
}
|
|
624
755
|
return WasmDownscaleResult.__wrap(ret[0]);
|
|
625
756
|
}
|
|
757
|
+
exports.downscale_rgba = downscale_rgba;
|
|
626
758
|
|
|
627
759
|
/**
|
|
628
760
|
* @param {Uint8Array} image_data
|
|
@@ -633,13 +765,14 @@ export function downscale_rgba(image_data, width, height, target_width, target_h
|
|
|
633
765
|
* @param {number} num_colors
|
|
634
766
|
* @returns {WasmDownscaleResult}
|
|
635
767
|
*/
|
|
636
|
-
|
|
768
|
+
function downscale_simple(image_data, width, height, target_width, target_height, num_colors) {
|
|
637
769
|
const ret = wasm.downscale_simple(image_data, width, height, target_width, target_height, num_colors);
|
|
638
770
|
if (ret[2]) {
|
|
639
771
|
throw takeFromExternrefTable0(ret[1]);
|
|
640
772
|
}
|
|
641
773
|
return WasmDownscaleResult.__wrap(ret[0]);
|
|
642
774
|
}
|
|
775
|
+
exports.downscale_simple = downscale_simple;
|
|
643
776
|
|
|
644
777
|
/**
|
|
645
778
|
* @param {Uint8Array} image_data
|
|
@@ -651,7 +784,7 @@ export function downscale_simple(image_data, width, height, target_width, target
|
|
|
651
784
|
* @param {WasmDownscaleConfig | null} [config]
|
|
652
785
|
* @returns {WasmDownscaleResult}
|
|
653
786
|
*/
|
|
654
|
-
|
|
787
|
+
function downscale_with_palette(image_data, width, height, target_width, target_height, palette_data, config) {
|
|
655
788
|
let ptr0 = 0;
|
|
656
789
|
if (!isLikeNone(config)) {
|
|
657
790
|
_assertClass(config, WasmDownscaleConfig);
|
|
@@ -663,6 +796,7 @@ export function downscale_with_palette(image_data, width, height, target_width,
|
|
|
663
796
|
}
|
|
664
797
|
return WasmDownscaleResult.__wrap(ret[0]);
|
|
665
798
|
}
|
|
799
|
+
exports.downscale_with_palette = downscale_with_palette;
|
|
666
800
|
|
|
667
801
|
/**
|
|
668
802
|
* @param {Uint8Array} image_data
|
|
@@ -673,7 +807,7 @@ export function downscale_with_palette(image_data, width, height, target_width,
|
|
|
673
807
|
* @param {string | null} [strategy]
|
|
674
808
|
* @returns {Uint8Array}
|
|
675
809
|
*/
|
|
676
|
-
|
|
810
|
+
function extract_palette_from_image(image_data, _width, _height, num_colors, kmeans_iterations, strategy) {
|
|
677
811
|
var ptr0 = isLikeNone(strategy) ? 0 : passStringToWasm0(strategy, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
678
812
|
var len0 = WASM_VECTOR_LEN;
|
|
679
813
|
const ret = wasm.extract_palette_from_image(image_data, _width, _height, num_colors, kmeans_iterations, ptr0, len0);
|
|
@@ -682,6 +816,7 @@ export function extract_palette_from_image(image_data, _width, _height, num_colo
|
|
|
682
816
|
}
|
|
683
817
|
return takeFromExternrefTable0(ret[0]);
|
|
684
818
|
}
|
|
819
|
+
exports.extract_palette_from_image = extract_palette_from_image;
|
|
685
820
|
|
|
686
821
|
/**
|
|
687
822
|
* @param {number} r
|
|
@@ -689,10 +824,11 @@ export function extract_palette_from_image(image_data, _width, _height, num_colo
|
|
|
689
824
|
* @param {number} b
|
|
690
825
|
* @returns {number}
|
|
691
826
|
*/
|
|
692
|
-
|
|
827
|
+
function get_chroma(r, g, b) {
|
|
693
828
|
const ret = wasm.get_chroma(r, g, b);
|
|
694
829
|
return ret;
|
|
695
830
|
}
|
|
831
|
+
exports.get_chroma = get_chroma;
|
|
696
832
|
|
|
697
833
|
/**
|
|
698
834
|
* @param {number} r
|
|
@@ -700,31 +836,35 @@ export function get_chroma(r, g, b) {
|
|
|
700
836
|
* @param {number} b
|
|
701
837
|
* @returns {number}
|
|
702
838
|
*/
|
|
703
|
-
|
|
839
|
+
function get_lightness(r, g, b) {
|
|
704
840
|
const ret = wasm.get_lightness(r, g, b);
|
|
705
841
|
return ret;
|
|
706
842
|
}
|
|
843
|
+
exports.get_lightness = get_lightness;
|
|
707
844
|
|
|
708
845
|
/**
|
|
709
846
|
* @returns {Array<any>}
|
|
710
847
|
*/
|
|
711
|
-
|
|
848
|
+
function get_palette_strategies() {
|
|
712
849
|
const ret = wasm.get_palette_strategies();
|
|
713
850
|
return ret;
|
|
714
851
|
}
|
|
852
|
+
exports.get_palette_strategies = get_palette_strategies;
|
|
715
853
|
|
|
716
|
-
|
|
854
|
+
function init() {
|
|
717
855
|
wasm.init();
|
|
718
856
|
}
|
|
857
|
+
exports.init = init;
|
|
719
858
|
|
|
720
859
|
/**
|
|
721
860
|
* @param {string} message
|
|
722
861
|
*/
|
|
723
|
-
|
|
862
|
+
function log(message) {
|
|
724
863
|
const ptr0 = passStringToWasm0(message, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
725
864
|
const len0 = WASM_VECTOR_LEN;
|
|
726
865
|
wasm.log(ptr0, len0);
|
|
727
866
|
}
|
|
867
|
+
exports.log = log;
|
|
728
868
|
|
|
729
869
|
/**
|
|
730
870
|
* @param {number} l
|
|
@@ -732,10 +872,55 @@ export function log(message) {
|
|
|
732
872
|
* @param {number} b
|
|
733
873
|
* @returns {Uint8Array}
|
|
734
874
|
*/
|
|
735
|
-
|
|
875
|
+
function oklab_to_rgb(l, a, b) {
|
|
736
876
|
const ret = wasm.oklab_to_rgb(l, a, b);
|
|
737
877
|
return ret;
|
|
738
878
|
}
|
|
879
|
+
exports.oklab_to_rgb = oklab_to_rgb;
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Prepare from a `Uint8Array` (RGBA).
|
|
883
|
+
* @param {Uint8Array} image_data
|
|
884
|
+
* @param {number} width
|
|
885
|
+
* @param {number} height
|
|
886
|
+
* @param {WasmDownscaleConfig | null} [config]
|
|
887
|
+
* @returns {WasmPreparedImage}
|
|
888
|
+
*/
|
|
889
|
+
function prepare(image_data, width, height, config) {
|
|
890
|
+
let ptr0 = 0;
|
|
891
|
+
if (!isLikeNone(config)) {
|
|
892
|
+
_assertClass(config, WasmDownscaleConfig);
|
|
893
|
+
ptr0 = config.__destroy_into_raw();
|
|
894
|
+
}
|
|
895
|
+
const ret = wasm.prepare(image_data, width, height, ptr0);
|
|
896
|
+
if (ret[2]) {
|
|
897
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
898
|
+
}
|
|
899
|
+
return WasmPreparedImage.__wrap(ret[0]);
|
|
900
|
+
}
|
|
901
|
+
exports.prepare = prepare;
|
|
902
|
+
|
|
903
|
+
/**
|
|
904
|
+
* Prepare from a `Uint8ClampedArray` (canvas `getImageData`).
|
|
905
|
+
* @param {Uint8ClampedArray} image_data
|
|
906
|
+
* @param {number} width
|
|
907
|
+
* @param {number} height
|
|
908
|
+
* @param {WasmDownscaleConfig | null} [config]
|
|
909
|
+
* @returns {WasmPreparedImage}
|
|
910
|
+
*/
|
|
911
|
+
function prepare_rgba(image_data, width, height, config) {
|
|
912
|
+
let ptr0 = 0;
|
|
913
|
+
if (!isLikeNone(config)) {
|
|
914
|
+
_assertClass(config, WasmDownscaleConfig);
|
|
915
|
+
ptr0 = config.__destroy_into_raw();
|
|
916
|
+
}
|
|
917
|
+
const ret = wasm.prepare_rgba(image_data, width, height, ptr0);
|
|
918
|
+
if (ret[2]) {
|
|
919
|
+
throw takeFromExternrefTable0(ret[1]);
|
|
920
|
+
}
|
|
921
|
+
return WasmPreparedImage.__wrap(ret[0]);
|
|
922
|
+
}
|
|
923
|
+
exports.prepare_rgba = prepare_rgba;
|
|
739
924
|
|
|
740
925
|
/**
|
|
741
926
|
* Quantize to palette — FIX: compute Oklab once per pixel (was twice)
|
|
@@ -745,13 +930,14 @@ export function oklab_to_rgb(l, a, b) {
|
|
|
745
930
|
* @param {Uint8Array} palette_data
|
|
746
931
|
* @returns {WasmDownscaleResult}
|
|
747
932
|
*/
|
|
748
|
-
|
|
933
|
+
function quantize_to_palette(image_data, width, height, palette_data) {
|
|
749
934
|
const ret = wasm.quantize_to_palette(image_data, width, height, palette_data);
|
|
750
935
|
if (ret[2]) {
|
|
751
936
|
throw takeFromExternrefTable0(ret[1]);
|
|
752
937
|
}
|
|
753
938
|
return WasmDownscaleResult.__wrap(ret[0]);
|
|
754
939
|
}
|
|
940
|
+
exports.quantize_to_palette = quantize_to_palette;
|
|
755
941
|
|
|
756
942
|
/**
|
|
757
943
|
* @param {number} r
|
|
@@ -759,15 +945,16 @@ export function quantize_to_palette(image_data, width, height, palette_data) {
|
|
|
759
945
|
* @param {number} b
|
|
760
946
|
* @returns {Float32Array}
|
|
761
947
|
*/
|
|
762
|
-
|
|
948
|
+
function rgb_to_oklab(r, g, b) {
|
|
763
949
|
const ret = wasm.rgb_to_oklab(r, g, b);
|
|
764
950
|
return ret;
|
|
765
951
|
}
|
|
952
|
+
exports.rgb_to_oklab = rgb_to_oklab;
|
|
766
953
|
|
|
767
954
|
/**
|
|
768
955
|
* @returns {string}
|
|
769
956
|
*/
|
|
770
|
-
|
|
957
|
+
function version() {
|
|
771
958
|
let deferred1_0;
|
|
772
959
|
let deferred1_1;
|
|
773
960
|
try {
|
|
@@ -779,6 +966,7 @@ export function version() {
|
|
|
779
966
|
wasm.__wbindgen_free(deferred1_0, deferred1_1, 1);
|
|
780
967
|
}
|
|
781
968
|
}
|
|
969
|
+
exports.version = version;
|
|
782
970
|
|
|
783
971
|
function __wbg_get_imports() {
|
|
784
972
|
const import0 = {
|
|
@@ -876,6 +1064,9 @@ const WasmDownscaleConfigFinalization = (typeof FinalizationRegistry === 'undefi
|
|
|
876
1064
|
const WasmDownscaleResultFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
877
1065
|
? { register: () => {}, unregister: () => {} }
|
|
878
1066
|
: new FinalizationRegistry(ptr => wasm.__wbg_wasmdownscaleresult_free(ptr >>> 0, 1));
|
|
1067
|
+
const WasmPreparedImageFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
1068
|
+
? { register: () => {}, unregister: () => {} }
|
|
1069
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_wasmpreparedimage_free(ptr >>> 0, 1));
|
|
879
1070
|
|
|
880
1071
|
function addToExternrefTable0(obj) {
|
|
881
1072
|
const idx = wasm.__externref_table_alloc();
|
|
@@ -965,15 +1156,7 @@ function takeFromExternrefTable0(idx) {
|
|
|
965
1156
|
|
|
966
1157
|
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
967
1158
|
cachedTextDecoder.decode();
|
|
968
|
-
const MAX_SAFARI_DECODE_BYTES = 2146435072;
|
|
969
|
-
let numBytesDecoded = 0;
|
|
970
1159
|
function decodeText(ptr, len) {
|
|
971
|
-
numBytesDecoded += len;
|
|
972
|
-
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
|
|
973
|
-
cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
974
|
-
cachedTextDecoder.decode();
|
|
975
|
-
numBytesDecoded = len;
|
|
976
|
-
}
|
|
977
1160
|
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
978
1161
|
}
|
|
979
1162
|
|
|
@@ -992,94 +1175,8 @@ if (!('encodeInto' in cachedTextEncoder)) {
|
|
|
992
1175
|
|
|
993
1176
|
let WASM_VECTOR_LEN = 0;
|
|
994
1177
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
wasm.__wbindgen_start();
|
|
1001
|
-
return wasm;
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
async function __wbg_load(module, imports) {
|
|
1005
|
-
if (typeof Response === 'function' && module instanceof Response) {
|
|
1006
|
-
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
|
1007
|
-
try {
|
|
1008
|
-
return await WebAssembly.instantiateStreaming(module, imports);
|
|
1009
|
-
} catch (e) {
|
|
1010
|
-
const validResponse = module.ok && expectedResponseType(module.type);
|
|
1011
|
-
|
|
1012
|
-
if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
|
|
1013
|
-
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
|
|
1014
|
-
|
|
1015
|
-
} else { throw e; }
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
const bytes = await module.arrayBuffer();
|
|
1020
|
-
return await WebAssembly.instantiate(bytes, imports);
|
|
1021
|
-
} else {
|
|
1022
|
-
const instance = await WebAssembly.instantiate(module, imports);
|
|
1023
|
-
|
|
1024
|
-
if (instance instanceof WebAssembly.Instance) {
|
|
1025
|
-
return { instance, module };
|
|
1026
|
-
} else {
|
|
1027
|
-
return instance;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
function expectedResponseType(type) {
|
|
1032
|
-
switch (type) {
|
|
1033
|
-
case 'basic': case 'cors': case 'default': return true;
|
|
1034
|
-
}
|
|
1035
|
-
return false;
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
|
|
1039
|
-
function initSync(module) {
|
|
1040
|
-
if (wasm !== undefined) return wasm;
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
if (module !== undefined) {
|
|
1044
|
-
if (Object.getPrototypeOf(module) === Object.prototype) {
|
|
1045
|
-
({module} = module)
|
|
1046
|
-
} else {
|
|
1047
|
-
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
const imports = __wbg_get_imports();
|
|
1052
|
-
if (!(module instanceof WebAssembly.Module)) {
|
|
1053
|
-
module = new WebAssembly.Module(module);
|
|
1054
|
-
}
|
|
1055
|
-
const instance = new WebAssembly.Instance(module, imports);
|
|
1056
|
-
return __wbg_finalize_init(instance, module);
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
async function __wbg_init(module_or_path) {
|
|
1060
|
-
if (wasm !== undefined) return wasm;
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
if (module_or_path !== undefined) {
|
|
1064
|
-
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
|
|
1065
|
-
({module_or_path} = module_or_path)
|
|
1066
|
-
} else {
|
|
1067
|
-
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
if (module_or_path === undefined) {
|
|
1072
|
-
module_or_path = new URL('smart_downscaler_bg.wasm', import.meta.url);
|
|
1073
|
-
}
|
|
1074
|
-
const imports = __wbg_get_imports();
|
|
1075
|
-
|
|
1076
|
-
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
|
|
1077
|
-
module_or_path = fetch(module_or_path);
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
const { instance, module } = await __wbg_load(await module_or_path, imports);
|
|
1081
|
-
|
|
1082
|
-
return __wbg_finalize_init(instance, module);
|
|
1083
|
-
}
|
|
1084
|
-
|
|
1085
|
-
export { initSync, __wbg_init as default };
|
|
1178
|
+
const wasmPath = `${__dirname}/smart_downscaler_bg.wasm`;
|
|
1179
|
+
const wasmBytes = require('fs').readFileSync(wasmPath);
|
|
1180
|
+
const wasmModule = new WebAssembly.Module(wasmBytes);
|
|
1181
|
+
const wasm = new WebAssembly.Instance(wasmModule, __wbg_get_imports()).exports;
|
|
1182
|
+
wasm.__wbindgen_start();
|
package/smart_downscaler_bg.wasm
CHANGED
|
Binary file
|