@radialapps/memories-vips-editor 0.1.3 → 0.1.6
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 +17 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/math.d.ts +25 -0
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +35 -0
- package/dist/math.js.map +1 -1
- package/dist/pipeline.d.ts +7 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +23 -1
- package/dist/pipeline.js.map +1 -1
- package/dist/smallPreview.d.ts +31 -0
- package/dist/smallPreview.d.ts.map +1 -0
- package/dist/smallPreview.js +118 -0
- package/dist/smallPreview.js.map +1 -0
- package/dist/transforms/flip.d.ts +18 -0
- package/dist/transforms/flip.d.ts.map +1 -0
- package/dist/transforms/flip.js +43 -0
- package/dist/transforms/flip.js.map +1 -0
- package/dist/transforms/highlights.d.ts +17 -0
- package/dist/transforms/highlights.d.ts.map +1 -0
- package/dist/transforms/highlights.js +49 -0
- package/dist/transforms/highlights.js.map +1 -0
- package/dist/transforms/index.d.ts.map +1 -1
- package/dist/transforms/index.js +8 -0
- package/dist/transforms/index.js.map +1 -1
- package/dist/transforms/shadows.d.ts +17 -0
- package/dist/transforms/shadows.d.ts.map +1 -0
- package/dist/transforms/shadows.js +49 -0
- package/dist/transforms/shadows.js.map +1 -0
- package/dist/transforms/warmth.d.ts +17 -0
- package/dist/transforms/warmth.d.ts.map +1 -0
- package/dist/transforms/warmth.js +46 -0
- package/dist/transforms/warmth.js.map +1 -0
- package/dist/types.d.ts +6 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -3
- package/src/math.ts +40 -0
- package/src/pipeline.ts +25 -1
- package/src/smallPreview.ts +120 -0
- package/src/transforms/flip.ts +64 -0
- package/src/transforms/highlights.ts +67 -0
- package/src/transforms/index.ts +8 -0
- package/src/transforms/shadows.ts +67 -0
- package/src/transforms/warmth.ts +64 -0
- package/src/types.ts +14 -2
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// highlights.ts — boost/recover the bright end, weighted by brightness.
|
|
2
|
+
//
|
|
3
|
+
// weight w = clamp(avg,0,1)^2 (0 in blacks, 1 in whites, quadratic falloff),
|
|
4
|
+
// then rgb += (amount * 0.5) * w. The 0.5 keeps the gray curve monotonic over
|
|
5
|
+
// the whole slider range (a full-strength recovery would push white below
|
|
6
|
+
// near-white). Same formula in src/transforms/highlights.h and
|
|
7
|
+
// math.ts applyHighlights, so the canvas preview is WYSIWYG.
|
|
8
|
+
import { clampf } from '../math.js';
|
|
9
|
+
import {
|
|
10
|
+
isNumInRange,
|
|
11
|
+
setFloatUniform,
|
|
12
|
+
type ParamControl,
|
|
13
|
+
type RenderContext,
|
|
14
|
+
type Target,
|
|
15
|
+
type Transform,
|
|
16
|
+
} from './transform.js';
|
|
17
|
+
|
|
18
|
+
export interface HighlightsEdit {
|
|
19
|
+
edit: 'highlights';
|
|
20
|
+
/** -1 (recover highlights) .. 1 (boost highlights). Default 0. */
|
|
21
|
+
amount?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Full = Required<HighlightsEdit>;
|
|
25
|
+
|
|
26
|
+
const FRAG = `
|
|
27
|
+
precision mediump float;
|
|
28
|
+
uniform sampler2D texture;
|
|
29
|
+
uniform float amount;
|
|
30
|
+
varying vec2 texCoord;
|
|
31
|
+
void main() {
|
|
32
|
+
vec4 color = texture2D(texture, texCoord);
|
|
33
|
+
float avg = (color.r + color.g + color.b) / 3.0;
|
|
34
|
+
float l = clamp(avg, 0.0, 1.0);
|
|
35
|
+
float w = l * l;
|
|
36
|
+
color.rgb += (amount * 0.5) * w;
|
|
37
|
+
gl_FragColor = color;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
export class HighlightsTransform implements Transform<'highlights', HighlightsEdit> {
|
|
42
|
+
public readonly edit = 'highlights' as const;
|
|
43
|
+
public controls: ParamControl[] = [
|
|
44
|
+
{ key: 'amount', label: 'amount', min: -1, max: 1, step: 0.01, def: 0 },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
public defaults(): Full {
|
|
48
|
+
return { edit: 'highlights', amount: 0 };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public validate(params: HighlightsEdit): string[] {
|
|
52
|
+
if (params.amount !== undefined && !isNumInRange(params.amount, -1, 1))
|
|
53
|
+
return ['amount must be a number in [-1,1]'];
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public normalize(params: HighlightsEdit): Full {
|
|
58
|
+
return { edit: 'highlights', amount: clampf(-1, params.amount ?? 0, 1) };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public apply(ctx: RenderContext, from: Target, to: Target, params: Full): Target {
|
|
62
|
+
ctx.pass(FRAG, from, to, (gl, p) => {
|
|
63
|
+
setFloatUniform(gl, p, 'amount', params.amount);
|
|
64
|
+
});
|
|
65
|
+
return to;
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/transforms/index.ts
CHANGED
|
@@ -9,14 +9,18 @@ import { BlurTransform } from './blur.js';
|
|
|
9
9
|
import { BrightnessContrastTransform } from './brightnessContrast.js';
|
|
10
10
|
import { CropTransform } from './crop.js';
|
|
11
11
|
import { ExposureTransform } from './exposure.js';
|
|
12
|
+
import { FlipTransform } from './flip.js';
|
|
12
13
|
import { GrayscaleTransform } from './grayscale.js';
|
|
14
|
+
import { HighlightsTransform } from './highlights.js';
|
|
13
15
|
import { HueSaturationTransform } from './hueSaturation.js';
|
|
14
16
|
import { InvertTransform } from './invert.js';
|
|
15
17
|
import { RotateTransform } from './rotate.js';
|
|
16
18
|
import { SepiaTransform } from './sepia.js';
|
|
19
|
+
import { ShadowsTransform } from './shadows.js';
|
|
17
20
|
import { SharpenTransform } from './sharpen.js';
|
|
18
21
|
import { VibranceTransform } from './vibrance.js';
|
|
19
22
|
import { VignetteTransform } from './vignette.js';
|
|
23
|
+
import { WarmthTransform } from './warmth.js';
|
|
20
24
|
|
|
21
25
|
export const TRANSFORMS: Record<EditName, Transform<string, object>> = {
|
|
22
26
|
brightnessContrast: new BrightnessContrastTransform(),
|
|
@@ -27,10 +31,14 @@ export const TRANSFORMS: Record<EditName, Transform<string, object>> = {
|
|
|
27
31
|
blur: new BlurTransform(),
|
|
28
32
|
sharpen: new SharpenTransform(),
|
|
29
33
|
vignette: new VignetteTransform(),
|
|
34
|
+
shadows: new ShadowsTransform(),
|
|
35
|
+
highlights: new HighlightsTransform(),
|
|
36
|
+
warmth: new WarmthTransform(),
|
|
30
37
|
invert: new InvertTransform(),
|
|
31
38
|
grayscale: new GrayscaleTransform(),
|
|
32
39
|
crop: new CropTransform(),
|
|
33
40
|
rotate: new RotateTransform(),
|
|
41
|
+
flip: new FlipTransform(),
|
|
34
42
|
};
|
|
35
43
|
|
|
36
44
|
/** Human-readable problems; empty means the edit is VIPS-appliable. */
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// shadows.ts — lift/crush the dark end, weighted by darkness.
|
|
2
|
+
//
|
|
3
|
+
// weight w = (1 - clamp(avg,0,1))^2 (1 in blacks, 0 in whites, quadratic
|
|
4
|
+
// falloff), then rgb += (amount * 0.5) * w. The 0.5 keeps the gray curve
|
|
5
|
+
// monotonic over the whole slider range (a full-strength lift would reverse
|
|
6
|
+
// deep shadows past mid-gray). Same formula in src/transforms/shadows.h
|
|
7
|
+
// and math.ts applyShadows, so the canvas preview is WYSIWYG.
|
|
8
|
+
import { clampf } from '../math.js';
|
|
9
|
+
import {
|
|
10
|
+
isNumInRange,
|
|
11
|
+
setFloatUniform,
|
|
12
|
+
type ParamControl,
|
|
13
|
+
type RenderContext,
|
|
14
|
+
type Target,
|
|
15
|
+
type Transform,
|
|
16
|
+
} from './transform.js';
|
|
17
|
+
|
|
18
|
+
export interface ShadowsEdit {
|
|
19
|
+
edit: 'shadows';
|
|
20
|
+
/** -1 (crush shadows) .. 1 (lift shadows). Default 0. */
|
|
21
|
+
amount?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
type Full = Required<ShadowsEdit>;
|
|
25
|
+
|
|
26
|
+
const FRAG = `
|
|
27
|
+
precision mediump float;
|
|
28
|
+
uniform sampler2D texture;
|
|
29
|
+
uniform float amount;
|
|
30
|
+
varying vec2 texCoord;
|
|
31
|
+
void main() {
|
|
32
|
+
vec4 color = texture2D(texture, texCoord);
|
|
33
|
+
float avg = (color.r + color.g + color.b) / 3.0;
|
|
34
|
+
float l = clamp(avg, 0.0, 1.0);
|
|
35
|
+
float w = (1.0 - l) * (1.0 - l);
|
|
36
|
+
color.rgb += (amount * 0.5) * w;
|
|
37
|
+
gl_FragColor = color;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
export class ShadowsTransform implements Transform<'shadows', ShadowsEdit> {
|
|
42
|
+
public readonly edit = 'shadows' as const;
|
|
43
|
+
public controls: ParamControl[] = [
|
|
44
|
+
{ key: 'amount', label: 'amount', min: -1, max: 1, step: 0.01, def: 0 },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
public defaults(): Full {
|
|
48
|
+
return { edit: 'shadows', amount: 0 };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public validate(params: ShadowsEdit): string[] {
|
|
52
|
+
if (params.amount !== undefined && !isNumInRange(params.amount, -1, 1))
|
|
53
|
+
return ['amount must be a number in [-1,1]'];
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public normalize(params: ShadowsEdit): Full {
|
|
58
|
+
return { edit: 'shadows', amount: clampf(-1, params.amount ?? 0, 1) };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public apply(ctx: RenderContext, from: Target, to: Target, params: Full): Target {
|
|
62
|
+
ctx.pass(FRAG, from, to, (gl, p) => {
|
|
63
|
+
setFloatUniform(gl, p, 'amount', params.amount);
|
|
64
|
+
});
|
|
65
|
+
return to;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// warmth.ts — white-balance-style temperature shift (red/blue gains).
|
|
2
|
+
//
|
|
3
|
+
// r *= 1 + 0.3*amount, b *= 1 - 0.3*amount, g unchanged: +1 is warm
|
|
4
|
+
// (amber), -1 is cool (blue). Blacks stay black (multiplicative, like a
|
|
5
|
+
// white-balance gain). Same factors in src/transforms/warmth.h and
|
|
6
|
+
// math.ts warmthGains, so the canvas preview is WYSIWYG.
|
|
7
|
+
import { clampf } from '../math.js';
|
|
8
|
+
import {
|
|
9
|
+
isNumInRange,
|
|
10
|
+
setFloatUniform,
|
|
11
|
+
type ParamControl,
|
|
12
|
+
type RenderContext,
|
|
13
|
+
type Target,
|
|
14
|
+
type Transform,
|
|
15
|
+
} from './transform.js';
|
|
16
|
+
|
|
17
|
+
export interface WarmthEdit {
|
|
18
|
+
edit: 'warmth';
|
|
19
|
+
/** -1 (cool) .. 1 (warm). Default 0. */
|
|
20
|
+
amount?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type Full = Required<WarmthEdit>;
|
|
24
|
+
|
|
25
|
+
const FRAG = `
|
|
26
|
+
precision mediump float;
|
|
27
|
+
uniform sampler2D texture;
|
|
28
|
+
uniform float amount;
|
|
29
|
+
varying vec2 texCoord;
|
|
30
|
+
void main() {
|
|
31
|
+
vec4 color = texture2D(texture, texCoord);
|
|
32
|
+
color.r *= 1.0 + amount * 0.3;
|
|
33
|
+
color.b *= 1.0 - amount * 0.3;
|
|
34
|
+
gl_FragColor = color;
|
|
35
|
+
}
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
export class WarmthTransform implements Transform<'warmth', WarmthEdit> {
|
|
39
|
+
public readonly edit = 'warmth' as const;
|
|
40
|
+
public controls: ParamControl[] = [
|
|
41
|
+
{ key: 'amount', label: 'amount', min: -1, max: 1, step: 0.01, def: 0 },
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
public defaults(): Full {
|
|
45
|
+
return { edit: 'warmth', amount: 0 };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public validate(params: WarmthEdit): string[] {
|
|
49
|
+
if (params.amount !== undefined && !isNumInRange(params.amount, -1, 1))
|
|
50
|
+
return ['amount must be a number in [-1,1]'];
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public normalize(params: WarmthEdit): Full {
|
|
55
|
+
return { edit: 'warmth', amount: clampf(-1, params.amount ?? 0, 1) };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public apply(ctx: RenderContext, from: Target, to: Target, params: Full): Target {
|
|
59
|
+
ctx.pass(FRAG, from, to, (gl, p) => {
|
|
60
|
+
setFloatUniform(gl, p, 'amount', params.amount);
|
|
61
|
+
});
|
|
62
|
+
return to;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -9,14 +9,18 @@ import type { BlurEdit } from './transforms/blur.js';
|
|
|
9
9
|
import type { BrightnessContrastEdit } from './transforms/brightnessContrast.js';
|
|
10
10
|
import type { CropEdit } from './transforms/crop.js';
|
|
11
11
|
import type { ExposureEdit } from './transforms/exposure.js';
|
|
12
|
+
import type { FlipEdit } from './transforms/flip.js';
|
|
12
13
|
import type { GrayscaleEdit } from './transforms/grayscale.js';
|
|
14
|
+
import type { HighlightsEdit } from './transforms/highlights.js';
|
|
13
15
|
import type { HueSaturationEdit } from './transforms/hueSaturation.js';
|
|
14
16
|
import type { InvertEdit } from './transforms/invert.js';
|
|
15
17
|
import type { RotateEdit } from './transforms/rotate.js';
|
|
16
18
|
import type { SepiaEdit } from './transforms/sepia.js';
|
|
19
|
+
import type { ShadowsEdit } from './transforms/shadows.js';
|
|
17
20
|
import type { SharpenEdit } from './transforms/sharpen.js';
|
|
18
21
|
import type { VibranceEdit } from './transforms/vibrance.js';
|
|
19
22
|
import type { VignetteEdit } from './transforms/vignette.js';
|
|
23
|
+
import type { WarmthEdit } from './transforms/warmth.js';
|
|
20
24
|
|
|
21
25
|
export type EditName =
|
|
22
26
|
| 'brightnessContrast'
|
|
@@ -27,10 +31,14 @@ export type EditName =
|
|
|
27
31
|
| 'blur'
|
|
28
32
|
| 'sharpen'
|
|
29
33
|
| 'vignette'
|
|
34
|
+
| 'shadows'
|
|
35
|
+
| 'highlights'
|
|
36
|
+
| 'warmth'
|
|
30
37
|
| 'invert'
|
|
31
38
|
| 'grayscale'
|
|
32
39
|
| 'crop'
|
|
33
|
-
| 'rotate'
|
|
40
|
+
| 'rotate'
|
|
41
|
+
| 'flip';
|
|
34
42
|
|
|
35
43
|
export type Edit =
|
|
36
44
|
| BrightnessContrastEdit
|
|
@@ -41,10 +49,14 @@ export type Edit =
|
|
|
41
49
|
| BlurEdit
|
|
42
50
|
| SharpenEdit
|
|
43
51
|
| VignetteEdit
|
|
52
|
+
| ShadowsEdit
|
|
53
|
+
| HighlightsEdit
|
|
54
|
+
| WarmthEdit
|
|
44
55
|
| InvertEdit
|
|
45
56
|
| GrayscaleEdit
|
|
46
57
|
| CropEdit
|
|
47
|
-
| RotateEdit
|
|
58
|
+
| RotateEdit
|
|
59
|
+
| FlipEdit;
|
|
48
60
|
|
|
49
61
|
export const EDIT_STATE_VERSION = 1 as const;
|
|
50
62
|
|