@pixerate/editor 0.11.0 → 0.13.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.
@@ -0,0 +1,372 @@
1
+ interface ImageDimensions {
2
+ width: number;
3
+ height: number;
4
+ }
5
+ interface ImagePoint {
6
+ x: number;
7
+ y: number;
8
+ }
9
+ interface CropBox {
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ }
15
+ type AspectRatio = number | null;
16
+ interface ImageTransform {
17
+ rotate: number;
18
+ flipH: boolean;
19
+ flipV: boolean;
20
+ }
21
+ interface ImageAdjustments {
22
+ brightness: number;
23
+ contrast: number;
24
+ saturation: number;
25
+ exposure: number;
26
+ temperature: number;
27
+ blur: number;
28
+ opacity: number;
29
+ }
30
+ interface DepthMaskOptions {
31
+ enabled: boolean;
32
+ maskSource: string | null;
33
+ mode: 'threshold' | 'brightness';
34
+ depthRange: [number, number];
35
+ softness: number;
36
+ invert: boolean;
37
+ }
38
+ type AnnotationType = 'pen' | 'rect' | 'circle' | 'arrow' | 'line' | 'text' | 'image';
39
+ interface BaseAnnotation {
40
+ id: string;
41
+ type: AnnotationType;
42
+ x: number;
43
+ y: number;
44
+ rotation?: number;
45
+ opacity?: number;
46
+ }
47
+ interface PenAnnotation extends BaseAnnotation {
48
+ type: 'pen';
49
+ points: ImagePoint[];
50
+ strokeColor: string;
51
+ strokeWidth: number;
52
+ }
53
+ interface RectAnnotation extends BaseAnnotation {
54
+ type: 'rect';
55
+ width: number;
56
+ height: number;
57
+ strokeColor: string;
58
+ strokeWidth: number;
59
+ fillColor?: string;
60
+ cornerRadius?: number;
61
+ }
62
+ interface CircleAnnotation extends BaseAnnotation {
63
+ type: 'circle';
64
+ radiusX: number;
65
+ radiusY: number;
66
+ strokeColor: string;
67
+ strokeWidth: number;
68
+ fillColor?: string;
69
+ }
70
+ interface ArrowAnnotation extends BaseAnnotation {
71
+ type: 'arrow';
72
+ endX: number;
73
+ endY: number;
74
+ strokeColor: string;
75
+ strokeWidth: number;
76
+ }
77
+ interface LineAnnotation extends BaseAnnotation {
78
+ type: 'line';
79
+ endX: number;
80
+ endY: number;
81
+ strokeColor: string;
82
+ strokeWidth: number;
83
+ }
84
+ interface TextAnnotation extends BaseAnnotation {
85
+ type: 'text';
86
+ text: string;
87
+ fontSize: number;
88
+ fontFamily?: string;
89
+ color: string;
90
+ fontWeight?: string | number;
91
+ }
92
+ interface ImageAnnotation extends BaseAnnotation {
93
+ type: 'image';
94
+ src: string;
95
+ width: number;
96
+ height: number;
97
+ }
98
+ type Annotation = PenAnnotation | RectAnnotation | CircleAnnotation | ArrowAnnotation | LineAnnotation | TextAnnotation | ImageAnnotation;
99
+ type CreateAnnotationPayload = Omit<PenAnnotation, 'id'> | Omit<RectAnnotation, 'id'> | Omit<CircleAnnotation, 'id'> | Omit<ArrowAnnotation, 'id'> | Omit<LineAnnotation, 'id'> | Omit<TextAnnotation, 'id'> | Omit<ImageAnnotation, 'id'>;
100
+ type ImageEditorTool = 'select' | 'crop' | 'pen' | 'rect' | 'circle' | 'arrow' | 'line' | 'text' | 'watermark';
101
+ interface ImageEditorState {
102
+ sourceUrl: string | null;
103
+ imageDimensions: ImageDimensions;
104
+ crop: CropBox | null;
105
+ transform: ImageTransform;
106
+ adjustments: ImageAdjustments;
107
+ depthMask: DepthMaskOptions;
108
+ annotations: Annotation[];
109
+ selectedAnnotationId: string | null;
110
+ activeTool: ImageEditorTool;
111
+ zoom: number;
112
+ pan: ImagePoint;
113
+ isOriginalCompared: boolean;
114
+ }
115
+ interface SerializedImageEditorState {
116
+ version: 1;
117
+ crop: CropBox | null;
118
+ transform: ImageTransform;
119
+ adjustments: ImageAdjustments;
120
+ depthMask: DepthMaskOptions;
121
+ annotations: Annotation[];
122
+ }
123
+ interface ExportOptions {
124
+ format?: 'image/png' | 'image/jpeg' | 'image/webp';
125
+ quality?: number;
126
+ width?: number;
127
+ height?: number;
128
+ includeAnnotations?: boolean;
129
+ }
130
+ interface ImageEditorOptions {
131
+ image?: string | HTMLImageElement;
132
+ initialState?: Partial<SerializedImageEditorState>;
133
+ onStateChange?: (state: ImageEditorState) => void;
134
+ maxHistoryDepth?: number;
135
+ }
136
+ interface InpaintOptions {
137
+ prompt: string;
138
+ imageDataUrl: string;
139
+ maskDataUrl?: string;
140
+ cropBox?: CropBox | null;
141
+ }
142
+ interface RemoveBackgroundOptions {
143
+ imageDataUrl: string;
144
+ }
145
+ type InpaintHook = (options: InpaintOptions) => Promise<string | void> | string | void;
146
+ type RemoveBackgroundHook = (options: RemoveBackgroundOptions) => Promise<string | void> | string | void;
147
+ interface ImageEditorAIHooks {
148
+ onInpaint?: InpaintHook;
149
+ onRemoveBackground?: RemoveBackgroundHook;
150
+ }
151
+
152
+ /**
153
+ * Applies color and tone adjustments (brightness, contrast, saturation, exposure, temperature, opacity, blur)
154
+ * directly to the pixel data of an ImageData buffer.
155
+ */
156
+ declare function applyAdjustments(imageData: ImageData, adjustments: ImageAdjustments): void;
157
+ /**
158
+ * Blends a depth/alpha mask into the main image data based on Gleamforge depth masking.
159
+ * Supports threshold range with softness gradation, brightness mode, and optional inversion.
160
+ */
161
+ declare function applyDepthMask(mainImageData: ImageData, maskImageData: ImageData, options: DepthMaskOptions): void;
162
+ /**
163
+ * Removes alpha channel by setting all alpha bytes to 255 (completely opaque).
164
+ * From Gleamforge image-filters.ts.
165
+ */
166
+ declare function removeAlpha(imageData: ImageData): void;
167
+
168
+ interface RenderOptions {
169
+ viewportWidth?: number;
170
+ viewportHeight?: number;
171
+ showCropOverlay?: boolean;
172
+ showSelectionOverlay?: boolean;
173
+ interactive?: boolean;
174
+ draftAnnotation?: Annotation | null;
175
+ draftCrop?: CropBox | null;
176
+ }
177
+ /**
178
+ * Image Cache to avoid refetching HTMLImageElement objects repeatedly during re-renders.
179
+ */
180
+ declare class ImageCache {
181
+ private cache;
182
+ get(src: string): Promise<HTMLImageElement>;
183
+ set(src: string, img: HTMLImageElement): void;
184
+ clear(): void;
185
+ }
186
+ declare const globalImageCache: ImageCache;
187
+ /**
188
+ * Renders an image with all its transformations, filters, depth masks, and annotations
189
+ * to a target HTMLCanvasElement.
190
+ */
191
+ declare class ImageEditorRenderer {
192
+ imageCache: ImageCache;
193
+ private cachedBaseCanvas;
194
+ private cachedBaseKey;
195
+ /**
196
+ * Helper to create a canvas element safely in browser/JSDOM.
197
+ */
198
+ createCanvas(width: number, height: number): HTMLCanvasElement;
199
+ /**
200
+ * Renders the editor state onto a canvas (viewport or preview).
201
+ */
202
+ renderToCanvas(targetCanvas: HTMLCanvasElement, imageSource: CanvasImageSource | string, state: ImageEditorState, options?: RenderOptions): Promise<void>;
203
+ /**
204
+ * Draws an individual annotation onto the canvas.
205
+ */
206
+ drawAnnotation(ctx: CanvasRenderingContext2D, ann: Annotation): Promise<void>;
207
+ /**
208
+ * Draws selection box around active annotation.
209
+ */
210
+ private drawAnnotationSelection;
211
+ getAnnotationBounds(ann: Annotation): {
212
+ x: number;
213
+ y: number;
214
+ width: number;
215
+ height: number;
216
+ };
217
+ /**
218
+ * Draws a rule-of-thirds crop grid overlay.
219
+ */
220
+ private drawCropOverlay;
221
+ private drawFittedImage;
222
+ /**
223
+ * Performs an export render at exact pixel dimensions without zoom/pan viewport offsets.
224
+ */
225
+ renderExport(imageSource: CanvasImageSource | string, state: ImageEditorState, options?: ExportOptions): Promise<HTMLCanvasElement>;
226
+ /**
227
+ * Generates a binary mask canvas (white for mask/selected area, black for unmasked).
228
+ * Useful for AI inpainting and selective generative fill.
229
+ */
230
+ renderMask(state: ImageEditorState, options?: {
231
+ useAnnotations?: boolean;
232
+ useCrop?: boolean;
233
+ }): Promise<HTMLCanvasElement>;
234
+ }
235
+
236
+ declare const DEFAULT_ADJUSTMENTS: ImageAdjustments;
237
+ declare const DEFAULT_TRANSFORM: ImageTransform;
238
+ declare const DEFAULT_DEPTH_MASK: DepthMaskOptions;
239
+ /**
240
+ * ImageEditorController manages the complete lifecycle, canvas rendering pipeline,
241
+ * tool interaction states, undo/redo history, serialization, and image export.
242
+ */
243
+ declare class ImageEditorController {
244
+ private state;
245
+ private renderer;
246
+ private imageElement;
247
+ private listeners;
248
+ private onStateChange?;
249
+ private undoStack;
250
+ private redoStack;
251
+ private maxHistoryDepth;
252
+ private isApplyingHistory;
253
+ private initialSnapshot;
254
+ constructor(options?: ImageEditorOptions);
255
+ loadImage(src: string | HTMLImageElement): Promise<void>;
256
+ getImageElement(): CanvasImageSource | null;
257
+ getState(): ImageEditorState;
258
+ subscribe(listener: (state: ImageEditorState) => void): () => void;
259
+ private notify;
260
+ private recordHistory;
261
+ get canUndo(): boolean;
262
+ get canRedo(): boolean;
263
+ undo(): boolean;
264
+ redo(): boolean;
265
+ reset(): void;
266
+ setTool(tool: ImageEditorTool): void;
267
+ setCrop(crop: CropBox | null, addToHistory?: boolean): void;
268
+ applyCropPreset(ratio: AspectRatio): void;
269
+ resetCrop(): void;
270
+ rotate(degrees: number): void;
271
+ setRotation(degrees: number): void;
272
+ flipHorizontal(): void;
273
+ flipVertical(): void;
274
+ setAdjustments(partial: Partial<ImageAdjustments>, commitToHistory?: boolean): void;
275
+ resetAdjustments(): void;
276
+ setDepthMask(partial: Partial<DepthMaskOptions>): void;
277
+ resetDepthMask(): void;
278
+ addAnnotation(annotation: CreateAnnotationPayload): string;
279
+ updateAnnotation(id: string, partial: Partial<Annotation>, commitToHistory?: boolean): void;
280
+ removeAnnotation(id: string): void;
281
+ clearAnnotations(): void;
282
+ selectAnnotation(id: string | null): void;
283
+ setZoom(zoom: number): void;
284
+ zoomIn(factor?: number): void;
285
+ zoomOut(factor?: number): void;
286
+ resetZoom(): void;
287
+ setPan(pan: ImagePoint): void;
288
+ setComparing(comparing: boolean): void;
289
+ render(targetCanvas: HTMLCanvasElement, options?: RenderOptions): Promise<void>;
290
+ renderExport(options?: ExportOptions): Promise<HTMLCanvasElement>;
291
+ toDataURL(options?: ExportOptions): Promise<string>;
292
+ toBlob(options?: ExportOptions): Promise<Blob>;
293
+ renderMask(options?: {
294
+ useAnnotations?: boolean;
295
+ useCrop?: boolean;
296
+ }): Promise<HTMLCanvasElement>;
297
+ toMaskDataURL(options?: {
298
+ useAnnotations?: boolean;
299
+ useCrop?: boolean;
300
+ }): Promise<string>;
301
+ applyInpaintedImage(newDataUrl: string): Promise<void>;
302
+ applyBackgroundRemovedImage(newDataUrl: string): Promise<void>;
303
+ toJSON(): SerializedImageEditorState;
304
+ loadJSON(data: SerializedImageEditorState): void;
305
+ private serializeState;
306
+ private applySerializedState;
307
+ }
308
+
309
+ type CropHandle = 'nw' | 'ne' | 'se' | 'sw' | 'n' | 's' | 'e' | 'w' | 'inside' | null;
310
+ interface ViewportMetrics {
311
+ vpWidth: number;
312
+ vpHeight: number;
313
+ centerX: number;
314
+ centerY: number;
315
+ baseW: number;
316
+ baseH: number;
317
+ fitScale: number;
318
+ renderW: number;
319
+ renderH: number;
320
+ totalScale: number;
321
+ imageRect: {
322
+ x: number;
323
+ y: number;
324
+ width: number;
325
+ height: number;
326
+ };
327
+ cropRect: {
328
+ x: number;
329
+ y: number;
330
+ width: number;
331
+ height: number;
332
+ } | null;
333
+ }
334
+ /**
335
+ * Calculates current viewport projection metrics for canvas <-> image coordinate mapping.
336
+ */
337
+ declare function getViewportMetrics(state: ImageEditorState, vpWidth: number, vpHeight: number, isActivelyCropping?: boolean, cropOverride?: CropBox | null): ViewportMetrics;
338
+ /**
339
+ * Converts a point on the canvas (in internal canvas pixel space) to image/annotation pixel space.
340
+ */
341
+ declare function canvasToImagePoint(canvasPt: ImagePoint, state: ImageEditorState, vpWidth: number, vpHeight: number, isActivelyCropping?: boolean): ImagePoint;
342
+ /**
343
+ * Converts a point in image/annotation pixel space to internal canvas pixel space.
344
+ */
345
+ declare function imageToCanvasPoint(imgPt: ImagePoint, state: ImageEditorState, vpWidth: number, vpHeight: number, isActivelyCropping?: boolean): ImagePoint;
346
+ /**
347
+ * Hit-tests the crop box and its corner/edge handles given a canvas point.
348
+ */
349
+ declare function hitTestCrop(canvasPt: ImagePoint, state: ImageEditorState, vpWidth: number, vpHeight: number, handleThreshold?: number, cropOverride?: CropBox | null): CropHandle;
350
+ /**
351
+ * Returns CSS cursor style corresponding to a crop handle.
352
+ */
353
+ declare function getCropCursor(handle: CropHandle): string;
354
+ /**
355
+ * Calculates updated crop rectangle given a drag interaction.
356
+ */
357
+ declare function calculateCropDrag(startCrop: CropBox, handle: CropHandle, startImgPt: ImagePoint, currentImgPt: ImagePoint, imgWidth: number, imgHeight: number, aspectRatio?: number | null): CropBox;
358
+ /**
359
+ * Calculates bounding box of an annotation.
360
+ */
361
+ declare function getAnnotationBounds(ann: Annotation): {
362
+ x: number;
363
+ y: number;
364
+ width: number;
365
+ height: number;
366
+ };
367
+ /**
368
+ * Hit tests if a point in image space is within an annotation's bounds.
369
+ */
370
+ declare function hitTestAnnotation(imgPt: ImagePoint, ann: Annotation, padding?: number): boolean;
371
+
372
+ export { type Annotation, type AnnotationType, type ArrowAnnotation, type AspectRatio, type BaseAnnotation, type CircleAnnotation, type CreateAnnotationPayload, type CropBox, type CropHandle, DEFAULT_ADJUSTMENTS, DEFAULT_DEPTH_MASK, DEFAULT_TRANSFORM, type DepthMaskOptions, type ExportOptions, type ImageAdjustments, type ImageAnnotation, type ImageDimensions, type ImageEditorAIHooks, ImageEditorController, type ImageEditorOptions, ImageEditorRenderer, type ImageEditorState, type ImageEditorTool, type ImagePoint, type ImageTransform, type InpaintHook, type InpaintOptions, type LineAnnotation, type PenAnnotation, type RectAnnotation, type RemoveBackgroundHook, type RemoveBackgroundOptions, type RenderOptions, type SerializedImageEditorState, type TextAnnotation, type ViewportMetrics, applyAdjustments, applyDepthMask, calculateCropDrag, canvasToImagePoint, getAnnotationBounds, getCropCursor, getViewportMetrics, globalImageCache, hitTestAnnotation, hitTestCrop, imageToCanvasPoint, removeAlpha };