Skip to content

Commit 1bbc904

Browse files
authored
fix: stabilize iOS harness image dimensions (#152)
1 parent bc4de24 commit 1bbc904

2 files changed

Lines changed: 35 additions & 16 deletions

File tree

packages/react-native-nitro-image/ios/HybridImageFactory.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class HybridImageFactory: HybridImageFactorySpec {
1717
let size = CGSize(width: width, height: height)
1818
let format = UIGraphicsImageRendererFormat()
1919
format.opaque = !enableAlpha
20+
format.scale = 1
2021
// 2. Create a new UIImage
2122
let uiImage = UIGraphicsImageRenderer(size: size, format: format).image { canvas in
2223
if let fill {

packages/react-native-nitro-image/ios/Public/NativeImage.swift

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
import UIKit
99
import NitroModules
1010

11+
private func ceilPixelDimension(_ value: CGFloat) -> CGFloat {
12+
let rounded = value.rounded()
13+
if abs(value - rounded) < 0.001 {
14+
return rounded
15+
}
16+
return ceil(value)
17+
}
18+
1119
/**
1220
* A protocol that represents a native image.
1321
* This can be used to downcast from `HybridImageSpec`
@@ -63,20 +71,25 @@ public extension NativeImage {
6371
return HybridImage(uiImage: rotated)
6472
} else {
6573
// Slow path: we actually rotate using UIGraphicsImageRenderer
66-
let renderer = UIGraphicsImageRenderer(size: uiImage.size)
74+
let sourceSize = uiImage.size
75+
let angleInRadians = CGFloat(degrees * .pi / 180)
76+
let rotatedBounds = CGRect(origin: .zero, size: sourceSize)
77+
.applying(CGAffineTransform(rotationAngle: angleInRadians))
78+
let outputSize = CGSize(width: ceilPixelDimension(rotatedBounds.width),
79+
height: ceilPixelDimension(rotatedBounds.height))
80+
let format = UIGraphicsImageRendererFormat()
81+
format.scale = 1
82+
let renderer = UIGraphicsImageRenderer(size: outputSize, format: format)
6783
let rotatedImage = renderer.image { context in
68-
let width = uiImage.size.width
69-
let height = uiImage.size.height
7084
// 1. Move to the center of the image so our origin is the center
71-
context.cgContext.translateBy(x: width / 2, y: height / 2)
72-
// 2. Rotate by the given radians
73-
let radians = degrees * .pi / 180
74-
context.cgContext.rotate(by: radians)
85+
context.cgContext.translateBy(x: outputSize.width / 2, y: outputSize.height / 2)
86+
// 2. Rotate by the requested angle
87+
context.cgContext.rotate(by: angleInRadians)
7588
// 3. Draw the Image offset by half the frame so we counter our center origin from step 1.
76-
let rect = CGRect(x: -(width / 2),
77-
y: -(height / 2),
78-
width: width,
79-
height: height)
89+
let rect = CGRect(x: -(sourceSize.width / 2),
90+
y: -(sourceSize.height / 2),
91+
width: sourceSize.width,
92+
height: sourceSize.height)
8093
uiImage.draw(in: rect)
8194
}
8295
return HybridImage(uiImage: rotatedImage)
@@ -97,7 +110,9 @@ public extension NativeImage {
97110
}
98111
let targetSize = CGSize(width: width, height: height)
99112

100-
let renderer = UIGraphicsImageRenderer(size: targetSize)
113+
let format = UIGraphicsImageRendererFormat()
114+
format.scale = 1
115+
let renderer = UIGraphicsImageRenderer(size: targetSize, format: format)
101116
let resizedImage = renderer.image { context in
102117
let targetRect = CGRect(origin: .zero, size: targetSize)
103118
uiImage.draw(in: targetRect)
@@ -124,8 +139,10 @@ public extension NativeImage {
124139
throw RuntimeError.error(withMessage: "This image does not have an underlying .cgImage!")
125140
}
126141

127-
let targetRect = CGRect(origin: CGPoint(x: startX, y: startY),
128-
size: CGSize(width: uiImage.size.width, height: uiImage.size.height))
142+
let targetRect = CGRect(x: startX,
143+
y: startY,
144+
width: targetWidth,
145+
height: targetHeight)
129146
guard let croppedCgImage = cgImage.cropping(to: targetRect) else {
130147
throw RuntimeError.error(withMessage: "Failed to crop CGImage to \(targetRect)!")
131148
}
@@ -192,8 +209,9 @@ public extension NativeImage {
192209
throw RuntimeError.error(withMessage: "The given image (\(newImage)) is not a `NativeImage`!")
193210
}
194211
// 1. Prepare a UIImage rendered
195-
let renderer = UIGraphicsImageRenderer(size: uiImage.size,
196-
format: uiImage.imageRendererFormat)
212+
let format = UIGraphicsImageRendererFormat()
213+
format.scale = 1
214+
let renderer = UIGraphicsImageRenderer(size: uiImage.size, format: format)
197215
let renderedImage = renderer.image { context in
198216
// 2. Render our own image (copy)
199217
self.uiImage.draw(at: .zero)

0 commit comments

Comments
 (0)