From 3bc04df02b48274cce8aacb52af875531d584bfc Mon Sep 17 00:00:00 2001 From: Woraphot Chokratanasombat Date: Sun, 2 Jun 2024 15:54:24 +0700 Subject: [PATCH 1/4] [UIImage+PhotoManipulator] Change: Add rotate() --- README.md | 9 +++- WCPhotoManipulator.xcodeproj/project.pbxproj | 6 ++- WCPhotoManipulator/RotationMode.swift | 23 ++++++++++ .../UIImage+PhotoManipulator.swift | 8 ++++ .../UIImage+PhotoManipulatorObjCTests.m | 40 +++++++++++++++++ .../UIImage+PhotoManipulatorSwiftTests.swift | 44 ++++++++++++++++++- 6 files changed, 126 insertions(+), 4 deletions(-) create mode 100644 WCPhotoManipulator/RotationMode.swift diff --git a/README.md b/README.md index 4fbc3bb..cf5905e 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,19 @@ Overlay image on top of background image | position | CGPoint | Yes | Position of overlay image in background image | ### [image flip] -Flip the image horizontally and/or vertically +Flip the image horizontally, vertically or both | NAME | TYPE | REQUIRED | DESCRIPTION | |------------|-----------------------|----------|------------------------------------------------------------------------| | mode | FlipMode | Yes | Flip mode .Vertical or .Horizontal or .Both | +### [image rotate] +Rotate the image 90°, 180° or 270° + +| NAME | TYPE | REQUIRED | DESCRIPTION | +|------------|-----------------------|----------|------------------------------------------------------------------------| +| mode | RotationMode | Yes | Rotation mode .R90 (90° Clockwise), .R180 (180° Half Rotation) or .R270 (270° Clockwise, aka 90° Counterclockwise) | + ## Usage FileUtils diff --git a/WCPhotoManipulator.xcodeproj/project.pbxproj b/WCPhotoManipulator.xcodeproj/project.pbxproj index 91a934f..418afa1 100644 --- a/WCPhotoManipulator.xcodeproj/project.pbxproj +++ b/WCPhotoManipulator.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ CA10C5262BB331B7008464A7 /* FlipMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA10C5252BB331B7008464A7 /* FlipMode.swift */; }; + CA11C0FB2C0C1E9B005542AA /* RotationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA11C0FA2C0C1E9B005542AA /* RotationMode.swift */; }; CC92AF5124D250160061CC87 /* FoolSonarcloud.m in Sources */ = {isa = PBXBuildFile; fileRef = CC92AF5024D250160061CC87 /* FoolSonarcloud.m */; }; CC92AF6A24D545070061CC87 /* BitmapUtilsSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC92AF6924D545070061CC87 /* BitmapUtilsSwiftTests.swift */; }; CC92AF6C24D558500061CC87 /* FileUtilsSwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC92AF6B24D558500061CC87 /* FileUtilsSwiftTests.swift */; }; @@ -53,6 +54,7 @@ /* Begin PBXFileReference section */ CA10C5252BB331B7008464A7 /* FlipMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlipMode.swift; sourceTree = ""; }; + CA11C0FA2C0C1E9B005542AA /* RotationMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RotationMode.swift; sourceTree = ""; }; CC92AF4F24D250160061CC87 /* FoolSonarcloud.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FoolSonarcloud.h; sourceTree = ""; }; CC92AF5024D250160061CC87 /* FoolSonarcloud.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FoolSonarcloud.m; sourceTree = ""; }; CC92AF6924D545070061CC87 /* BitmapUtilsSwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitmapUtilsSwiftTests.swift; sourceTree = ""; }; @@ -123,11 +125,12 @@ children = ( CCC2506B24D05C97000BAC48 /* BitmapUtils.swift */, CCC2505824CFF0C1000BAC48 /* FileUtils.swift */, + CA10C5252BB331B7008464A7 /* FlipMode.swift */, CC92AF4F24D250160061CC87 /* FoolSonarcloud.h */, CC92AF5024D250160061CC87 /* FoolSonarcloud.m */, CCC2503B24CFDE4B000BAC48 /* MimeUtils.swift */, CCC2506D24D05EE7000BAC48 /* ResizeMode.swift */, - CA10C5252BB331B7008464A7 /* FlipMode.swift */, + CA11C0FA2C0C1E9B005542AA /* RotationMode.swift */, CCC2506F24D060C9000BAC48 /* UIImage+PhotoManipulator.swift */, CCC2505124CFE1D2000BAC48 /* WCPhotoManipulator-Bridging-Header.h */, ); @@ -269,6 +272,7 @@ files = ( CCC2506C24D05C97000BAC48 /* BitmapUtils.swift in Sources */, CCC2507024D060C9000BAC48 /* UIImage+PhotoManipulator.swift in Sources */, + CA11C0FB2C0C1E9B005542AA /* RotationMode.swift in Sources */, CA10C5262BB331B7008464A7 /* FlipMode.swift in Sources */, CCC2503C24CFDE4B000BAC48 /* MimeUtils.swift in Sources */, CC92AF5124D250160061CC87 /* FoolSonarcloud.m in Sources */, diff --git a/WCPhotoManipulator/RotationMode.swift b/WCPhotoManipulator/RotationMode.swift new file mode 100644 index 0000000..88505a9 --- /dev/null +++ b/WCPhotoManipulator/RotationMode.swift @@ -0,0 +1,23 @@ +// +// RotationMode.swift +// WCPhotoManipulator +// +// Created by Woraphot Chokratanasombat on 2/6/2567 BE. +// Copyright © 2567 BE Woraphot Chokratanasombat. All rights reserved. +// + +import Foundation +import UIKit + +@objc public enum RotationMode: Int { + case None = 0, R90 = 90, R180 = 180, R270 = 270 + + func transform() -> CGAffineTransform { + switch self { + case .None: + return CGAffineTransformIdentity + default: + return CGAffineTransformMakeRotation(-CGFloat(self.rawValue) * CGFloat.pi / 180.0) + } + } +} diff --git a/WCPhotoManipulator/UIImage+PhotoManipulator.swift b/WCPhotoManipulator/UIImage+PhotoManipulator.swift index 03c2812..bb7d23f 100644 --- a/WCPhotoManipulator/UIImage+PhotoManipulator.swift +++ b/WCPhotoManipulator/UIImage+PhotoManipulator.swift @@ -117,6 +117,14 @@ public extension UIImage { return UIImage(ciImage: cgimage) } + // Flip + @objc func rotate(_ rotateMode: RotationMode) -> UIImage { + if (rotateMode == .None) { return self } + + let cgimage = ciImage().transformed(by: rotateMode.transform()) + return UIImage(ciImage: cgimage) + } + // Flip @objc func ciImage() -> CIImage { return (ciImage != nil) ? ciImage! : CIImage(cgImage: cgImage!) diff --git a/WCPhotoManipulatorTests/UIImage+PhotoManipulatorObjCTests.m b/WCPhotoManipulatorTests/UIImage+PhotoManipulatorObjCTests.m index 9e4c4ef..17ed876 100644 --- a/WCPhotoManipulatorTests/UIImage+PhotoManipulatorObjCTests.m +++ b/WCPhotoManipulatorTests/UIImage+PhotoManipulatorObjCTests.m @@ -158,4 +158,44 @@ - (void)testFlip_WhenHasBoth_ShouldReturnCorrectly { XCTAssertEqual(image4.size.width, 800); XCTAssertEqual(image4.size.height, 530); } + +- (void)testRotate_WhenR90_ShouldReturnCorrectly { + image = [UIImage imageNamedTest:@"background.jpg"]; + XCTAssertNotNil(image); + + image = [image rotate:RotationModeR90]; + XCTAssertNotNil(image); + XCTAssertEqual(image.size.width, 530); + XCTAssertEqual(image.size.height, 800); +} + +- (void)testRotate_WhenR180_ShouldReturnCorrectly { + image = [UIImage imageNamedTest:@"background.jpg"]; + XCTAssertNotNil(image); + + image = [image rotate:RotationModeR180]; + XCTAssertNotNil(image); + XCTAssertEqual(image.size.width, 800); + XCTAssertEqual(image.size.height, 530); +} + +- (void)testRotate_WhenR270_ShouldReturnCorrectly { + image = [UIImage imageNamedTest:@"background.jpg"]; + XCTAssertNotNil(image); + + image = [image rotate:RotationModeR270]; + XCTAssertNotNil(image); + XCTAssertEqual(image.size.width, 530); + XCTAssertEqual(image.size.height, 800); +} + +- (void)testRotate_WhenNone_ShouldReturnCorrectly { + image = [UIImage imageNamedTest:@"background.jpg"]; + XCTAssertNotNil(image); + + image = [image rotate:RotationModeNone]; + XCTAssertNotNil(image); + XCTAssertEqual(image.size.width, 800); + XCTAssertEqual(image.size.height, 530); +} @end diff --git a/WCPhotoManipulatorTests/UIImage+PhotoManipulatorSwiftTests.swift b/WCPhotoManipulatorTests/UIImage+PhotoManipulatorSwiftTests.swift index df77cd8..3af2d93 100644 --- a/WCPhotoManipulatorTests/UIImage+PhotoManipulatorSwiftTests.swift +++ b/WCPhotoManipulatorTests/UIImage+PhotoManipulatorSwiftTests.swift @@ -74,7 +74,8 @@ class UIImage_PhotoManipulatorSwiftTests: XCTestCase { XCTAssertEqual(image.size, CGSize(width: 332, height: 70)) XCTAssertEqual(actualColor, expectedColor) } - + + // Draw Text func testDrawText_WhenUseFontAndNoScale_ShouldReturnCorrectly() throws { image = UIImage.init(namedTest: "background.jpg") XCTAssertNotNil(image) @@ -111,6 +112,7 @@ class UIImage_PhotoManipulatorSwiftTests: XCTestCase { XCTAssertEqual(image.size, CGSize(width: 800, height: 530)) } + // Overlay Image func testOverlayImage_WhenNoScale_ShouldReturnCorrectly() throws { image = UIImage.init(namedTest: "background.jpg") overlay = UIImage.init(namedTest: "overlay.png") @@ -130,7 +132,8 @@ class UIImage_PhotoManipulatorSwiftTests: XCTestCase { XCTAssertNotNil(image) XCTAssertEqual(image.size, CGSize(width: 800, height: 530)) } - + + // Flip func testFlip_WhenHasHorizontal_ShouldReturnCorrectly() throws { image = UIImage.init(namedTest: "background.jpg") XCTAssertNotNil(image) @@ -179,4 +182,41 @@ class UIImage_PhotoManipulatorSwiftTests: XCTestCase { XCTAssertNotNil(image4) XCTAssertEqual(image4.size, CGSize(width: 800, height: 530)) } + + // Rotate + func testRotate_WhenR90_ShouldReturnCorrectly() throws { + image = UIImage.init(namedTest: "background.jpg") + XCTAssertNotNil(image) + + let actual = image.rotate(.R90) + XCTAssertNotNil(actual) + XCTAssertEqual(actual.size, CGSize(width: 530, height: 800)) + } + + func testRotate_WhenR180_ShouldReturnCorrectly() throws { + image = UIImage.init(namedTest: "background.jpg") + XCTAssertNotNil(image) + + let actual = image.rotate(.R180) + XCTAssertNotNil(actual) + XCTAssertEqual(actual.size, CGSize(width: 800, height: 530)) + } + + func testRotate_WhenR270_ShouldReturnCorrectly() throws { + image = UIImage.init(namedTest: "background.jpg") + XCTAssertNotNil(image) + + let actual = image.rotate(.R270) + XCTAssertNotNil(actual) + XCTAssertEqual(actual.size, CGSize(width: 530, height: 800)) + } + + func testRotate_WhenNone_ShouldReturnCorrectly() throws { + image = UIImage.init(namedTest: "background.jpg") + XCTAssertNotNil(image) + + let actual = image.rotate(.None) + XCTAssertNotNil(actual) + XCTAssertEqual(actual.size, CGSize(width: 800, height: 530)) + } } From 364631ea5d5298c21c0077776f3b046addf9bbf7 Mon Sep 17 00:00:00 2001 From: Woraphot Chokratanasombat Date: Wed, 5 Jun 2024 01:23:17 +0700 Subject: [PATCH 2/4] [Refactor@SonarCloud] Fix: Use if instead switch, one statement per line --- WCPhotoManipulator/RotationMode.swift | 9 +++------ WCPhotoManipulator/UIImage+PhotoManipulator.swift | 3 ++- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/WCPhotoManipulator/RotationMode.swift b/WCPhotoManipulator/RotationMode.swift index 88505a9..f316e86 100644 --- a/WCPhotoManipulator/RotationMode.swift +++ b/WCPhotoManipulator/RotationMode.swift @@ -13,11 +13,8 @@ import UIKit case None = 0, R90 = 90, R180 = 180, R270 = 270 func transform() -> CGAffineTransform { - switch self { - case .None: - return CGAffineTransformIdentity - default: - return CGAffineTransformMakeRotation(-CGFloat(self.rawValue) * CGFloat.pi / 180.0) - } + if (self == .None) { return CGAffineTransformIdentity } + + return CGAffineTransformMakeRotation(-CGFloat(self.rawValue) * CGFloat.pi / 180.0) } } diff --git a/WCPhotoManipulator/UIImage+PhotoManipulator.swift b/WCPhotoManipulator/UIImage+PhotoManipulator.swift index bb7d23f..4e1acd3 100644 --- a/WCPhotoManipulator/UIImage+PhotoManipulator.swift +++ b/WCPhotoManipulator/UIImage+PhotoManipulator.swift @@ -127,6 +127,7 @@ public extension UIImage { // Flip @objc func ciImage() -> CIImage { - return (ciImage != nil) ? ciImage! : CIImage(cgImage: cgImage!) + if (ciImage != nil) { return ciImage! } + return CIImage(cgImage: cgImage!) } } From 6d64857528bc43a3b4473b2ced6cdc8a35cc8912 Mon Sep 17 00:00:00 2001 From: Woraphot Chokratanasombat Date: Wed, 5 Jun 2024 01:26:48 +0700 Subject: [PATCH 3/4] [RotationMode@transform()] Change: Remove case .None --- WCPhotoManipulator/RotationMode.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/WCPhotoManipulator/RotationMode.swift b/WCPhotoManipulator/RotationMode.swift index f316e86..b143177 100644 --- a/WCPhotoManipulator/RotationMode.swift +++ b/WCPhotoManipulator/RotationMode.swift @@ -13,8 +13,6 @@ import UIKit case None = 0, R90 = 90, R180 = 180, R270 = 270 func transform() -> CGAffineTransform { - if (self == .None) { return CGAffineTransformIdentity } - return CGAffineTransformMakeRotation(-CGFloat(self.rawValue) * CGFloat.pi / 180.0) } } From 6dfa0c3ae5cf1adc031dce66688961983ac6277a Mon Sep 17 00:00:00 2001 From: Woraphot Chokratanasombat Date: Wed, 5 Jun 2024 01:30:57 +0700 Subject: [PATCH 4/4] [Refactor@SonarCloud] Fix: One statement per line --- WCPhotoManipulator/UIImage+PhotoManipulator.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WCPhotoManipulator/UIImage+PhotoManipulator.swift b/WCPhotoManipulator/UIImage+PhotoManipulator.swift index 4e1acd3..90f8948 100644 --- a/WCPhotoManipulator/UIImage+PhotoManipulator.swift +++ b/WCPhotoManipulator/UIImage+PhotoManipulator.swift @@ -119,7 +119,9 @@ public extension UIImage { // Flip @objc func rotate(_ rotateMode: RotationMode) -> UIImage { - if (rotateMode == .None) { return self } + if (rotateMode == .None) { + return self + } let cgimage = ciImage().transformed(by: rotateMode.transform()) return UIImage(ciImage: cgimage) @@ -127,7 +129,10 @@ public extension UIImage { // Flip @objc func ciImage() -> CIImage { - if (ciImage != nil) { return ciImage! } + if (ciImage != nil) { + return ciImage! + } + return CIImage(cgImage: cgImage!) } }