Skip to content

Commit 5781ee2

Browse files
move utility functions to ImageContentScaleUtil
1 parent 2d7703e commit 5781ee2

File tree

2 files changed

+97
-85
lines changed

2 files changed

+97
-85
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.smarttoolfactory.image
2+
3+
import androidx.compose.foundation.Canvas
4+
import androidx.compose.foundation.layout.BoxWithConstraints
5+
import androidx.compose.foundation.layout.BoxWithConstraintsScope
6+
import androidx.compose.ui.graphics.Canvas
7+
import androidx.compose.ui.graphics.ImageBitmap
8+
import androidx.compose.ui.layout.ContentScale
9+
import androidx.compose.ui.unit.IntOffset
10+
import androidx.compose.ui.unit.IntRect
11+
import androidx.compose.ui.unit.IntSize
12+
13+
/**
14+
* Get Rectangle of [ImageBitmap] with [bitmapWidth] and [bitmapHeight] that is drawn inside
15+
* Canvas with [imageWidth] and [imageHeight]. [boxWidth] and [boxHeight] belong
16+
* to [BoxWithConstraints] that contains Canvas.
17+
* @param boxWidth width of the parent container
18+
* @param boxHeight height of the parent container
19+
* @param imageWidth width of the [Canvas] that draws [ImageBitmap]
20+
* @param imageHeight height of the [Canvas] that draws [ImageBitmap]
21+
* @param bitmapWidth intrinsic width of the [ImageBitmap]
22+
* @param bitmapHeight intrinsic height of the [ImageBitmap]
23+
* @return [IntRect] that covers [ImageBitmap] bounds. When image [ContentScale] is crop
24+
* this rectangle might return smaller rectangle than actual [ImageBitmap] and left or top
25+
* of the rectangle might be bigger than zero.
26+
*/
27+
internal fun getScaledBitmapRect(
28+
boxWidth: Int,
29+
boxHeight: Int,
30+
imageWidth: Float,
31+
imageHeight: Float,
32+
bitmapWidth: Int,
33+
bitmapHeight: Int
34+
): IntRect {
35+
// Get scale of box to width of the image
36+
// We need a rect that contains Bitmap bounds to pass if any child requires it
37+
// For a image with 100x100 px with 300x400 px container and image with crop 400x400px
38+
// So we need to pass top left as 0,50 and size
39+
val scaledBitmapX = boxWidth / imageWidth
40+
val scaledBitmapY = boxHeight / imageHeight
41+
42+
val topLeft = IntOffset(
43+
x = (bitmapWidth * (imageWidth - boxWidth) / imageWidth / 2)
44+
.coerceAtLeast(0f).toInt(),
45+
y = (bitmapHeight * (imageHeight - boxHeight) / imageHeight / 2)
46+
.coerceAtLeast(0f).toInt()
47+
)
48+
49+
val size = IntSize(
50+
width = (bitmapWidth * scaledBitmapX).toInt().coerceAtMost(bitmapWidth),
51+
height = (bitmapHeight * scaledBitmapY).toInt().coerceAtMost(bitmapHeight)
52+
)
53+
54+
return IntRect(offset = topLeft, size = size)
55+
}
56+
57+
/**
58+
* Get [IntSize] of the parent or container that contains [Canvas] that draws [ImageBitmap]
59+
* @param bitmapWidth intrinsic width of the [ImageBitmap]
60+
* @param bitmapHeight intrinsic height of the [ImageBitmap]
61+
* @return size of parent Composable. When Modifier is assigned with fixed or finite size
62+
* they are used, but when any dimension is set to infinity intrinsic dimensions of
63+
* [ImageBitmap] are returned
64+
*/
65+
internal fun BoxWithConstraintsScope.getParentSize(
66+
bitmapWidth: Int,
67+
bitmapHeight: Int
68+
): IntSize {
69+
// Check if Composable has fixed size dimensions
70+
val hasBoundedDimens = constraints.hasBoundedWidth && constraints.hasBoundedHeight
71+
// Check if Composable has infinite dimensions
72+
val hasFixedDimens = constraints.hasFixedWidth && constraints.hasFixedHeight
73+
74+
// Box is the parent(BoxWithConstraints) that contains Canvas under the hood
75+
// Canvas aspect ratio or size might not match parent but it's upper bounds are
76+
// what are passed from parent. Canvas cannot be bigger or taller than BoxWithConstraints
77+
val boxWidth: Int = if (hasBoundedDimens || hasFixedDimens) {
78+
constraints.maxWidth
79+
} else {
80+
constraints.minWidth.coerceAtLeast(bitmapWidth)
81+
}
82+
val boxHeight: Int = if (hasBoundedDimens || hasFixedDimens) {
83+
constraints.maxHeight
84+
} else {
85+
constraints.minHeight.coerceAtLeast(bitmapHeight)
86+
}
87+
return IntSize(boxWidth, boxHeight)
88+
}

image/src/main/java/com/smarttoolfactory/image/ImageWithConstraints.kt

Lines changed: 9 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.smarttoolfactory.image
22

33
import androidx.compose.foundation.Canvas
44
import androidx.compose.foundation.layout.BoxWithConstraints
5-
import androidx.compose.foundation.layout.BoxWithConstraintsScope
65
import androidx.compose.foundation.layout.size
76
import androidx.compose.runtime.Composable
87
import androidx.compose.ui.Alignment
@@ -18,7 +17,10 @@ import androidx.compose.ui.semantics.Role
1817
import androidx.compose.ui.semantics.contentDescription
1918
import androidx.compose.ui.semantics.role
2019
import androidx.compose.ui.semantics.semantics
21-
import androidx.compose.ui.unit.*
20+
import androidx.compose.ui.unit.Constraints
21+
import androidx.compose.ui.unit.Dp
22+
import androidx.compose.ui.unit.IntRect
23+
import androidx.compose.ui.unit.IntSize
2224

2325

2426
/**
@@ -129,83 +131,6 @@ fun ImageWithConstraints(
129131
}
130132
}
131133

132-
/**
133-
* Get Rectangle of [ImageBitmap] with [bitmapWidth] and [bitmapHeight] that is drawn inside
134-
* Canvas with [imageWidth] and [imageHeight]. [boxWidth] and [boxHeight] belong
135-
* to [BoxWithConstraints] that contains Canvas.
136-
* @param boxWidth width of the parent container
137-
* @param boxHeight height of the parent container
138-
* @param imageWidth width of the [Canvas] that draw [ImageBitmap]
139-
* @param imageHeight height of the [Canvas] that draw [ImageBitmap]
140-
* @param bitmapWidth intrinsic width of the [ImageBitmap]
141-
* @param bitmapHeight intrinsic height of the [ImageBitmap]
142-
* @return [IntRect] that covers [ImageBitmap] bounds. When image [ContentScale] is crop
143-
* this rectangle might return smaller rectangle than actual [ImageBitmap] and left or top
144-
* of the rectangle might be bigger than zero.
145-
*/
146-
private fun getScaledBitmapRect(
147-
boxWidth: Int,
148-
boxHeight: Int,
149-
imageWidth: Float,
150-
imageHeight: Float,
151-
bitmapWidth: Int,
152-
bitmapHeight: Int
153-
): IntRect {
154-
// Get scale of box to width of the image
155-
// We need a rect that contains Bitmap bounds to pass if any child requires it
156-
// For a image with 100x100 px with 300x400 px container and image with crop 400x400px
157-
// So we need to pass top left as 0,50 and size
158-
val scaledBitmapX = boxWidth / imageWidth
159-
val scaledBitmapY = boxHeight / imageHeight
160-
161-
val topLeft = IntOffset(
162-
x = (bitmapWidth * (imageWidth - boxWidth) / imageWidth / 2)
163-
.coerceAtLeast(0f).toInt(),
164-
y = (bitmapHeight * (imageHeight - boxHeight) / imageHeight / 2)
165-
.coerceAtLeast(0f).toInt()
166-
)
167-
168-
val size = IntSize(
169-
width = (bitmapWidth * scaledBitmapX).toInt().coerceAtMost(bitmapWidth),
170-
height = (bitmapHeight * scaledBitmapY).toInt().coerceAtMost(bitmapHeight)
171-
)
172-
173-
return IntRect(offset = topLeft, size = size)
174-
}
175-
176-
/**
177-
* Get [IntSize] of the parent or container that contains [Canvas] that draws [ImageBitmap]
178-
* @param bitmapWidth intrinsic width of the [ImageBitmap]
179-
* @param bitmapHeight intrinsic height of the [ImageBitmap]
180-
* @return size of parent Composable. When Modifier is assigned with fixed or finite size
181-
* they are used, but when any dimension is set to infinity intrinsic dimensions of
182-
* [ImageBitmap] are returned
183-
*/
184-
private fun BoxWithConstraintsScope.getParentSize(
185-
bitmapWidth: Int,
186-
bitmapHeight: Int
187-
): IntSize {
188-
// Check if Composable has fixed size dimensions
189-
val hasBoundedDimens = constraints.hasBoundedWidth && constraints.hasBoundedHeight
190-
// Check if Composable has infinite dimensions
191-
val hasFixedDimens = constraints.hasFixedWidth && constraints.hasFixedHeight
192-
193-
// Box is the parent(BoxWithConstraints) that contains Canvas under the hood
194-
// Canvas aspect ratio or size might not match parent but it's upper bounds are
195-
// what are passed from parent. Canvas cannot be bigger or taller than BoxWithConstraints
196-
val boxWidth: Int = if (hasBoundedDimens || hasFixedDimens) {
197-
constraints.maxWidth
198-
} else {
199-
constraints.minWidth.coerceAtLeast(bitmapWidth)
200-
}
201-
val boxHeight: Int = if (hasBoundedDimens || hasFixedDimens) {
202-
constraints.maxHeight
203-
} else {
204-
constraints.minHeight.coerceAtLeast(bitmapHeight)
205-
}
206-
return IntSize(boxWidth, boxHeight)
207-
}
208-
209134
@Composable
210135
private fun ImageLayout(
211136
constraints: Constraints,
@@ -232,12 +157,11 @@ private fun ImageLayout(
232157
canvasHeightInDp = imageHeight.coerceAtMost(boxHeight.toFloat()).toDp()
233158
}
234159

235-
// Send the not scaled ImageBitmap dimensions which can be larger than Canvas size
236-
// but the one constraint with Canvas size
237-
// because modes like ContentScale.Crop
238-
// which displays center section of the ImageBitmap if it's scaled
239-
// to be bigger than Canvas.
240-
// What user see on screen cannot be bigger than Canvas dimensions
160+
// Send rectangle of Bitmap drawn to Canvas as bitmapRect, content scale modes like
161+
// crop might crop image from center so Rect can be such as IntRect(250,250,500,500)
162+
163+
// canvasWidthInDp, and canvasHeightInDp are Canvas dimensions coerced to Box size
164+
// that covers Canvas
241165
val imageScopeImpl = ImageScopeImpl(
242166
density = density,
243167
constraints = constraints,

0 commit comments

Comments
 (0)