@@ -9,9 +9,9 @@ import androidx.compose.ui.input.pointer.PointerInputChange
99import androidx.compose.ui.unit.IntOffset
1010import androidx.compose.ui.unit.IntRect
1111import androidx.compose.ui.unit.IntSize
12- import com.smarttoolfactory.cropper.CropProperties
1312import com.smarttoolfactory.cropper.model.AspectRatio
1413import com.smarttoolfactory.cropper.model.CropData
14+ import com.smarttoolfactory.cropper.settings.CropProperties
1515
1616val CropState .cropData: CropData
1717 get() = CropData (
@@ -96,25 +96,24 @@ abstract class CropState internal constructor(
9696 pannable = cropProperties.pannable
9797 zoomable = cropProperties.zoomable
9898 rotatable = cropProperties.rotatable
99- zoomMax = cropProperties.maxZoom
10099
101- val newZoom = zoom.coerceAtMost(zoomMax)
102- animatableZoom.updateBounds(zoomMin, newZoom)
103-
104- resetWithAnimation(zoom = newZoom)
100+ // TODO Fix zoom reset
101+ // zoomMax = cropProperties.maxZoom
105102
106103 // Update overlay rectangle
107104 val aspectRatio = cropProperties.aspectRatio
108- animateOverlayRectTo( getOverlayFromAspectRatio(
109- containerSize.width.toFloat(),
110- containerSize.height.toFloat(),
111- drawAreaRect.size.width,
112- drawAreaRect.size.height,
113- aspectRatio
114- ))
105+ animateOverlayRectTo(
106+ getOverlayFromAspectRatio(
107+ containerSize.width.toFloat(),
108+ containerSize.height.toFloat(),
109+ drawAreaRect.size.width,
110+ drawAreaRect.size.height,
111+ aspectRatio
112+ )
113+ )
115114
116115 // Update image draw area
117- updateImageDrawAreaRectFromTransformation ()
116+ updateImageDrawRectFromTransformation ()
118117 }
119118
120119 /* *
@@ -166,9 +165,21 @@ abstract class CropState internal constructor(
166165
167166 /* *
168167 * Update rectangle for area that image is drawn. This rect changes when zoom and
169- * pan changes and position of image changes on screen as result of transformation
168+ * pan changes and position of image changes on screen as result of transformation.
169+ *
170+ * This function is called
171+ *
172+ * * when [onGesture] is called to update rect when zoom or pan changes
173+ * and if [fling] is true just after **fling** gesture starts with target
174+ * value in [StaticCropState].
175+ *
176+ * * when [updateProperties] is called in [CropState]
177+ *
178+ * * when [onUp] is called in [DynamicCropState] to match [overlayRect] that could be
179+ * changed and animated if it's out of [containerSize] bounds or its grow
180+ * bigger than previous size
170181 */
171- internal fun updateImageDrawAreaRectFromTransformation () {
182+ internal fun updateImageDrawRectFromTransformation () {
172183 val containerWidth = containerSize.width
173184 val containerHeight = containerSize.height
174185
@@ -200,26 +211,49 @@ abstract class CropState internal constructor(
200211 * Resets to bounds with animation and resets tracking for fling animation.
201212 * Changes pan, zoom and rotation to valid bounds based on [drawAreaRect] and [overlayRect]
202213 */
203- internal suspend fun animateToValidBounds () {
214+ internal suspend fun animateTransformationToOverlayBounds () {
204215
205216 val zoom = zoom.coerceAtLeast(1f )
206217
207218 // Calculate new pan based on overlay
208- val newRect = calculateValidImageDrawRect(overlayRect, drawAreaRect)
219+ val newDrawAreaRect = calculateValidImageDrawRect(overlayRect, drawAreaRect)
209220
210- val leftChange = newRect.left - drawAreaRect.left
211- val topChange = newRect.top - drawAreaRect.top
221+ val newZoom =
222+ calculateNewZoom(oldRect = drawAreaRect, newRect = newDrawAreaRect, zoom = zoom)
212223
213- // Update draw area based on new pan and zoom values
214- drawAreaRect = newRect
224+ val leftChange = newDrawAreaRect.left - drawAreaRect.left
225+ val topChange = newDrawAreaRect.top - drawAreaRect.top
226+
227+ val widthChange = newDrawAreaRect.width - drawAreaRect.width
228+ val heightChange = newDrawAreaRect.height - drawAreaRect.height
215229
216- val newPanX = pan.x + leftChange
217- val newPanY = pan.y + topChange
230+ val panXChange = leftChange + widthChange / 2
231+ val panYChange = topChange + heightChange / 2
218232
219- resetWithAnimation(pan = Offset (newPanX, newPanY), zoom = zoom)
233+ val newPanX = pan.x + panXChange
234+ val newPanY = pan.y + panYChange
235+
236+ // Update draw area based on new pan and zoom values
237+ drawAreaRect = newDrawAreaRect
238+
239+ resetWithAnimation(pan = Offset (newPanX, newPanY), zoom = newZoom)
220240 resetTracking()
221241 }
222242
243+ /* *
244+ * If new overlay is bigger, when crop type is dynamic, we need to increase zoom at least
245+ * size of bigger dimension for image draw area([drawAreaRect]) to cover overlay([overlayRect])
246+ */
247+ private fun calculateNewZoom (oldRect : Rect , newRect : Rect , zoom : Float ): Float {
248+
249+ val widthChange = (newRect.width / oldRect.width)
250+ .coerceAtLeast(1f )
251+ val heightChange = (newRect.height / oldRect.height)
252+ .coerceAtLeast(1f )
253+
254+ return widthChange.coerceAtLeast(heightChange) * zoom
255+ }
256+
223257 /* *
224258 * Calculate valid position for image draw rectangle when pointer is up. Overlay rectangle
225259 * should fit inside draw image rectangle to have valid bounds when calculation is completed.
@@ -229,7 +263,18 @@ abstract class CropState internal constructor(
229263 */
230264 private fun calculateValidImageDrawRect (rectOverlay : Rect , rectImage : Rect ): Rect {
231265
232- var rectImageArea = rectImage.copy()
266+ var width = rectImage.width
267+ var height = rectImage.height
268+
269+ if (width < rectOverlay.width) {
270+ width = rectOverlay.width
271+ }
272+
273+ if (height < rectOverlay.height) {
274+ height = rectOverlay.height
275+ }
276+
277+ var rectImageArea = Rect (offset = rectImage.topLeft, size = Size (width, height))
233278
234279 if (rectImageArea.left > rectOverlay.left) {
235280 rectImageArea = rectImageArea.translate(rectOverlay.left - rectImageArea.left, 0f )
0 commit comments