Why Do iOS Thumbnail Images Turn Weird? Unraveling the Mystery
One of my apps generates thumbnails for images. When I attempted to create them using ImageContext, some of the images appeared strange, being either upside-down or right-side-left.
imageOrientation
Fortunately, I could find out how to know the camera orientation when the images were taken.
UIImage has imageOrientation.
public enum Orientation : Int, @unchecked Sendable {
case up = 0
case down = 1
case left = 2
case right = 3
case upMirrored = 4
case downMirrored = 5
case leftMirrored = 6
case rightMirrored = 7
}
And default orientation is up
.
Graphic Context
How to get thumbnail image?
Draw a new image with the original image and a reduced size.
To draw Image, I should use the graphic context.
size : CGSize
...
UIGraphicsBeginImageContext(size);
You would already realized by the wordBegin
. I had to end the image context also.
defer{
UIGraphicsEndImageContext();
}
Move Origin
Before start drawing, we should move origin. Origin is (0,0) position of drawing.
context!.translateBy(x: ..., y: ...);
Default position is 0,0.
Scale
I used scaleBy
, with the argument x
representing the width and y
representing the height. It means if x
is 0.5, the width will be scaled down to half.
context!.scaleBy(x: 0.5 y: 1);
Draw
The last task is drawing. I had to draw the image with CGImage, not UIImage. However, I didn’t have to know about CGImage; we can get CGImage with the cgImage
property of UIImage.
context!.draw(originalImage.cgImage!, in: CGRect(origin: CGPoint.zero, size: size));
Final Image
Now, the drawing is finished. So, how can we obtain the new image? Retrieve the image from the current image.
let newImage = UIGraphicsGetImageFromCurrentImageContext();
Restore
If the real-world material is like this
Up
and the imageOrientation
is set to Orientation.up, the captured image will appear upside-down.
I thought the default is Portrait.
However, when the image orientation is up, the device orientation is landscape left.
Let’s place the cgImage on the 2D graphic coordinate plane.
The reason the image is upside-down is that the cgImage is a raw image without orientation information.
How to restore the image?
First, translate the image.
context!.translateBy(x: 1, y: size.height);
Second, flip it along the Y-axis.
context!.scaleBy(x: 1, y: -1);
When the scale value is less than zero, drawing will be opposite direction.
Right
I thought if the picture is taken with the device in landscape orientation to the left,
The image will be captured as follows:
Place the image on the coordinate.
Differently from the down
case, in this time, rotate the image first.
context.rotate(by: CGFloat(Double.pi / 2));
I used M_PI_2, but it is deprecated now.
Next, flip it by Y-axis.
context.scaleBy(x: 1, y: -1);
Wait…! Why not like x: -1
? It was flipped horizontally. Because since rotating the context, axes were swapped.
Left
If I took the photo after rotating the device left,
The cgImage will look like this.
Left is more complex.
Let’s start again. Rotate the image left.
ctx.rotate(by: -CGFloat(Double.pi / 2));
Flip it to get the originally rotated version.
ctx.scaleBy(x: 1, y: -1);
However, I couldn’t see anything because the final image is out of bounds. Move it within the bounds.
ctx.translateBy(x: -size.height, y: -size.width);
Why I had to move it in the negative direction? Because when I swapped axes, directions were also swapped.
It also means width is swapped with height.
size: CGSize(width: size.height, height: size.width))
I also created an example. Check this github repository.
If you found this post helpful, please give it a round of applause 👏. Explore more iOS-related content in my other posts.