@yogiswara/honcho-editor-ui 2.7.13 → 2.7.14

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.
Files changed (139) hide show
  1. package/package.json +1 -1
  2. package/dist/color.d.ts +0 -9
  3. package/dist/color.js +0 -9
  4. package/dist/components/editor/GalleryAlbum/AlbumImageGallery.d.ts +0 -8
  5. package/dist/components/editor/GalleryAlbum/AlbumImageGallery.js +0 -28
  6. package/dist/components/editor/GalleryAlbum/ImageItem.d.ts +0 -10
  7. package/dist/components/editor/GalleryAlbum/ImageItem.js +0 -81
  8. package/dist/components/editor/HAccordionAspectRatio.d.ts +0 -14
  9. package/dist/components/editor/HAccordionAspectRatio.js +0 -102
  10. package/dist/components/editor/HAccordionColor.d.ts +0 -16
  11. package/dist/components/editor/HAccordionColor.js +0 -282
  12. package/dist/components/editor/HAccordionColorAdjustment.d.ts +0 -35
  13. package/dist/components/editor/HAccordionColorAdjustment.js +0 -31
  14. package/dist/components/editor/HAccordionDetails.d.ts +0 -12
  15. package/dist/components/editor/HAccordionDetails.js +0 -157
  16. package/dist/components/editor/HAccordionLight.d.ts +0 -20
  17. package/dist/components/editor/HAccordionLight.js +0 -414
  18. package/dist/components/editor/HAccordionPreset.d.ts +0 -23
  19. package/dist/components/editor/HAccordionPreset.js +0 -50
  20. package/dist/components/editor/HAlertBox.d.ts +0 -8
  21. package/dist/components/editor/HAlertBox.js +0 -55
  22. package/dist/components/editor/HAspectRatioMobile.d.ts +0 -0
  23. package/dist/components/editor/HAspectRatioMobile.js +0 -1
  24. package/dist/components/editor/HBulkAccordionColorAdjustment.d.ts +0 -55
  25. package/dist/components/editor/HBulkAccordionColorAdjustment.js +0 -31
  26. package/dist/components/editor/HBulkAccordionColorAdjustmentColors.d.ts +0 -20
  27. package/dist/components/editor/HBulkAccordionColorAdjustmentColors.js +0 -121
  28. package/dist/components/editor/HBulkAccordionColorAdjustmentDetails.d.ts +0 -12
  29. package/dist/components/editor/HBulkAccordionColorAdjustmentDetails.js +0 -65
  30. package/dist/components/editor/HBulkAccordionColorAdjustmentLight.d.ts +0 -28
  31. package/dist/components/editor/HBulkAccordionColorAdjustmentLight.js +0 -177
  32. package/dist/components/editor/HBulkColorAdjustmentMobile.d.ts +0 -53
  33. package/dist/components/editor/HBulkColorAdjustmentMobile.js +0 -16
  34. package/dist/components/editor/HBulkColorMobile.d.ts +0 -20
  35. package/dist/components/editor/HBulkColorMobile.js +0 -121
  36. package/dist/components/editor/HBulkDetailsMobile.d.ts +0 -12
  37. package/dist/components/editor/HBulkDetailsMobile.js +0 -65
  38. package/dist/components/editor/HBulkLightMobile.d.ts +0 -28
  39. package/dist/components/editor/HBulkLightMobile.js +0 -192
  40. package/dist/components/editor/HBulkPreset.d.ts +0 -24
  41. package/dist/components/editor/HBulkPreset.js +0 -43
  42. package/dist/components/editor/HBulkPresetMobile.d.ts +0 -15
  43. package/dist/components/editor/HBulkPresetMobile.js +0 -26
  44. package/dist/components/editor/HDialogBox.d.ts +0 -18
  45. package/dist/components/editor/HDialogBox.js +0 -51
  46. package/dist/components/editor/HDialogCopy.d.ts +0 -40
  47. package/dist/components/editor/HDialogCopy.js +0 -80
  48. package/dist/components/editor/HFooter.d.ts +0 -12
  49. package/dist/components/editor/HFooter.js +0 -24
  50. package/dist/components/editor/HHeaderEditor.d.ts +0 -17
  51. package/dist/components/editor/HHeaderEditor.js +0 -36
  52. package/dist/components/editor/HImageEditorBulkDekstop.d.ts +0 -15
  53. package/dist/components/editor/HImageEditorBulkDekstop.js +0 -29
  54. package/dist/components/editor/HImageEditorBulkMobile.d.ts +0 -72
  55. package/dist/components/editor/HImageEditorBulkMobile.js +0 -81
  56. package/dist/components/editor/HImageEditorDekstop.d.ts +0 -15
  57. package/dist/components/editor/HImageEditorDekstop.js +0 -29
  58. package/dist/components/editor/HImageEditorMobile.d.ts +0 -51
  59. package/dist/components/editor/HImageEditorMobile.js +0 -92
  60. package/dist/components/editor/HImageEditorMobileLayout.d.ts +0 -14
  61. package/dist/components/editor/HImageEditorMobileLayout.js +0 -58
  62. package/dist/components/editor/HModalEditorDekstop.d.ts +0 -13
  63. package/dist/components/editor/HModalEditorDekstop.js +0 -22
  64. package/dist/components/editor/HModalMobile.d.ts +0 -13
  65. package/dist/components/editor/HModalMobile.js +0 -7
  66. package/dist/components/editor/HPresetDelete.d.ts +0 -7
  67. package/dist/components/editor/HPresetDelete.js +0 -7
  68. package/dist/components/editor/HPresetOptionMenu.d.ts +0 -9
  69. package/dist/components/editor/HPresetOptionMenu.js +0 -20
  70. package/dist/components/editor/HSliderColorMobile.d.ts +0 -16
  71. package/dist/components/editor/HSliderColorMobile.js +0 -270
  72. package/dist/components/editor/HSliderDetailsMobile.d.ts +0 -12
  73. package/dist/components/editor/HSliderDetailsMobile.js +0 -154
  74. package/dist/components/editor/HSliderLightMobile.d.ts +0 -20
  75. package/dist/components/editor/HSliderLightMobile.js +0 -420
  76. package/dist/components/editor/HTabAspectRatioMobile.d.ts +0 -0
  77. package/dist/components/editor/HTabAspectRatioMobile.js +0 -1
  78. package/dist/components/editor/HTabColorAdjustmentMobile.d.ts +0 -33
  79. package/dist/components/editor/HTabColorAdjustmentMobile.js +0 -16
  80. package/dist/components/editor/HTabPresetMobile.d.ts +0 -14
  81. package/dist/components/editor/HTabPresetMobile.js +0 -10
  82. package/dist/components/editor/HTextField.d.ts +0 -14
  83. package/dist/components/editor/HTextField.js +0 -51
  84. package/dist/components/editor/HWatermarkView.d.ts +0 -6
  85. package/dist/components/editor/HWatermarkView.js +0 -16
  86. package/dist/components/editor/svg/Tick.d.ts +0 -2
  87. package/dist/components/editor/svg/Tick.js +0 -6
  88. package/dist/components/modal/HModalDialog.d.ts +0 -12
  89. package/dist/components/modal/HModalDialog.js +0 -18
  90. package/dist/components/modal/HModalRename.d.ts +0 -14
  91. package/dist/components/modal/HModalRename.js +0 -35
  92. package/dist/hooks/demo/HonchoEditorBulkDemo.d.ts +0 -3
  93. package/dist/hooks/demo/HonchoEditorBulkDemo.js +0 -410
  94. package/dist/hooks/demo/HonchoEditorSingleCleanDemo.d.ts +0 -3
  95. package/dist/hooks/demo/HonchoEditorSingleCleanDemo.js +0 -354
  96. package/dist/hooks/demo/index.d.ts +0 -2
  97. package/dist/hooks/demo/index.js +0 -2
  98. package/dist/hooks/editor/type.d.ts +0 -174
  99. package/dist/hooks/editor/type.js +0 -1
  100. package/dist/hooks/editor/useHonchoEditorBulk.d.ts +0 -96
  101. package/dist/hooks/editor/useHonchoEditorBulk.js +0 -427
  102. package/dist/hooks/editor/useHonchoEditorSingle.d.ts +0 -44
  103. package/dist/hooks/editor/useHonchoEditorSingle.js +0 -162
  104. package/dist/hooks/useAdjustmentHistory.d.ts +0 -97
  105. package/dist/hooks/useAdjustmentHistory.js +0 -493
  106. package/dist/hooks/useAdjustmentHistoryBatch.d.ts +0 -177
  107. package/dist/hooks/useAdjustmentHistoryBatch.js +0 -1189
  108. package/dist/hooks/useGallerySwipe.d.ts +0 -36
  109. package/dist/hooks/useGallerySwipe.js +0 -344
  110. package/dist/hooks/usePaging.d.ts +0 -89
  111. package/dist/hooks/usePaging.js +0 -211
  112. package/dist/hooks/usePreset.d.ts +0 -82
  113. package/dist/hooks/usePreset.js +0 -344
  114. package/dist/index.d.ts +0 -41
  115. package/dist/index.js +0 -44
  116. package/dist/lib/context/EditorContext.d.ts +0 -28
  117. package/dist/lib/context/EditorContext.js +0 -60
  118. package/dist/lib/context/EditorProcessingService.d.ts +0 -36
  119. package/dist/lib/context/EditorProcessingService.js +0 -249
  120. package/dist/lib/editor/honcho-editor.d.ts +0 -324
  121. package/dist/lib/editor/honcho-editor.js +0 -825
  122. package/dist/lib/hooks/useEditor.d.ts +0 -22
  123. package/dist/lib/hooks/useEditor.js +0 -35
  124. package/dist/lib/hooks/useEditorHeadless.d.ts +0 -34
  125. package/dist/lib/hooks/useEditorHeadless.js +0 -207
  126. package/dist/lib/hooks/useImageProcessor.d.ts +0 -18
  127. package/dist/lib/hooks/useImageProcessor.js +0 -113
  128. package/dist/setupTests.d.ts +0 -1
  129. package/dist/setupTests.js +0 -1
  130. package/dist/themes/colors.d.ts +0 -12
  131. package/dist/themes/colors.js +0 -12
  132. package/dist/themes/honchoTheme.d.ts +0 -25
  133. package/dist/themes/honchoTheme.js +0 -94
  134. package/dist/utils/adjustment.d.ts +0 -6
  135. package/dist/utils/adjustment.js +0 -48
  136. package/dist/utils/imageLoader.d.ts +0 -11
  137. package/dist/utils/imageLoader.js +0 -48
  138. package/dist/utils/isMobile.d.ts +0 -1
  139. package/dist/utils/isMobile.js +0 -5
@@ -1,825 +0,0 @@
1
- "use strict";
2
- /**
3
- * Honcho Photo Editor - JavaScript Wrapper
4
- *
5
- * This wrapper provides a clean interface to the C++ image processing engine.
6
- *
7
- * ARCHITECTURE: Dual-Path Processing
8
- * ================================
9
- *
10
- * FRONTEND (Real-time Preview):
11
- * - Uses GPU-to-GPU rendering via renderToCanvas()
12
- * - Zero memory copies for 60fps performance
13
- * - Optimized for interactive UI sliders
14
- *
15
- * BACKEND (Server Processing):
16
- * - Uses CPU memory path via getProcessedImageData()
17
- * - Full quality data export for server workflows
18
- * - Headless processing without WebGL context
19
- * - Batch processing and API endpoints
20
- *
21
- * Both paths use the same C++ processing core with identical results.
22
- * JavaScript handles all UI and canvas rendering.
23
- * C++ handles all image processing algorithms.
24
- */
25
- // Adjustment ranges for UI components
26
- const ADJUSTMENT_RANGES = {
27
- temperature: { min: -100, max: 100, default: 0, step: 1 },
28
- tint: { min: -100, max: 100, default: 0, step: 1 },
29
- saturation: { min: -100, max: 100, default: 0, step: 1 },
30
- vibrance: { min: -100, max: 100, default: 0, step: 1 },
31
- exposure: { min: -100, max: 100, default: 0, step: 1 },
32
- contrast: { min: -100, max: 100, default: 0, step: 1 },
33
- highlights: { min: -100, max: 100, default: 0, step: 1 },
34
- shadows: { min: -100, max: 100, default: 0, step: 1 },
35
- whites: { min: -100, max: 100, default: 0, step: 1 },
36
- blacks: { min: -100, max: 100, default: 0, step: 1 },
37
- clarity: { min: -100, max: 100, default: 0, step: 1 },
38
- sharpness: { min: -100, max: 100, default: 0, step: 1 }
39
- };
40
- class HonchoEditor {
41
- constructor() {
42
- this.wasmModule = null;
43
- this.isInitialized = false;
44
- this.currentImageData = null;
45
- this.canvas = null;
46
- this.currentWidth = 0;
47
- this.currentHeight = 0;
48
- }
49
- /**
50
- * Initialize the WASM module
51
- * @param {boolean} verbose - Enable verbose logging (default: false)
52
- */
53
- async initialize(verbose = false) {
54
- try {
55
- console.log('Initializing Honcho Photo Editor...');
56
- // Create a hidden canvas for WebGL context that C++ can find
57
- this.canvas = document.createElement('canvas');
58
- this.canvas.width = 1;
59
- this.canvas.height = 1;
60
- this.canvas.style.display = 'none';
61
- this.canvas.id = 'canvas'; // Use standard ID that C++ looks for
62
- document.body.appendChild(this.canvas);
63
- // Load the WASM module - Module is a factory function
64
- if (typeof Module !== 'undefined') {
65
- // Configure the module with the canvas
66
- this.wasmModule = await Module({
67
- canvas: this.canvas,
68
- noExitRuntime: true,
69
- noInitialRun: true
70
- });
71
- }
72
- else {
73
- throw new Error('WASM module not found - make sure to load honcho-editor.js first');
74
- }
75
- // Create the processor instance with verbose flag (this will initialize OpenGL)
76
- console.log('Creating processor...');
77
- // Check if the function exists first
78
- if (this.wasmModule && typeof this.wasmModule._createProcessor === 'function') {
79
- this.wasmModule._createProcessor(verbose);
80
- console.log('Successfully created processor with verbose =', verbose);
81
- }
82
- else {
83
- console.error('_createProcessor function not found!');
84
- if (this.wasmModule) {
85
- console.log('Available functions:', Object.keys(this.wasmModule));
86
- }
87
- throw new Error('_createProcessor function not available');
88
- }
89
- this.isInitialized = true;
90
- console.log('Honcho Photo Editor initialized successfully');
91
- return true;
92
- }
93
- catch (error) {
94
- console.error('Failed to initialize Honcho Photo Editor:', error);
95
- throw error;
96
- }
97
- }
98
- /**
99
- * Clean up resources before loading a new image
100
- */
101
- cleanupForNewImage() {
102
- if (!this.isInitialized || !this.wasmModule) {
103
- return;
104
- }
105
- try {
106
- // Reset any internal state
107
- this.currentImageData = null;
108
- this.currentWidth = 0;
109
- this.currentHeight = 0;
110
- // Call C++ cleanup if available
111
- if (typeof this.wasmModule._resetProcessor === 'function') {
112
- this.wasmModule._resetProcessor();
113
- }
114
- console.log('Cleaned up for new image');
115
- }
116
- catch (error) {
117
- console.warn('Warning during cleanup:', error);
118
- }
119
- }
120
- /**
121
- * Load image from File object (drag & drop, file input)
122
- */
123
- async loadImageFromFile(file) {
124
- if (!this.isInitialized || !this.wasmModule) {
125
- throw new Error('Editor not initialized');
126
- }
127
- // Clean up before loading new image
128
- this.cleanupForNewImage();
129
- return new Promise((resolve, reject) => {
130
- const reader = new FileReader();
131
- reader.onload = (e) => {
132
- const img = new Image();
133
- img.onload = () => {
134
- try {
135
- const size = this.loadImageFromImageElement(img);
136
- resolve(size);
137
- }
138
- catch (error) {
139
- reject(error);
140
- }
141
- };
142
- img.onerror = () => {
143
- reject(new Error('Failed to load image'));
144
- };
145
- img.src = e.target.result;
146
- };
147
- reader.onerror = () => {
148
- reject(new Error('Failed to read file'));
149
- };
150
- reader.readAsDataURL(file);
151
- });
152
- }
153
- /**
154
- * Load image from URL
155
- */
156
- async loadImageFromUrl(url) {
157
- if (!this.isInitialized || !this.wasmModule) {
158
- throw new Error('Editor not initialized');
159
- }
160
- // Clean up before loading new image
161
- this.cleanupForNewImage();
162
- return new Promise((resolve, reject) => {
163
- const img = new Image();
164
- img.onload = () => {
165
- try {
166
- const size = this.loadImageFromImageElement(img);
167
- resolve(size);
168
- }
169
- catch (error) {
170
- reject(error);
171
- }
172
- };
173
- img.onerror = () => {
174
- reject(new Error('Failed to load image from URL'));
175
- };
176
- img.crossOrigin = 'anonymous'; // Enable CORS
177
- img.src = url;
178
- });
179
- }
180
- /**
181
- * Load image from HTML Image element
182
- */
183
- loadImageFromImageElement(img) {
184
- if (!this.isInitialized || !this.wasmModule) {
185
- throw new Error('Editor not initialized');
186
- }
187
- // Clean up before loading new image
188
- this.cleanupForNewImage();
189
- // Create canvas to extract image data
190
- const canvas = document.createElement('canvas');
191
- canvas.width = img.width;
192
- canvas.height = img.height;
193
- const ctx = canvas.getContext('2d');
194
- if (!ctx) {
195
- throw new Error('Failed to get 2D context');
196
- }
197
- ctx.drawImage(img, 0, 0);
198
- const imageData = ctx.getImageData(0, 0, img.width, img.height);
199
- return this.loadImageFromImageData(imageData);
200
- }
201
- /**
202
- * Load image from ImageData
203
- */
204
- loadImageFromImageData(imageData) {
205
- if (!this.isInitialized || !this.wasmModule) {
206
- throw new Error('Editor not initialized');
207
- }
208
- // Clean up before loading new image (only if we have existing image data)
209
- if (this.currentImageData) {
210
- this.cleanupForNewImage();
211
- }
212
- try {
213
- // Validate input
214
- if (!imageData || !imageData.data || imageData.width <= 0 || imageData.height <= 0) {
215
- throw new Error('Invalid image data provided');
216
- }
217
- this.currentImageData = imageData;
218
- this.currentWidth = imageData.width;
219
- this.currentHeight = imageData.height;
220
- // Allocate memory in WASM
221
- const dataSize = imageData.data.length;
222
- const expectedSize = imageData.width * imageData.height * 4; // RGBA
223
- if (dataSize !== expectedSize) {
224
- throw new Error(`Image data size mismatch: expected ${expectedSize}, got ${dataSize}`);
225
- }
226
- console.log(`Allocating ${dataSize} bytes for ${imageData.width}x${imageData.height} image`);
227
- const dataPtr = this.wasmModule._malloc(dataSize);
228
- if (!dataPtr) {
229
- throw new Error('Failed to allocate memory in WASM');
230
- }
231
- // Copy image data to WASM memory
232
- this.wasmModule.HEAPU8.set(imageData.data, dataPtr);
233
- // Set image data in C++
234
- const result = this.wasmModule._setImageData(dataPtr, imageData.width, imageData.height, 4);
235
- // Free allocated memory
236
- this.wasmModule._free(dataPtr);
237
- if (!result) {
238
- throw new Error('C++ setImageData failed');
239
- }
240
- console.log(`Image loaded successfully: ${imageData.width}x${imageData.height}`);
241
- return {
242
- width: imageData.width,
243
- height: imageData.height
244
- };
245
- }
246
- catch (error) {
247
- console.error('Failed to load image data:', error);
248
- throw error;
249
- }
250
- }
251
- // Adjustment methods (all auto-trigger re-render)
252
- setExposure(value) {
253
- this.validateAdjustment('exposure', value);
254
- this.wasmModule?._setExposure(value);
255
- }
256
- setContrast(value) {
257
- this.validateAdjustment('contrast', value);
258
- this.wasmModule?._setContrast(value);
259
- }
260
- setHighlights(value) {
261
- this.validateAdjustment('highlights', value);
262
- this.wasmModule?._setHighlights(value);
263
- }
264
- setShadows(value) {
265
- this.validateAdjustment('shadows', value);
266
- this.wasmModule?._setShadows(value);
267
- }
268
- setSaturation(value) {
269
- this.validateAdjustment('saturation', value);
270
- this.wasmModule?._setSaturation(value);
271
- }
272
- setVibrance(value) {
273
- this.validateAdjustment('vibrance', value);
274
- this.wasmModule?._setVibrance(value);
275
- }
276
- setTemperature(value) {
277
- this.validateAdjustment('temperature', value);
278
- this.wasmModule?._setTemperature(value);
279
- }
280
- setTint(value) {
281
- this.validateAdjustment('tint', value);
282
- this.wasmModule?._setTint(value);
283
- }
284
- setBlacks(value) {
285
- this.validateAdjustment('blacks', value);
286
- if (this.wasmModule && typeof this.wasmModule._setBlacks === 'function') {
287
- this.wasmModule._setBlacks(value);
288
- }
289
- else {
290
- console.warn('setBlacks not available in current WASM build');
291
- }
292
- }
293
- setWhites(value) {
294
- this.validateAdjustment('whites', value);
295
- if (this.wasmModule && typeof this.wasmModule._setWhites === 'function') {
296
- this.wasmModule._setWhites(value);
297
- }
298
- else {
299
- console.warn('setWhites not available in current WASM build');
300
- }
301
- }
302
- setClarity(value) {
303
- this.validateAdjustment('clarity', value);
304
- if (this.wasmModule && typeof this.wasmModule._setClarity === 'function') {
305
- this.wasmModule._setClarity(value);
306
- }
307
- else {
308
- console.warn('setClarity not available in current WASM build');
309
- }
310
- }
311
- setSharpness(value) {
312
- this.validateAdjustment('sharpness', value);
313
- if (this.wasmModule && typeof this.wasmModule._setSharpness === 'function') {
314
- this.wasmModule._setSharpness(value);
315
- }
316
- else {
317
- console.warn('setSharpness not available in current WASM build');
318
- }
319
- }
320
- /**
321
- * Set multiple adjustments at once
322
- */
323
- setAdjustments(adjustments) {
324
- Object.entries(adjustments).forEach(([key, value]) => {
325
- if (value !== undefined) {
326
- const methodName = `set${key.charAt(0).toUpperCase() + key.slice(1)}`;
327
- if (typeof this[methodName] === 'function') {
328
- this[methodName](value);
329
- }
330
- }
331
- });
332
- }
333
- /**
334
- * Reset all adjustments to default values
335
- */
336
- resetAdjustments() {
337
- this.wasmModule?._resetAdjustments();
338
- }
339
- // ==========================================
340
- // FRAME FUNCTIONALITY - NEW FEATURE
341
- // ==========================================
342
- /**
343
- * Set a frame overlay from raw image data
344
- * @param {Uint8Array} data - Raw RGBA image data
345
- * @param {number} width - Frame width
346
- * @param {number} height - Frame height
347
- * @param {number} channels - Number of channels (usually 4 for RGBA)
348
- * @returns {boolean} - true if frame was set successfully
349
- */
350
- setFrame(data, width, height, channels = 4) {
351
- if (!this.isInitialized || !this.wasmModule) {
352
- throw new Error('Editor not initialized');
353
- }
354
- if (!this.currentImageData) {
355
- throw new Error('No image loaded. Load an image first before setting frame.');
356
- }
357
- try {
358
- // Allocate memory in WASM for frame data
359
- const frameSize = width * height * channels;
360
- const framePtr = this.wasmModule._malloc(frameSize);
361
- if (!framePtr) {
362
- throw new Error('Failed to allocate memory for frame data');
363
- }
364
- // Copy frame data to WASM memory
365
- this.wasmModule.HEAPU8.set(data, framePtr);
366
- // Call native setFrame function
367
- const success = this.wasmModule._setFrame(framePtr, width, height, channels);
368
- // Free allocated memory
369
- this.wasmModule._free(framePtr);
370
- if (success) {
371
- console.log(`✅ Frame set: ${width}x${height}`);
372
- return true;
373
- }
374
- else {
375
- console.error('❌ Failed to set frame');
376
- return false;
377
- }
378
- }
379
- catch (error) {
380
- console.error('Error setting frame:', error);
381
- return false;
382
- }
383
- }
384
- /**
385
- * Set a frame overlay from an ImageData object
386
- * The frame will be scaled to match the image size and composited on top
387
- * @param {ImageData} frameImageData - Frame image data (preferably with alpha channel)
388
- * @returns {boolean} - true if frame was set successfully
389
- */
390
- setFrameFromImageData(frameImageData) {
391
- if (!this.isInitialized || !this.wasmModule) {
392
- throw new Error('Editor not initialized');
393
- }
394
- if (!this.currentImageData) {
395
- throw new Error('No image loaded. Load an image first before setting frame.');
396
- }
397
- try {
398
- const { width, height, data } = frameImageData;
399
- // Allocate memory in WASM for frame data
400
- const frameSize = width * height * 4; // RGBA
401
- const framePtr = this.wasmModule._malloc(frameSize);
402
- if (!framePtr) {
403
- throw new Error('Failed to allocate memory for frame data');
404
- }
405
- // Copy frame data to WASM memory
406
- this.wasmModule.HEAPU8.set(data, framePtr);
407
- // Call native setFrame function
408
- const success = this.wasmModule._setFrame(framePtr, width, height, 4);
409
- // Free allocated memory
410
- this.wasmModule._free(framePtr);
411
- if (success) {
412
- console.log(`✅ Frame set: ${width}x${height}`);
413
- return true;
414
- }
415
- else {
416
- console.error('❌ Failed to set frame');
417
- return false;
418
- }
419
- }
420
- catch (error) {
421
- console.error('Error setting frame:', error);
422
- return false;
423
- }
424
- }
425
- /**
426
- * Set a frame overlay from an HTML Image element
427
- * @param {HTMLImageElement} frameImage - Frame image element
428
- * @returns {boolean} - true if frame was set successfully
429
- */
430
- setFrameFromImage(frameImage) {
431
- if (!frameImage.complete) {
432
- throw new Error('Frame image not loaded. Wait for image.onload before calling this method.');
433
- }
434
- // Create a canvas to extract ImageData
435
- const tempCanvas = document.createElement('canvas');
436
- const tempCtx = tempCanvas.getContext('2d');
437
- tempCanvas.width = frameImage.width || frameImage.naturalWidth;
438
- tempCanvas.height = frameImage.height || frameImage.naturalHeight;
439
- // Draw frame image to canvas
440
- tempCtx.drawImage(frameImage, 0, 0);
441
- // Extract ImageData
442
- const frameImageData = tempCtx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
443
- return this.setFrameFromImageData(frameImageData);
444
- }
445
- /**
446
- * Set a frame overlay from a Canvas element
447
- * @param {HTMLCanvasElement} frameCanvas - Frame canvas element
448
- * @returns {boolean} - true if frame was set successfully
449
- */
450
- setFrameFromCanvas(frameCanvas) {
451
- const ctx = frameCanvas.getContext('2d');
452
- const frameImageData = ctx.getImageData(0, 0, frameCanvas.width, frameCanvas.height);
453
- return this.setFrameFromImageData(frameImageData);
454
- }
455
- /**
456
- * Clear the current frame overlay
457
- */
458
- clearFrame() {
459
- if (!this.isInitialized || !this.wasmModule) {
460
- throw new Error('Editor not initialized');
461
- }
462
- if (this.wasmModule._clearFrame) {
463
- this.wasmModule._clearFrame();
464
- console.log('🧹 Frame cleared');
465
- }
466
- }
467
- /**
468
- * Check if a frame is currently set
469
- * @returns {boolean} - true if a frame is set
470
- */
471
- hasFrame() {
472
- if (!this.isInitialized || !this.wasmModule) {
473
- return false;
474
- }
475
- return this.wasmModule._hasFrame ? this.wasmModule._hasFrame() : false;
476
- }
477
- // ==========================================
478
- // CONVENIENCE METHODS
479
- // ==========================================
480
- /**
481
- * One-shot processing: load image, apply adjustments, get result
482
- * Most efficient for single image processing or batch operations
483
- *
484
- * @param {File|ImageData|HTMLImageElement} imageSource - Image source (File, ImageData, or Image element)
485
- * @param {Object} adjustments - Object containing adjustment values (-100 to 100)
486
- * @param {File|ImageData|HTMLImageElement|null} frameSource - Optional frame source
487
- * @returns {ImageData} - Processed image data
488
- *
489
- * @example
490
- * // Basic usage
491
- * const result = await editor.processImageOneShot(imageFile, {
492
- * exposure: 20,
493
- * contrast: 15,
494
- * saturation: 10
495
- * });
496
- *
497
- * // With frame
498
- * const result = await editor.processImageOneShot(imageFile, {
499
- * exposure: 20,
500
- * contrast: 15
501
- * }, frameFile);
502
- *
503
- * // All adjustments
504
- * const result = await editor.processImageOneShot(imageFile, {
505
- * temperature: 10,
506
- * tint: -5,
507
- * exposure: 20,
508
- * contrast: 15,
509
- * highlights: -20,
510
- * shadows: 30,
511
- * saturation: 25,
512
- * vibrance: 20,
513
- * whites: 10,
514
- * blacks: -10,
515
- * clarity: 15,
516
- * sharpness: 20
517
- * });
518
- */
519
- async processImageOneShot(imageSource, adjustments = {}, frameSource = null) {
520
- if (!this.isInitialized || !this.wasmModule) {
521
- throw new Error('Editor not initialized');
522
- }
523
- try {
524
- // Step 1: Load the image
525
- console.log('📁 Loading image for one-shot processing...');
526
- let imageSize;
527
- if (imageSource instanceof File) {
528
- imageSize = await this.loadImageFromFile(imageSource);
529
- }
530
- else if (imageSource instanceof ImageData) {
531
- imageSize = this.loadImageFromImageData(imageSource);
532
- }
533
- else if (imageSource instanceof HTMLImageElement) {
534
- imageSize = this.loadImageFromImageElement(imageSource);
535
- }
536
- else {
537
- throw new Error('Unsupported image source type. Use File, ImageData, or HTMLImageElement.');
538
- }
539
- console.log(`✅ Image loaded: ${imageSize.width}×${imageSize.height}`);
540
- // Step 2: Apply frame if provided
541
- if (frameSource) {
542
- console.log('🖼️ Applying frame...');
543
- if (frameSource instanceof File) {
544
- await this.setFrameFromFile(frameSource);
545
- }
546
- else if (frameSource instanceof ImageData) {
547
- this.setFrameFromImageData(frameSource);
548
- }
549
- else if (frameSource instanceof HTMLImageElement) {
550
- this.setFrameFromImage(frameSource);
551
- }
552
- else {
553
- throw new Error('Unsupported frame source type. Use File, ImageData, or HTMLImageElement.');
554
- }
555
- console.log('✅ Frame applied successfully');
556
- }
557
- // Step 3: Apply all adjustments
558
- console.log('🎛️ Applying adjustments:', adjustments);
559
- const validAdjustments = [
560
- 'temperature', 'tint', 'exposure', 'contrast', 'highlights',
561
- 'shadows', 'saturation', 'vibrance', 'whites', 'blacks',
562
- 'clarity', 'sharpness'
563
- ];
564
- validAdjustments.forEach(name => {
565
- if (adjustments[name] !== undefined) {
566
- const methodName = `set${name.charAt(0).toUpperCase() + name.slice(1)}`;
567
- if (typeof this[methodName] === 'function') {
568
- this[methodName](adjustments[name]);
569
- }
570
- }
571
- });
572
- // Step 4: Process the image
573
- console.log('⚙️ Processing image...');
574
- this.processImage();
575
- // Step 5: Get the processed result
576
- console.log('📤 Exporting processed image data...');
577
- const result = this.getProcessedImageData();
578
- console.log('✅ One-shot processing complete');
579
- return result;
580
- }
581
- catch (error) {
582
- console.error('❌ One-shot processing failed:', error);
583
- throw error;
584
- }
585
- }
586
- /**
587
- * Helper method to load frame from File (for processImageOneShot)
588
- */
589
- async setFrameFromFile(frameFile) {
590
- return new Promise((resolve, reject) => {
591
- const img = new Image();
592
- img.onload = () => {
593
- try {
594
- this.setFrameFromImage(img);
595
- resolve();
596
- }
597
- catch (error) {
598
- reject(error);
599
- }
600
- };
601
- img.onerror = () => reject(new Error('Failed to load frame image'));
602
- const reader = new FileReader();
603
- reader.onload = (e) => {
604
- img.src = e.target.result;
605
- };
606
- reader.onerror = () => reject(new Error('Failed to read frame file'));
607
- reader.readAsDataURL(frameFile);
608
- });
609
- }
610
- /**
611
- * Manual processing trigger - call this after setting adjustments
612
- */
613
- processImage() {
614
- if (this.wasmModule && typeof this.wasmModule._processImage === 'function') {
615
- this.wasmModule._processImage();
616
- }
617
- else {
618
- console.warn('processImage not available in current WASM build');
619
- }
620
- }
621
- /**
622
- * GPU Path: Render to canvas for real-time preview (<1ms)
623
- */
624
- renderToCanvas(canvas) {
625
- if (!this.isInitialized || !this.wasmModule) {
626
- throw new Error('Editor not initialized');
627
- }
628
- if (!this.currentImageData) {
629
- throw new Error('No image loaded');
630
- }
631
- try {
632
- // Resize canvas to match image if needed
633
- if (canvas.width !== this.currentWidth || canvas.height !== this.currentHeight) {
634
- canvas.width = this.currentWidth;
635
- canvas.height = this.currentHeight;
636
- }
637
- // Update the C++ canvas size to match our target canvas
638
- if (this.canvas) {
639
- this.canvas.width = this.currentWidth;
640
- this.canvas.height = this.currentHeight;
641
- }
642
- // Trigger GPU rendering in C++
643
- this.wasmModule._renderToDisplay();
644
- // Copy from C++ canvas to target canvas
645
- const ctx = canvas.getContext('2d');
646
- if (ctx && this.canvas) {
647
- ctx.drawImage(this.canvas, 0, 0);
648
- }
649
- }
650
- catch (error) {
651
- console.error('Failed to render to canvas:', error);
652
- throw error;
653
- }
654
- }
655
- /**
656
- * CPU Path: Get processed image data for export (50-100ms)
657
- */
658
- getProcessedImageData() {
659
- if (!this.isInitialized || !this.wasmModule) {
660
- throw new Error('Editor not initialized');
661
- }
662
- if (!this.currentImageData) {
663
- throw new Error('No image loaded');
664
- }
665
- try {
666
- // Get processed image data from C++
667
- const dataPtr = this.wasmModule._getProcessedImageData();
668
- if (dataPtr === 0) {
669
- throw new Error('Failed to get processed image data');
670
- }
671
- // Copy data from WASM memory
672
- const dataSize = this.currentWidth * this.currentHeight * 4;
673
- const processedData = new Uint8ClampedArray(dataSize);
674
- processedData.set(this.wasmModule.HEAPU8.subarray(dataPtr, dataPtr + dataSize));
675
- // Create ImageData object
676
- const imageData = new ImageData(processedData, this.currentWidth, this.currentHeight);
677
- return imageData;
678
- }
679
- catch (error) {
680
- console.error('Failed to get processed image data:', error);
681
- throw error;
682
- }
683
- }
684
- /**
685
- * Get current image dimensions
686
- */
687
- getImageSize() {
688
- return {
689
- width: this.currentWidth,
690
- height: this.currentHeight
691
- };
692
- }
693
- /**
694
- * Get current image width
695
- */
696
- getWidth() {
697
- return this.currentWidth || 0;
698
- }
699
- /**
700
- * Get current image height
701
- */
702
- getHeight() {
703
- return this.currentHeight || 0;
704
- }
705
- /**
706
- * Check if editor is initialized
707
- */
708
- getInitialized() {
709
- return this.isInitialized;
710
- }
711
- /**
712
- * Get raw WASM module (advanced usage)
713
- */
714
- getRawModule() {
715
- return this.wasmModule;
716
- }
717
- /**
718
- * Validate adjustment value against range
719
- */
720
- validateAdjustment(name, value) {
721
- const range = ADJUSTMENT_RANGES[name];
722
- if (!range) {
723
- throw new Error(`Unknown adjustment: ${name}`);
724
- }
725
- if (value < range.min || value > range.max) {
726
- throw new Error(`${name} value ${value} out of range [${range.min}, ${range.max}]`);
727
- }
728
- }
729
- /**
730
- * Cleanup resources
731
- */
732
- cleanup() {
733
- if (this.wasmModule) {
734
- this.wasmModule._destroyProcessor();
735
- }
736
- if (this.canvas && this.canvas.parentNode) {
737
- this.canvas.parentNode.removeChild(this.canvas);
738
- }
739
- this.isInitialized = false;
740
- this.wasmModule = null;
741
- this.currentImageData = null;
742
- this.canvas = null;
743
- }
744
- }
745
- // Export for convenience
746
- // export default HonchoEditor; // Disabled for browser compatibility
747
- // Helper functions for common operations
748
- const HonchoEditorUtils = {
749
- /**
750
- * Convert ImageData to Blob for download
751
- */
752
- async imageDataToBlob(imageData, format = 'jpeg', quality = 0.9) {
753
- const canvas = document.createElement('canvas');
754
- canvas.width = imageData.width;
755
- canvas.height = imageData.height;
756
- const ctx = canvas.getContext('2d');
757
- if (!ctx) {
758
- throw new Error('Failed to get 2D context');
759
- }
760
- ctx.putImageData(imageData, 0, 0);
761
- const mimeType = `image/${format}`;
762
- return new Promise((resolve, reject) => {
763
- canvas.toBlob((blob) => {
764
- if (blob) {
765
- resolve(blob);
766
- }
767
- else {
768
- reject(new Error('Failed to create blob'));
769
- }
770
- }, mimeType, quality);
771
- });
772
- },
773
- /**
774
- * Download blob as file
775
- */
776
- downloadBlob(blob, filename) {
777
- const url = URL.createObjectURL(blob);
778
- const a = document.createElement('a');
779
- a.href = url;
780
- a.download = filename;
781
- a.click();
782
- URL.revokeObjectURL(url);
783
- },
784
- /**
785
- * Create debounced function for performance optimization
786
- */
787
- debounce(func, wait) {
788
- let timeout;
789
- return function executedFunction(...args) {
790
- const later = () => {
791
- clearTimeout(timeout);
792
- func(...args);
793
- };
794
- clearTimeout(timeout);
795
- timeout = setTimeout(later, wait);
796
- };
797
- },
798
- /**
799
- * Validate image file type
800
- */
801
- isValidImageFile(file) {
802
- const validTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/bmp'];
803
- return validTypes.includes(file.type);
804
- },
805
- /**
806
- * Get file size in human readable format
807
- */
808
- formatFileSize(bytes) {
809
- if (bytes === 0)
810
- return '0 Bytes';
811
- const k = 1024;
812
- const sizes = ['Bytes', 'KB', 'MB', 'GB'];
813
- const i = Math.floor(Math.log(bytes) / Math.log(k));
814
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
815
- }
816
- };
817
- // Export for both Node.js and browser
818
- if (typeof module !== 'undefined' && module.exports) {
819
- module.exports = { HonchoEditor, HonchoEditorUtils, ADJUSTMENT_RANGES };
820
- }
821
- else {
822
- window.HonchoEditor = HonchoEditor;
823
- window.HonchoEditorUtils = HonchoEditorUtils;
824
- window.ADJUSTMENT_RANGES = ADJUSTMENT_RANGES;
825
- }