@umituz/react-native-image 1.2.5 → 1.3.2
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/package.json +6 -6
- package/src/index.ts +5 -34
- package/src/infrastructure/services/ImageAnnotationService.ts +0 -189
- package/src/infrastructure/services/ImageFilterService.ts +0 -168
- package/src/infrastructure/utils/CanvasRenderingService.ts +0 -134
- package/src/infrastructure/utils/CropTool.ts +0 -260
- package/src/infrastructure/utils/DrawingEngine.ts +0 -210
- package/src/infrastructure/utils/FilterEffects.ts +0 -51
- package/src/infrastructure/utils/ShapeRenderer.ts +0 -168
- package/src/infrastructure/utils/TextEditor.ts +0 -273
- package/src/presentation/components/CropComponent.tsx +0 -183
- package/src/presentation/components/Editor.tsx +0 -261
- package/src/presentation/components/EditorCanvas.tsx +0 -135
- package/src/presentation/components/EditorPanel.tsx +0 -321
- package/src/presentation/components/EditorToolbar.tsx +0 -180
- package/src/presentation/components/FilterSlider.tsx +0 -123
- package/src/presentation/hooks/useEditorTools.ts +0 -188
- package/src/presentation/hooks/useImageAnnotation.ts +0 -32
- package/src/presentation/hooks/useImageEditor.ts +0 -182
- package/src/presentation/hooks/useImageFilter.ts +0 -38
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/react-native-image",
|
|
3
|
-
"version": "1.2
|
|
3
|
+
"version": "1.3.2",
|
|
4
4
|
"description": "Image manipulation and viewing for React Native apps - resize, crop, rotate, flip, compress, gallery viewer",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -29,12 +29,13 @@
|
|
|
29
29
|
"type": "git",
|
|
30
30
|
"url": "https://github.com/umituz/react-native-image"
|
|
31
31
|
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"expo-image-manipulator": ">=12.0.0"
|
|
34
|
+
},
|
|
32
35
|
"peerDependencies": {
|
|
33
36
|
"@umituz/react-native-animation": "latest",
|
|
34
|
-
"@umituz/react-native-design-system": "
|
|
35
|
-
"@umituz/react-native-icons": "latest",
|
|
37
|
+
"@umituz/react-native-design-system": "^2.1.0",
|
|
36
38
|
"expo-image": ">=1.0.0",
|
|
37
|
-
"expo-image-manipulator": ">=12.0.0",
|
|
38
39
|
"expo-media-library": ">=15.0.0",
|
|
39
40
|
"expo-sharing": ">=12.0.0",
|
|
40
41
|
"react": ">=18.2.0",
|
|
@@ -44,8 +45,7 @@
|
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@umituz/react-native-animation": "latest",
|
|
47
|
-
"@umituz/react-native-design-system": "
|
|
48
|
-
"@umituz/react-native-icons": "latest",
|
|
48
|
+
"@umituz/react-native-design-system": "^2.1.0",
|
|
49
49
|
"expo-image": "~2.0.0",
|
|
50
50
|
"expo-image-manipulator": "~13.0.0",
|
|
51
51
|
"expo-media-library": "~17.0.0",
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @umituz/react-native-image - Public API
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* React Native image manipulation and viewing
|
|
5
5
|
* Resize, crop, rotate, flip, compress, gallery viewer
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
8
|
-
* import { useImage, ImageGallery,
|
|
8
|
+
* import { useImage, ImageGallery, ImageUtils } from '@umituz/react-native-image';
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
// =============================================================================
|
|
@@ -35,11 +35,10 @@ export { IMAGE_CONSTANTS } from './domain/entities/ImageConstants';
|
|
|
35
35
|
export { ImageUtils } from './domain/utils/ImageUtils';
|
|
36
36
|
|
|
37
37
|
// =============================================================================
|
|
38
|
-
// DOMAIN LAYER - Filter Types
|
|
38
|
+
// DOMAIN LAYER - Filter Types (React Native Compatible Only)
|
|
39
39
|
// =============================================================================
|
|
40
40
|
|
|
41
41
|
export type {
|
|
42
|
-
ImageFilter,
|
|
43
42
|
ImageFilterOptions,
|
|
44
43
|
ImageColorAdjustment,
|
|
45
44
|
ImageQualityMetrics,
|
|
@@ -52,7 +51,7 @@ export {
|
|
|
52
51
|
} from './domain/entities/ImageFilterTypes';
|
|
53
52
|
|
|
54
53
|
// =============================================================================
|
|
55
|
-
// INFRASTRUCTURE LAYER - Services
|
|
54
|
+
// INFRASTRUCTURE LAYER - React Native Compatible Services
|
|
56
55
|
// =============================================================================
|
|
57
56
|
|
|
58
57
|
export { ImageTransformService } from './infrastructure/services/ImageTransformService';
|
|
@@ -64,57 +63,29 @@ export {
|
|
|
64
63
|
type ImageViewerConfig,
|
|
65
64
|
} from './infrastructure/services/ImageViewerService';
|
|
66
65
|
|
|
67
|
-
// =============================================================================
|
|
68
|
-
// INFRASTRUCTURE LAYER - Advanced Services
|
|
69
|
-
// =============================================================================
|
|
70
|
-
|
|
71
|
-
export { ImageFilterService } from './infrastructure/services/ImageFilterService';
|
|
72
66
|
export { ImageBatchService, type BatchOperation, type BatchProcessingOptions, type BatchProcessingResult } from './infrastructure/services/ImageBatchService';
|
|
73
67
|
export { ImageAIEnhancementService, type AutoEnhancementOptions, type EnhancementResult } from './infrastructure/services/ImageAIEnhancementService';
|
|
74
|
-
export { ImageAnnotationService, type ImageAnnotation, type TextOverlay, type DrawingElement, type WatermarkOptions } from './infrastructure/services/ImageAnnotationService';
|
|
75
68
|
export { ImageMetadataService, type ImageMetadataExtractionOptions } from './infrastructure/services/ImageMetadataService';
|
|
76
69
|
export { ImageQualityPresetService, type QualityPreset, type QualityPresets, IMAGE_QUALITY_PRESETS } from './infrastructure/utils/ImageQualityPresets';
|
|
77
70
|
export { ImageSpecializedEnhancementService } from './infrastructure/services/ImageSpecializedEnhancementService';
|
|
78
71
|
|
|
79
72
|
// =============================================================================
|
|
80
|
-
// PRESENTATION LAYER - Components & Hooks
|
|
73
|
+
// PRESENTATION LAYER - React Native Components & Hooks
|
|
81
74
|
// =============================================================================
|
|
82
75
|
|
|
83
76
|
export { ImageGallery, type ImageGalleryProps } from './presentation/components/ImageGallery';
|
|
84
77
|
|
|
85
|
-
// =============================================================================
|
|
86
|
-
// PRESENTATION LAYER - Core Hooks
|
|
87
|
-
// =============================================================================
|
|
88
|
-
|
|
89
78
|
export { useImage } from './presentation/hooks/useImage';
|
|
90
79
|
export { useImageTransform } from './presentation/hooks/useImageTransform';
|
|
91
80
|
export { useImageConversion } from './presentation/hooks/useImageConversion';
|
|
92
|
-
// =============================================================================
|
|
93
|
-
// PRESENTATION LAYER - Editor Components & Hooks
|
|
94
|
-
// =============================================================================
|
|
95
|
-
|
|
96
|
-
export { useImageEditor } from './presentation/hooks/useImageEditor';
|
|
97
|
-
export { useEditorTools } from './presentation/hooks/useEditorTools';
|
|
98
|
-
export { Editor } from './presentation/components/Editor';
|
|
99
|
-
export { EditorCanvas } from './presentation/components/EditorCanvas';
|
|
100
|
-
export { EditorToolbar } from './presentation/components/EditorToolbar';
|
|
101
|
-
export { EditorPanel } from './presentation/components/EditorPanel';
|
|
102
|
-
export { CropComponent } from './presentation/components/CropComponent';
|
|
103
|
-
export { FilterSlider } from './presentation/components/FilterSlider';
|
|
104
81
|
|
|
105
82
|
export {
|
|
106
83
|
useImageGallery,
|
|
107
84
|
type UseImageGalleryReturn,
|
|
108
85
|
} from './presentation/hooks/useImageGallery';
|
|
109
86
|
|
|
110
|
-
// =============================================================================
|
|
111
|
-
// PRESENTATION LAYER - Advanced Hooks
|
|
112
|
-
// =============================================================================
|
|
113
|
-
|
|
114
|
-
export { useImageFilter } from './presentation/hooks/useImageFilter';
|
|
115
87
|
export { useImageBatch } from './presentation/hooks/useImageBatch';
|
|
116
88
|
export { useImageAIEnhancement } from './presentation/hooks/useImageAIEnhancement';
|
|
117
|
-
export { useImageAnnotation } from './presentation/hooks/useImageAnnotation';
|
|
118
89
|
export { useImageMetadata } from './presentation/hooks/useImageMetadata';
|
|
119
90
|
|
|
120
91
|
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image Infrastructure - Annotation Service
|
|
3
|
-
*
|
|
4
|
-
* Handles text overlay, drawing, and annotation features
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
|
|
8
|
-
import { ImageValidator } from '../utils/ImageValidator';
|
|
9
|
-
import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
|
|
10
|
-
import { CanvasRenderingService } from '../utils/CanvasRenderingService';
|
|
11
|
-
|
|
12
|
-
export interface TextOverlay {
|
|
13
|
-
text: string;
|
|
14
|
-
x: number;
|
|
15
|
-
y: number;
|
|
16
|
-
fontSize?: number;
|
|
17
|
-
fontFamily?: string;
|
|
18
|
-
color?: string;
|
|
19
|
-
backgroundColor?: string;
|
|
20
|
-
maxWidth?: number;
|
|
21
|
-
rotation?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface DrawingElement {
|
|
25
|
-
type: 'line' | 'rectangle' | 'circle' | 'arrow' | 'freehand';
|
|
26
|
-
points: Array<{ x: number; y: number }>;
|
|
27
|
-
color?: string;
|
|
28
|
-
strokeWidth?: number;
|
|
29
|
-
fillColor?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface WatermarkOptions {
|
|
33
|
-
text?: string;
|
|
34
|
-
imageUri?: string;
|
|
35
|
-
position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center';
|
|
36
|
-
opacity?: number;
|
|
37
|
-
size?: number;
|
|
38
|
-
margin?: number;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface ImageAnnotation {
|
|
42
|
-
textOverlays?: TextOverlay[];
|
|
43
|
-
drawings?: DrawingElement[];
|
|
44
|
-
watermark?: WatermarkOptions;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class ImageAnnotationService {
|
|
48
|
-
private static getPositionCoordinates(
|
|
49
|
-
position: string,
|
|
50
|
-
imageWidth: number,
|
|
51
|
-
imageHeight: number,
|
|
52
|
-
elementWidth: number,
|
|
53
|
-
elementHeight: number,
|
|
54
|
-
margin: number = 10
|
|
55
|
-
): { x: number; y: number } {
|
|
56
|
-
switch (position) {
|
|
57
|
-
case 'top-left':
|
|
58
|
-
return { x: margin, y: margin };
|
|
59
|
-
case 'top-right':
|
|
60
|
-
return { x: imageWidth - elementWidth - margin, y: margin };
|
|
61
|
-
case 'bottom-left':
|
|
62
|
-
return { x: margin, y: imageHeight - elementHeight - margin };
|
|
63
|
-
case 'bottom-right':
|
|
64
|
-
return { x: imageWidth - elementWidth - margin, y: imageHeight - elementHeight - margin };
|
|
65
|
-
case 'center':
|
|
66
|
-
return {
|
|
67
|
-
x: (imageWidth - elementWidth) / 2,
|
|
68
|
-
y: (imageHeight - elementHeight) / 2
|
|
69
|
-
};
|
|
70
|
-
default:
|
|
71
|
-
return { x: margin, y: margin };
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
static async addTextOverlay(
|
|
76
|
-
uri: string,
|
|
77
|
-
overlay: TextOverlay
|
|
78
|
-
): Promise<ImageManipulationResult> {
|
|
79
|
-
try {
|
|
80
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
81
|
-
if (!uriValidation.isValid) {
|
|
82
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addTextOverlay');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// In a real implementation, we would:
|
|
86
|
-
// 1. Load image into canvas
|
|
87
|
-
// 2. Apply text overlay using canvas rendering
|
|
88
|
-
// 3. Export canvas to new URI
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
uri, // Would be processed URI
|
|
92
|
-
width: 0,
|
|
93
|
-
height: 0,
|
|
94
|
-
};
|
|
95
|
-
} catch (error) {
|
|
96
|
-
throw ImageErrorHandler.handleUnknownError(error, 'addTextOverlay');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
static async addDrawingElements(
|
|
101
|
-
uri: string,
|
|
102
|
-
elements: DrawingElement[]
|
|
103
|
-
): Promise<ImageManipulationResult> {
|
|
104
|
-
try {
|
|
105
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
106
|
-
if (!uriValidation.isValid) {
|
|
107
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addDrawingElements');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Mock implementation
|
|
111
|
-
return {
|
|
112
|
-
uri,
|
|
113
|
-
width: 0,
|
|
114
|
-
height: 0,
|
|
115
|
-
};
|
|
116
|
-
} catch (error) {
|
|
117
|
-
throw ImageErrorHandler.handleUnknownError(error, 'addDrawingElements');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static async addWatermark(
|
|
122
|
-
uri: string,
|
|
123
|
-
options: WatermarkOptions
|
|
124
|
-
): Promise<ImageManipulationResult> {
|
|
125
|
-
try {
|
|
126
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
127
|
-
if (!uriValidation.isValid) {
|
|
128
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'addWatermark');
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (!options.text && !options.imageUri) {
|
|
132
|
-
throw ImageErrorHandler.createError(
|
|
133
|
-
'Either text or imageUri must be provided for watermark',
|
|
134
|
-
IMAGE_ERROR_CODES.VALIDATION_ERROR,
|
|
135
|
-
'addWatermark'
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Mock implementation
|
|
140
|
-
return {
|
|
141
|
-
uri,
|
|
142
|
-
width: 0,
|
|
143
|
-
height: 0,
|
|
144
|
-
};
|
|
145
|
-
} catch (error) {
|
|
146
|
-
throw ImageErrorHandler.handleUnknownError(error, 'addWatermark');
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
static async applyAnnotation(
|
|
151
|
-
uri: string,
|
|
152
|
-
annotation: ImageAnnotation
|
|
153
|
-
): Promise<ImageManipulationResult> {
|
|
154
|
-
try {
|
|
155
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
156
|
-
if (!uriValidation.isValid) {
|
|
157
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'applyAnnotation');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Apply all annotations in order
|
|
161
|
-
let resultUri = uri;
|
|
162
|
-
|
|
163
|
-
if (annotation.textOverlays) {
|
|
164
|
-
for (const overlay of annotation.textOverlays) {
|
|
165
|
-
const result = await ImageAnnotationService.addTextOverlay(resultUri, overlay);
|
|
166
|
-
resultUri = result.uri;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (annotation.drawings) {
|
|
171
|
-
const result = await ImageAnnotationService.addDrawingElements(resultUri, annotation.drawings);
|
|
172
|
-
resultUri = result.uri;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (annotation.watermark) {
|
|
176
|
-
const result = await ImageAnnotationService.addWatermark(resultUri, annotation.watermark);
|
|
177
|
-
resultUri = result.uri;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
uri: resultUri,
|
|
182
|
-
width: 0,
|
|
183
|
-
height: 0,
|
|
184
|
-
};
|
|
185
|
-
} catch (error) {
|
|
186
|
-
throw ImageErrorHandler.handleUnknownError(error, 'applyAnnotation');
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image Infrastructure - Filter Service
|
|
3
|
-
*
|
|
4
|
-
* Advanced image filtering and effects using canvas and image processing
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type {
|
|
8
|
-
ImageFilter,
|
|
9
|
-
ImageFilterType,
|
|
10
|
-
ImageColorAdjustment,
|
|
11
|
-
ImageQualityMetrics,
|
|
12
|
-
ImageColorPalette,
|
|
13
|
-
} from '../../domain/entities/ImageFilterTypes';
|
|
14
|
-
import type { ImageManipulationResult } from '../../domain/entities/ImageTypes';
|
|
15
|
-
import { ImageValidator } from '../utils/ImageValidator';
|
|
16
|
-
import { ImageErrorHandler, IMAGE_ERROR_CODES } from '../utils/ImageErrorHandler';
|
|
17
|
-
import { FilterEffects } from '../utils/FilterEffects';
|
|
18
|
-
|
|
19
|
-
export class ImageFilterService {
|
|
20
|
-
private static createCanvasImageData(
|
|
21
|
-
width: number,
|
|
22
|
-
height: number,
|
|
23
|
-
data: Uint8ClampedArray
|
|
24
|
-
): ImageData {
|
|
25
|
-
return { data, width, height } as ImageData;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
private static applyBrightness(
|
|
29
|
-
imageData: ImageData,
|
|
30
|
-
intensity: number
|
|
31
|
-
): ImageData {
|
|
32
|
-
const data = new Uint8ClampedArray(imageData.data);
|
|
33
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
34
|
-
data[i] = Math.min(255, Math.max(0, data[i] + intensity * 255));
|
|
35
|
-
data[i + 1] = Math.min(255, Math.max(0, data[i + 1] + intensity * 255));
|
|
36
|
-
data[i + 2] = Math.min(255, Math.max(0, data[i + 2] + intensity * 255));
|
|
37
|
-
}
|
|
38
|
-
return ImageFilterService.createCanvasImageData(imageData.width, imageData.height, data);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
private static applyContrast(
|
|
42
|
-
imageData: ImageData,
|
|
43
|
-
intensity: number
|
|
44
|
-
): ImageData {
|
|
45
|
-
const data = new Uint8ClampedArray(imageData.data);
|
|
46
|
-
const factor = (259 * (intensity * 255 + 255)) / (255 * (259 - intensity * 255));
|
|
47
|
-
|
|
48
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
49
|
-
data[i] = Math.min(255, Math.max(0, factor * (data[i] - 128) + 128));
|
|
50
|
-
data[i + 1] = Math.min(255, Math.max(0, factor * (data[i + 1] - 128) + 128));
|
|
51
|
-
data[i + 2] = Math.min(255, Math.max(0, factor * (data[i + 2] - 128) + 128));
|
|
52
|
-
}
|
|
53
|
-
return ImageFilterService.createCanvasImageData(imageData.width, imageData.height, data);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private static applyGrayscale(imageData: ImageData): ImageData {
|
|
57
|
-
const data = new Uint8ClampedArray(imageData.data);
|
|
58
|
-
for (let i = 0; i < data.length; i += 4) {
|
|
59
|
-
const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
|
|
60
|
-
data[i] = gray;
|
|
61
|
-
data[i + 1] = gray;
|
|
62
|
-
data[i + 2] = gray;
|
|
63
|
-
}
|
|
64
|
-
return ImageFilterService.createCanvasImageData(imageData.width, imageData.height, data);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private static applySepia(imageData: ImageData, intensity: number = 1): ImageData {
|
|
68
|
-
return FilterEffects.applySepia(imageData, intensity);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
static async applyFilter(
|
|
74
|
-
uri: string,
|
|
75
|
-
filter: ImageFilter
|
|
76
|
-
): Promise<ImageManipulationResult> {
|
|
77
|
-
try {
|
|
78
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
79
|
-
if (!uriValidation.isValid) {
|
|
80
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'applyFilter');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// In a real implementation, we would:
|
|
84
|
-
// 1. Load the image into a canvas
|
|
85
|
-
// 2. Apply the filter to the pixel data
|
|
86
|
-
// 3. Export the canvas to a new URI
|
|
87
|
-
|
|
88
|
-
// For now, return a mock implementation
|
|
89
|
-
return {
|
|
90
|
-
uri, // Would be the processed URI
|
|
91
|
-
width: 0,
|
|
92
|
-
height: 0,
|
|
93
|
-
base64: undefined,
|
|
94
|
-
};
|
|
95
|
-
} catch (error) {
|
|
96
|
-
throw ImageErrorHandler.handleUnknownError(error, 'applyFilter');
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
static async applyColorAdjustment(
|
|
101
|
-
uri: string,
|
|
102
|
-
adjustment: ImageColorAdjustment
|
|
103
|
-
): Promise<ImageManipulationResult> {
|
|
104
|
-
try {
|
|
105
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
106
|
-
if (!uriValidation.isValid) {
|
|
107
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'applyColorAdjustment');
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Apply brightness, contrast, saturation adjustments
|
|
111
|
-
return {
|
|
112
|
-
uri,
|
|
113
|
-
width: 0,
|
|
114
|
-
height: 0,
|
|
115
|
-
};
|
|
116
|
-
} catch (error) {
|
|
117
|
-
throw ImageErrorHandler.handleUnknownError(error, 'applyColorAdjustment');
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
static async analyzeQuality(uri: string): Promise<ImageQualityMetrics> {
|
|
122
|
-
try {
|
|
123
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
124
|
-
if (!uriValidation.isValid) {
|
|
125
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'analyzeQuality');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Mock implementation - would analyze actual image data
|
|
129
|
-
return {
|
|
130
|
-
sharpness: Math.random() * 100,
|
|
131
|
-
brightness: Math.random() * 100,
|
|
132
|
-
contrast: Math.random() * 100,
|
|
133
|
-
colorfulness: Math.random() * 100,
|
|
134
|
-
overallQuality: Math.random() * 100,
|
|
135
|
-
};
|
|
136
|
-
} catch (error) {
|
|
137
|
-
throw ImageErrorHandler.handleUnknownError(error, 'analyzeQuality');
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
static async extractColorPalette(
|
|
142
|
-
uri: string,
|
|
143
|
-
colorCount: number = 5
|
|
144
|
-
): Promise<ImageColorPalette> {
|
|
145
|
-
try {
|
|
146
|
-
const uriValidation = ImageValidator.validateUri(uri);
|
|
147
|
-
if (!uriValidation.isValid) {
|
|
148
|
-
throw ImageErrorHandler.createError(uriValidation.error!, IMAGE_ERROR_CODES.INVALID_URI, 'extractColorPalette');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Mock implementation - would extract actual colors
|
|
152
|
-
const colors = Array.from({ length: colorCount }, () =>
|
|
153
|
-
`#${Math.floor(Math.random()*16777215).toString(16).padStart(6, '0')}`
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
dominant: colors.slice(0, 3),
|
|
158
|
-
palette: colors.map((color, index) => ({
|
|
159
|
-
color,
|
|
160
|
-
percentage: Math.random() * 30 + 10,
|
|
161
|
-
population: Math.floor(Math.random() * 1000) + 100,
|
|
162
|
-
})),
|
|
163
|
-
};
|
|
164
|
-
} catch (error) {
|
|
165
|
-
throw ImageErrorHandler.handleUnknownError(error, 'extractColorPalette');
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image Infrastructure - Canvas Rendering Service
|
|
3
|
-
*
|
|
4
|
-
* Canvas-based rendering utilities for annotations and filters
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
export class CanvasRenderingService {
|
|
8
|
-
static renderTextOnCanvas(
|
|
9
|
-
ctx: CanvasRenderingContext2D,
|
|
10
|
-
overlay: import('../services/ImageAnnotationService').TextOverlay
|
|
11
|
-
): void {
|
|
12
|
-
ctx.save();
|
|
13
|
-
|
|
14
|
-
ctx.font = `${overlay.fontSize || 16}px ${overlay.fontFamily || 'Arial'}`;
|
|
15
|
-
ctx.fillStyle = overlay.color || '#000000';
|
|
16
|
-
|
|
17
|
-
if (overlay.backgroundColor) {
|
|
18
|
-
const metrics = ctx.measureText(overlay.text);
|
|
19
|
-
const padding = 4;
|
|
20
|
-
ctx.fillStyle = overlay.backgroundColor;
|
|
21
|
-
ctx.fillRect(
|
|
22
|
-
overlay.x - padding,
|
|
23
|
-
overlay.y - (overlay.fontSize || 16) - padding,
|
|
24
|
-
metrics.width + padding * 2,
|
|
25
|
-
(overlay.fontSize || 16) + padding * 2
|
|
26
|
-
);
|
|
27
|
-
ctx.fillStyle = overlay.color || '#000000';
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (overlay.rotation) {
|
|
31
|
-
ctx.translate(overlay.x, overlay.y);
|
|
32
|
-
ctx.rotate((overlay.rotation * Math.PI) / 180);
|
|
33
|
-
ctx.fillText(overlay.text, 0, 0);
|
|
34
|
-
} else {
|
|
35
|
-
ctx.fillText(overlay.text, overlay.x, overlay.y);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
ctx.restore();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
static renderDrawingOnCanvas(
|
|
42
|
-
ctx: CanvasRenderingContext2D,
|
|
43
|
-
drawing: import('../services/ImageAnnotationService').DrawingElement
|
|
44
|
-
): void {
|
|
45
|
-
ctx.save();
|
|
46
|
-
ctx.strokeStyle = drawing.color || '#000000';
|
|
47
|
-
ctx.lineWidth = drawing.strokeWidth || 2;
|
|
48
|
-
|
|
49
|
-
if (drawing.fillColor) {
|
|
50
|
-
ctx.fillStyle = drawing.fillColor;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
switch (drawing.type) {
|
|
54
|
-
case 'line':
|
|
55
|
-
if (drawing.points.length >= 2) {
|
|
56
|
-
ctx.beginPath();
|
|
57
|
-
ctx.moveTo(drawing.points[0].x, drawing.points[0].y);
|
|
58
|
-
for (let i = 1; i < drawing.points.length; i++) {
|
|
59
|
-
ctx.lineTo(drawing.points[i].x, drawing.points[i].y);
|
|
60
|
-
}
|
|
61
|
-
ctx.stroke();
|
|
62
|
-
}
|
|
63
|
-
break;
|
|
64
|
-
|
|
65
|
-
case 'rectangle':
|
|
66
|
-
if (drawing.points.length >= 2) {
|
|
67
|
-
const width = drawing.points[1].x - drawing.points[0].x;
|
|
68
|
-
const height = drawing.points[1].y - drawing.points[0].y;
|
|
69
|
-
if (drawing.fillColor) {
|
|
70
|
-
ctx.fillRect(drawing.points[0].x, drawing.points[0].y, width, height);
|
|
71
|
-
}
|
|
72
|
-
ctx.strokeRect(drawing.points[0].x, drawing.points[0].y, width, height);
|
|
73
|
-
}
|
|
74
|
-
break;
|
|
75
|
-
|
|
76
|
-
case 'circle':
|
|
77
|
-
if (drawing.points.length >= 2) {
|
|
78
|
-
const radius = Math.sqrt(
|
|
79
|
-
Math.pow(drawing.points[1].x - drawing.points[0].x, 2) +
|
|
80
|
-
Math.pow(drawing.points[1].y - drawing.points[0].y, 2)
|
|
81
|
-
);
|
|
82
|
-
ctx.beginPath();
|
|
83
|
-
ctx.arc(drawing.points[0].x, drawing.points[0].y, radius, 0, 2 * Math.PI);
|
|
84
|
-
if (drawing.fillColor) {
|
|
85
|
-
ctx.fill();
|
|
86
|
-
}
|
|
87
|
-
ctx.stroke();
|
|
88
|
-
}
|
|
89
|
-
break;
|
|
90
|
-
|
|
91
|
-
case 'arrow':
|
|
92
|
-
if (drawing.points.length >= 2) {
|
|
93
|
-
// Draw line
|
|
94
|
-
ctx.beginPath();
|
|
95
|
-
ctx.moveTo(drawing.points[0].x, drawing.points[0].y);
|
|
96
|
-
ctx.lineTo(drawing.points[1].x, drawing.points[1].y);
|
|
97
|
-
ctx.stroke();
|
|
98
|
-
|
|
99
|
-
// Draw arrowhead
|
|
100
|
-
const angle = Math.atan2(
|
|
101
|
-
drawing.points[1].y - drawing.points[0].y,
|
|
102
|
-
drawing.points[1].x - drawing.points[0].x
|
|
103
|
-
);
|
|
104
|
-
const arrowLength = 10;
|
|
105
|
-
ctx.beginPath();
|
|
106
|
-
ctx.moveTo(drawing.points[1].x, drawing.points[1].y);
|
|
107
|
-
ctx.lineTo(
|
|
108
|
-
drawing.points[1].x - arrowLength * Math.cos(angle - Math.PI / 6),
|
|
109
|
-
drawing.points[1].y - arrowLength * Math.sin(angle - Math.PI / 6)
|
|
110
|
-
);
|
|
111
|
-
ctx.moveTo(drawing.points[1].x, drawing.points[1].y);
|
|
112
|
-
ctx.lineTo(
|
|
113
|
-
drawing.points[1].x - arrowLength * Math.cos(angle + Math.PI / 6),
|
|
114
|
-
drawing.points[1].y - arrowLength * Math.sin(angle + Math.PI / 6)
|
|
115
|
-
);
|
|
116
|
-
ctx.stroke();
|
|
117
|
-
}
|
|
118
|
-
break;
|
|
119
|
-
|
|
120
|
-
case 'freehand':
|
|
121
|
-
if (drawing.points.length >= 2) {
|
|
122
|
-
ctx.beginPath();
|
|
123
|
-
ctx.moveTo(drawing.points[0].x, drawing.points[0].y);
|
|
124
|
-
for (let i = 1; i < drawing.points.length; i++) {
|
|
125
|
-
ctx.lineTo(drawing.points[i].x, drawing.points[i].y);
|
|
126
|
-
}
|
|
127
|
-
ctx.stroke();
|
|
128
|
-
}
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
ctx.restore();
|
|
133
|
-
}
|
|
134
|
-
}
|