react-native-expo-cropper 1.2.43 → 1.2.45
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/.babelrc +6 -6
- package/App.js +27 -27
- package/app.json +2 -2
- package/babel.config.js +5 -5
- package/dist/ImageMaskProcessor.js +25 -25
- package/dist/ImageProcessor.js +7 -7
- package/index.js +6 -6
- package/package.json +59 -59
- package/src/CustomCamera.js +355 -352
- package/src/ImageCropper.js +1445 -1447
- package/src/ImageCropperStyles.js +255 -244
- package/src/ImageMaskProcessor.js +143 -143
- package/src/ImageProcessor.js +20 -20
|
@@ -1,143 +1,143 @@
|
|
|
1
|
-
import React, { forwardRef } from 'react';
|
|
2
|
-
import { View } from 'react-native';
|
|
3
|
-
import Svg, { Path, Defs, ClipPath, Image as SvgImage } from 'react-native-svg';
|
|
4
|
-
import { captureRef } from 'react-native-view-shot';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* ImageMaskProcessor - Module pour appliquer un masque alpha à une image
|
|
8
|
-
*
|
|
9
|
-
* NOTE: Ce module est actuellement désactivé par défaut (enableMask = false)
|
|
10
|
-
* car captureRef peut se bloquer sur certaines configurations.
|
|
11
|
-
*
|
|
12
|
-
* Pour activer le masque, mettre enableMask = true dans ImageCropper.js
|
|
13
|
-
* ATTENTION: Peut causer des blocages sur certaines configurations
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Crée un chemin SVG à partir de points
|
|
18
|
-
*/
|
|
19
|
-
const createPathFromPoints = (points) => {
|
|
20
|
-
if (points.length < 3) return '';
|
|
21
|
-
let path = `M ${points[0].x} ${points[0].y} `;
|
|
22
|
-
for (let i = 1; i < points.length; i++) {
|
|
23
|
-
path += `L ${points[i].x} ${points[i].y} `;
|
|
24
|
-
}
|
|
25
|
-
return path + 'Z';
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Composant MaskView - Vue avec image et masque SVG
|
|
30
|
-
* Utilisé pour capturer l'image avec le masque appliqué
|
|
31
|
-
*/
|
|
32
|
-
export const MaskView = forwardRef(({ imageUri, points, width, height }, ref) => {
|
|
33
|
-
const path = createPathFromPoints(points);
|
|
34
|
-
|
|
35
|
-
// ✅ CORRECTION CRITIQUE :
|
|
36
|
-
// Utiliser un VRAI clip SVG (ClipPath + SvgImage) au lieu d'un overlay noir.
|
|
37
|
-
// Sinon captureRef peut capturer seulement l'overlay (noir/transparence) si l'Image RN n'est pas prête.
|
|
38
|
-
return (
|
|
39
|
-
<View
|
|
40
|
-
ref={ref}
|
|
41
|
-
collapsable={false}
|
|
42
|
-
style={{
|
|
43
|
-
width,
|
|
44
|
-
height,
|
|
45
|
-
overflow: 'hidden',
|
|
46
|
-
backgroundColor: 'transparent',
|
|
47
|
-
}}
|
|
48
|
-
>
|
|
49
|
-
<Svg width={width} height={height}>
|
|
50
|
-
<Defs>
|
|
51
|
-
<ClipPath id="cropClip">
|
|
52
|
-
<Path d={path} />
|
|
53
|
-
</ClipPath>
|
|
54
|
-
</Defs>
|
|
55
|
-
{/* Image rendue dans SVG + clip => extérieur transparent, intérieur = image */}
|
|
56
|
-
<SvgImage
|
|
57
|
-
width={width}
|
|
58
|
-
height={height}
|
|
59
|
-
href={{ uri: imageUri }}
|
|
60
|
-
preserveAspectRatio="xMidYMid slice"
|
|
61
|
-
clipPath="url(#cropClip)"
|
|
62
|
-
/>
|
|
63
|
-
</Svg>
|
|
64
|
-
</View>
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
MaskView.displayName = 'MaskView';
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Applique un masque alpha à une image en utilisant captureRef
|
|
72
|
-
*
|
|
73
|
-
* @param {string} imageUri - URI de l'image à masquer
|
|
74
|
-
* @param {Array} points - Points définissant le masque (coordonnées normalisées)
|
|
75
|
-
* @param {number} width - Largeur de l'image
|
|
76
|
-
* @param {number} height - Hauteur de l'image
|
|
77
|
-
* @param {React.Ref} maskViewRef - Référence à la vue MaskView
|
|
78
|
-
* @returns {Promise<string>} URI de l'image masquée
|
|
79
|
-
*
|
|
80
|
-
* NOTE: Cette fonction utilise captureRef qui peut se bloquer.
|
|
81
|
-
* Utiliser uniquement si enableMask = true dans ImageCropper.js
|
|
82
|
-
*/
|
|
83
|
-
// width/height = dimensions de sortie souhaitées EN PIXELS (pas dp)
|
|
84
|
-
export const applyMaskToImage = async (imageUri, points, width, height, maskViewRef) => {
|
|
85
|
-
try {
|
|
86
|
-
if (!maskViewRef?.current) {
|
|
87
|
-
console.warn("MaskView ref not available in applyMaskToImage");
|
|
88
|
-
return imageUri;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
console.log("Applying mask with captureRef...", {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
imageUri,
|
|
95
|
-
pointsCount: points.length,
|
|
96
|
-
width,
|
|
97
|
-
height,
|
|
98
|
-
refAvailable: !!maskViewRef.current
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// ✅ CORRECTION : Capturer la vue avec le masque appliqué
|
|
102
|
-
// captureRef supporte la transparence PNG pour préserver la forme exacte
|
|
103
|
-
// Utiliser une qualité maximale et un format PNG pour préserver la transparence
|
|
104
|
-
// Ajouter un petit délai supplémentaire pour s'assurer que la vue est complètement rendue
|
|
105
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
106
|
-
|
|
107
|
-
// ⚠️ LIMITATION CRITIQUE : captureRef capture à la résolution de la vue React Native
|
|
108
|
-
// La vue MaskView doit avoir les dimensions EXACTES de l'image croppée pour préserver la qualité
|
|
109
|
-
// Si width/height sont les dimensions réelles de l'image croppée, la qualité sera préservée
|
|
110
|
-
// Si width/height sont plus petits, la qualité sera réduite
|
|
111
|
-
console.log("📸 Capturing mask view at requested output size (px):", {
|
|
112
|
-
outputPx: { width, height },
|
|
113
|
-
imageUri,
|
|
114
|
-
note: "✅ captureRef width/height forces output resolution; view can stay smaller in dp"
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// ✅ CORRECTION : Utiliser les dimensions réelles de l'image croppée
|
|
118
|
-
// Si width/height correspondent aux dimensions réelles de croppedResult, la qualité sera maximale
|
|
119
|
-
const maskedUri = await captureRef(maskViewRef.current, {
|
|
120
|
-
format: 'png', // PNG supporte la transparence alpha
|
|
121
|
-
quality: 1, // Qualité maximale (0-1)
|
|
122
|
-
result: 'tmpfile', // Fichier temporaire
|
|
123
|
-
// ✅ CORRECTION : forcer la résolution de sortie (pixels)
|
|
124
|
-
// évite une capture 'écran' basée uniquement sur les dp de la vue (risque de downscale).
|
|
125
|
-
width,
|
|
126
|
-
height,
|
|
127
|
-
// ✅ IMPORTANT : La résolution de capture = taille de la vue (width x height)
|
|
128
|
-
// Si width/height = dimensions réelles de l'image croppée, qualité préservée
|
|
129
|
-
// Si width/height < dimensions réelles, qualité réduite
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
console.log("✅ Mask applied successfully:", {
|
|
133
|
-
maskedUri,
|
|
134
|
-
viewSize: `${width}x${height}`,
|
|
135
|
-
qualityStatus: width >= 1000 && height >= 1000 ? "✓ High quality preserved" : "⚠️ Check if dimensions match original"
|
|
136
|
-
});
|
|
137
|
-
return maskedUri;
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error("Error applying mask:", error);
|
|
140
|
-
// En cas d'erreur, retourner l'image originale (bounding box)
|
|
141
|
-
return imageUri;
|
|
142
|
-
}
|
|
143
|
-
};
|
|
1
|
+
import React, { forwardRef } from 'react';
|
|
2
|
+
import { View } from 'react-native';
|
|
3
|
+
import Svg, { Path, Defs, ClipPath, Image as SvgImage } from 'react-native-svg';
|
|
4
|
+
import { captureRef } from 'react-native-view-shot';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ImageMaskProcessor - Module pour appliquer un masque alpha à une image
|
|
8
|
+
*
|
|
9
|
+
* NOTE: Ce module est actuellement désactivé par défaut (enableMask = false)
|
|
10
|
+
* car captureRef peut se bloquer sur certaines configurations.
|
|
11
|
+
*
|
|
12
|
+
* Pour activer le masque, mettre enableMask = true dans ImageCropper.js
|
|
13
|
+
* ATTENTION: Peut causer des blocages sur certaines configurations
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Crée un chemin SVG à partir de points
|
|
18
|
+
*/
|
|
19
|
+
const createPathFromPoints = (points) => {
|
|
20
|
+
if (points.length < 3) return '';
|
|
21
|
+
let path = `M ${points[0].x} ${points[0].y} `;
|
|
22
|
+
for (let i = 1; i < points.length; i++) {
|
|
23
|
+
path += `L ${points[i].x} ${points[i].y} `;
|
|
24
|
+
}
|
|
25
|
+
return path + 'Z';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Composant MaskView - Vue avec image et masque SVG
|
|
30
|
+
* Utilisé pour capturer l'image avec le masque appliqué
|
|
31
|
+
*/
|
|
32
|
+
export const MaskView = forwardRef(({ imageUri, points, width, height }, ref) => {
|
|
33
|
+
const path = createPathFromPoints(points);
|
|
34
|
+
|
|
35
|
+
// ✅ CORRECTION CRITIQUE :
|
|
36
|
+
// Utiliser un VRAI clip SVG (ClipPath + SvgImage) au lieu d'un overlay noir.
|
|
37
|
+
// Sinon captureRef peut capturer seulement l'overlay (noir/transparence) si l'Image RN n'est pas prête.
|
|
38
|
+
return (
|
|
39
|
+
<View
|
|
40
|
+
ref={ref}
|
|
41
|
+
collapsable={false}
|
|
42
|
+
style={{
|
|
43
|
+
width,
|
|
44
|
+
height,
|
|
45
|
+
overflow: 'hidden',
|
|
46
|
+
backgroundColor: 'transparent',
|
|
47
|
+
}}
|
|
48
|
+
>
|
|
49
|
+
<Svg width={width} height={height}>
|
|
50
|
+
<Defs>
|
|
51
|
+
<ClipPath id="cropClip">
|
|
52
|
+
<Path d={path} />
|
|
53
|
+
</ClipPath>
|
|
54
|
+
</Defs>
|
|
55
|
+
{/* Image rendue dans SVG + clip => extérieur transparent, intérieur = image */}
|
|
56
|
+
<SvgImage
|
|
57
|
+
width={width}
|
|
58
|
+
height={height}
|
|
59
|
+
href={{ uri: imageUri }}
|
|
60
|
+
preserveAspectRatio="xMidYMid slice"
|
|
61
|
+
clipPath="url(#cropClip)"
|
|
62
|
+
/>
|
|
63
|
+
</Svg>
|
|
64
|
+
</View>
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
MaskView.displayName = 'MaskView';
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Applique un masque alpha à une image en utilisant captureRef
|
|
72
|
+
*
|
|
73
|
+
* @param {string} imageUri - URI de l'image à masquer
|
|
74
|
+
* @param {Array} points - Points définissant le masque (coordonnées normalisées)
|
|
75
|
+
* @param {number} width - Largeur de l'image
|
|
76
|
+
* @param {number} height - Hauteur de l'image
|
|
77
|
+
* @param {React.Ref} maskViewRef - Référence à la vue MaskView
|
|
78
|
+
* @returns {Promise<string>} URI de l'image masquée
|
|
79
|
+
*
|
|
80
|
+
* NOTE: Cette fonction utilise captureRef qui peut se bloquer.
|
|
81
|
+
* Utiliser uniquement si enableMask = true dans ImageCropper.js
|
|
82
|
+
*/
|
|
83
|
+
// width/height = dimensions de sortie souhaitées EN PIXELS (pas dp)
|
|
84
|
+
export const applyMaskToImage = async (imageUri, points, width, height, maskViewRef) => {
|
|
85
|
+
try {
|
|
86
|
+
if (!maskViewRef?.current) {
|
|
87
|
+
console.warn("MaskView ref not available in applyMaskToImage");
|
|
88
|
+
return imageUri;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.log("Applying mask with captureRef...", {
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
imageUri,
|
|
95
|
+
pointsCount: points.length,
|
|
96
|
+
width,
|
|
97
|
+
height,
|
|
98
|
+
refAvailable: !!maskViewRef.current
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ✅ CORRECTION : Capturer la vue avec le masque appliqué
|
|
102
|
+
// captureRef supporte la transparence PNG pour préserver la forme exacte
|
|
103
|
+
// Utiliser une qualité maximale et un format PNG pour préserver la transparence
|
|
104
|
+
// Ajouter un petit délai supplémentaire pour s'assurer que la vue est complètement rendue
|
|
105
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
106
|
+
|
|
107
|
+
// ⚠️ LIMITATION CRITIQUE : captureRef capture à la résolution de la vue React Native
|
|
108
|
+
// La vue MaskView doit avoir les dimensions EXACTES de l'image croppée pour préserver la qualité
|
|
109
|
+
// Si width/height sont les dimensions réelles de l'image croppée, la qualité sera préservée
|
|
110
|
+
// Si width/height sont plus petits, la qualité sera réduite
|
|
111
|
+
console.log("📸 Capturing mask view at requested output size (px):", {
|
|
112
|
+
outputPx: { width, height },
|
|
113
|
+
imageUri,
|
|
114
|
+
note: "✅ captureRef width/height forces output resolution; view can stay smaller in dp"
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ✅ CORRECTION : Utiliser les dimensions réelles de l'image croppée
|
|
118
|
+
// Si width/height correspondent aux dimensions réelles de croppedResult, la qualité sera maximale
|
|
119
|
+
const maskedUri = await captureRef(maskViewRef.current, {
|
|
120
|
+
format: 'png', // PNG supporte la transparence alpha
|
|
121
|
+
quality: 1, // Qualité maximale (0-1)
|
|
122
|
+
result: 'tmpfile', // Fichier temporaire
|
|
123
|
+
// ✅ CORRECTION : forcer la résolution de sortie (pixels)
|
|
124
|
+
// évite une capture 'écran' basée uniquement sur les dp de la vue (risque de downscale).
|
|
125
|
+
width,
|
|
126
|
+
height,
|
|
127
|
+
// ✅ IMPORTANT : La résolution de capture = taille de la vue (width x height)
|
|
128
|
+
// Si width/height = dimensions réelles de l'image croppée, qualité préservée
|
|
129
|
+
// Si width/height < dimensions réelles, qualité réduite
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
console.log("✅ Mask applied successfully:", {
|
|
133
|
+
maskedUri,
|
|
134
|
+
viewSize: `${width}x${height}`,
|
|
135
|
+
qualityStatus: width >= 1000 && height >= 1000 ? "✓ High quality preserved" : "⚠️ Check if dimensions match original"
|
|
136
|
+
});
|
|
137
|
+
return maskedUri;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("Error applying mask:", error);
|
|
140
|
+
// En cas d'erreur, retourner l'image originale (bounding box)
|
|
141
|
+
return imageUri;
|
|
142
|
+
}
|
|
143
|
+
};
|
package/src/ImageProcessor.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import * as ImageManipulator from 'expo-image-manipulator';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Retourne l'image originale sans redimensionnement pour préserver la qualité maximale.
|
|
5
|
-
* Le redimensionnement destructif a été supprimé pour éviter la perte de résolution.
|
|
6
|
-
*
|
|
7
|
-
* @param {string} uri - URI de l'image originale
|
|
8
|
-
* @param {number} addheight - Paramètre conservé pour compatibilité (non utilisé)
|
|
9
|
-
* @returns {Promise<string>} URI de l'image originale (sans modification)
|
|
10
|
-
*/
|
|
11
|
-
export const enhanceImage = async (uri, addheight) => {
|
|
12
|
-
try {
|
|
13
|
-
// Retourner l'URI originale sans redimensionnement
|
|
14
|
-
// Cela préserve la résolution maximale et évite toute perte de qualité
|
|
15
|
-
// par interpolation lors du redimensionnement.
|
|
16
|
-
return uri;
|
|
17
|
-
} catch (error) {
|
|
18
|
-
console.error("Erreur enhanceImage:", error);
|
|
19
|
-
return uri;
|
|
20
|
-
}
|
|
1
|
+
import * as ImageManipulator from 'expo-image-manipulator';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Retourne l'image originale sans redimensionnement pour préserver la qualité maximale.
|
|
5
|
+
* Le redimensionnement destructif a été supprimé pour éviter la perte de résolution.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} uri - URI de l'image originale
|
|
8
|
+
* @param {number} addheight - Paramètre conservé pour compatibilité (non utilisé)
|
|
9
|
+
* @returns {Promise<string>} URI de l'image originale (sans modification)
|
|
10
|
+
*/
|
|
11
|
+
export const enhanceImage = async (uri, addheight) => {
|
|
12
|
+
try {
|
|
13
|
+
// Retourner l'URI originale sans redimensionnement
|
|
14
|
+
// Cela préserve la résolution maximale et évite toute perte de qualité
|
|
15
|
+
// par interpolation lors du redimensionnement.
|
|
16
|
+
return uri;
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error("Erreur enhanceImage:", error);
|
|
19
|
+
return uri;
|
|
20
|
+
}
|
|
21
21
|
};
|