diff options
author | gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2009-06-02 17:47:08 +0000 |
---|---|---|
committer | gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3> | 2009-06-02 17:47:08 +0000 |
commit | c7f56d0f7ff41fdb99c1e97f447e5aa843e7d9ef (patch) | |
tree | 6a197f55c98cd138deeb4adaca5fbcbd187fdcdb | |
parent | e67090cdf08fe66642f0d14f46629ac0ba703390 (diff) |
[Author: altse]
Add category extension to UIImage to allow resizing while preserving aspect
ratios and optional image clipping.
R=thomasvl,dmaclach
DELTA=507 (507 added, 0 deleted, 0 changed)
23 files changed, 489 insertions, 0 deletions
diff --git a/GTMiPhone.xcodeproj/project.pbxproj b/GTMiPhone.xcodeproj/project.pbxproj index d8c2f4e..3f0adce 100644 --- a/GTMiPhone.xcodeproj/project.pbxproj +++ b/GTMiPhone.xcodeproj/project.pbxproj @@ -26,6 +26,26 @@ 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; }; 6294461C0EDE178D009295EA /* GTMNSArray+MergeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 629446190EDE177A009295EA /* GTMNSArray+MergeTest.m */; }; 6294461D0EDE17A0009295EA /* GTMNSArray+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 629446180EDE177A009295EA /* GTMNSArray+Merge.m */; }; + 64D0F5C80FD3E65C00506CC7 /* GTMUIImage+ResizeTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 64D0F5C60FD3E65C00506CC7 /* GTMUIImage+ResizeTest.m */; }; + 64D0F5C90FD3E65C00506CC7 /* GTMUIImage+Resize.m in Sources */ = {isa = PBXBuildFile; fileRef = 64D0F5C70FD3E65C00506CC7 /* GTMUIImage+Resize.m */; }; + 64D0F5DE0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_60x40.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5CC0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_60x40.png */; }; + 64D0F5DF0FD3E68400506CC7 /* GTMUIImage+Resize_50x100.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5CD0FD3E68400506CC7 /* GTMUIImage+Resize_50x100.png */; }; + 64D0F5E00FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_clip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5CE0FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_clip.png */; }; + 64D0F5E10FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_noclip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5CF0FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_noclip.png */; }; + 64D0F5E20FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_clip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D00FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_clip.png */; }; + 64D0F5E30FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_noclip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D10FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_noclip.png */; }; + 64D0F5E40FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_clip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D20FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_clip.png */; }; + 64D0F5E50FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_noclip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D30FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_noclip.png */; }; + 64D0F5E60FD3E68400506CC7 /* GTMUIImage+Resize_100x50.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D40FD3E68400506CC7 /* GTMUIImage+Resize_100x50.png */; }; + 64D0F5E70FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_clip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D50FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_clip.png */; }; + 64D0F5E80FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_noclip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D60FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_noclip.png */; }; + 64D0F5E90FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_clip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D70FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_clip.png */; }; + 64D0F5EA0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_noclip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D80FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_noclip.png */; }; + 64D0F5EB0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_clip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5D90FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_clip.png */; }; + 64D0F5EC0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_noclip.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5DA0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_noclip.png */; }; + 64D0F5ED0FD3E68400506CC7 /* GTMUIImage+Resize_100x100.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5DB0FD3E68400506CC7 /* GTMUIImage+Resize_100x100.png */; }; + 64D0F5EE0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_40x60.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5DC0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_40x60.png */; }; + 64D0F5EF0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_50x50.png in Resources */ = {isa = PBXBuildFile; fileRef = 64D0F5DD0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_50x50.png */; }; 67A7820C0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 67A7820B0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m */; }; 8B308BCE0DAD0B8400183556 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */; }; 8B3AA8F30E032FC7007E31B5 /* GTMNSString+URLArguments.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B3AA8F10E032FC7007E31B5 /* GTMNSString+URLArguments.m */; }; @@ -120,6 +140,27 @@ 629446170EDE177A009295EA /* GTMNSArray+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMNSArray+Merge.h"; sourceTree = "<group>"; }; 629446180EDE177A009295EA /* GTMNSArray+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSArray+Merge.m"; sourceTree = "<group>"; }; 629446190EDE177A009295EA /* GTMNSArray+MergeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMNSArray+MergeTest.m"; sourceTree = "<group>"; }; + 64D0F5C50FD3E65C00506CC7 /* GTMUIImage+Resize.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "GTMUIImage+Resize.h"; sourceTree = "<group>"; }; + 64D0F5C60FD3E65C00506CC7 /* GTMUIImage+ResizeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIImage+ResizeTest.m"; sourceTree = "<group>"; }; + 64D0F5C70FD3E65C00506CC7 /* GTMUIImage+Resize.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "GTMUIImage+Resize.m"; sourceTree = "<group>"; }; + 64D0F5CC0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_60x40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x100_to_60x40.png"; path = "TestData/GTMUIImage+Resize_100x100_to_60x40.png"; sourceTree = "<group>"; }; + 64D0F5CD0FD3E68400506CC7 /* GTMUIImage+Resize_50x100.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_50x100.png"; path = "TestData/GTMUIImage+Resize_50x100.png"; sourceTree = "<group>"; }; + 64D0F5CE0FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_clip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_50x100_to_40x60_clip.png"; path = "TestData/GTMUIImage+Resize_50x100_to_40x60_clip.png"; sourceTree = "<group>"; }; + 64D0F5CF0FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_noclip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_50x100_to_40x60_noclip.png"; path = "TestData/GTMUIImage+Resize_50x100_to_40x60_noclip.png"; sourceTree = "<group>"; }; + 64D0F5D00FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_clip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_50x100_to_50x50_clip.png"; path = "TestData/GTMUIImage+Resize_50x100_to_50x50_clip.png"; sourceTree = "<group>"; }; + 64D0F5D10FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_noclip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_50x100_to_50x50_noclip.png"; path = "TestData/GTMUIImage+Resize_50x100_to_50x50_noclip.png"; sourceTree = "<group>"; }; + 64D0F5D20FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_clip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_50x100_to_60x40_clip.png"; path = "TestData/GTMUIImage+Resize_50x100_to_60x40_clip.png"; sourceTree = "<group>"; }; + 64D0F5D30FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_noclip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_50x100_to_60x40_noclip.png"; path = "TestData/GTMUIImage+Resize_50x100_to_60x40_noclip.png"; sourceTree = "<group>"; }; + 64D0F5D40FD3E68400506CC7 /* GTMUIImage+Resize_100x50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x50.png"; path = "TestData/GTMUIImage+Resize_100x50.png"; sourceTree = "<group>"; }; + 64D0F5D50FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_clip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x50_to_40x60_clip.png"; path = "TestData/GTMUIImage+Resize_100x50_to_40x60_clip.png"; sourceTree = "<group>"; }; + 64D0F5D60FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_noclip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x50_to_40x60_noclip.png"; path = "TestData/GTMUIImage+Resize_100x50_to_40x60_noclip.png"; sourceTree = "<group>"; }; + 64D0F5D70FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_clip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x50_to_50x50_clip.png"; path = "TestData/GTMUIImage+Resize_100x50_to_50x50_clip.png"; sourceTree = "<group>"; }; + 64D0F5D80FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_noclip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x50_to_50x50_noclip.png"; path = "TestData/GTMUIImage+Resize_100x50_to_50x50_noclip.png"; sourceTree = "<group>"; }; + 64D0F5D90FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_clip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x50_to_60x40_clip.png"; path = "TestData/GTMUIImage+Resize_100x50_to_60x40_clip.png"; sourceTree = "<group>"; }; + 64D0F5DA0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_noclip.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x50_to_60x40_noclip.png"; path = "TestData/GTMUIImage+Resize_100x50_to_60x40_noclip.png"; sourceTree = "<group>"; }; + 64D0F5DB0FD3E68400506CC7 /* GTMUIImage+Resize_100x100.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x100.png"; path = "TestData/GTMUIImage+Resize_100x100.png"; sourceTree = "<group>"; }; + 64D0F5DC0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_40x60.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x100_to_40x60.png"; path = "TestData/GTMUIImage+Resize_100x100_to_40x60.png"; sourceTree = "<group>"; }; + 64D0F5DD0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_50x50.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "GTMUIImage+Resize_100x100_to_50x50.png"; path = "TestData/GTMUIImage+Resize_100x100_to_50x50.png"; sourceTree = "<group>"; }; 67A7820A0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMIPhoneUnitTestDelegate.h; sourceTree = "<group>"; }; 67A7820B0E00927400EBF506 /* GTMIPhoneUnitTestDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMIPhoneUnitTestDelegate.m; sourceTree = "<group>"; }; 8B308BCD0DAD0B8400183556 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; @@ -299,12 +340,41 @@ name = Frameworks; sourceTree = "<group>"; }; + 64D0F5CB0FD3E66A00506CC7 /* TestData */ = { + isa = PBXGroup; + children = ( + 64D0F5CC0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_60x40.png */, + 64D0F5CD0FD3E68400506CC7 /* GTMUIImage+Resize_50x100.png */, + 64D0F5CE0FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_clip.png */, + 64D0F5CF0FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_noclip.png */, + 64D0F5D00FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_clip.png */, + 64D0F5D10FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_noclip.png */, + 64D0F5D20FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_clip.png */, + 64D0F5D30FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_noclip.png */, + 64D0F5D40FD3E68400506CC7 /* GTMUIImage+Resize_100x50.png */, + 64D0F5D50FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_clip.png */, + 64D0F5D60FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_noclip.png */, + 64D0F5D70FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_clip.png */, + 64D0F5D80FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_noclip.png */, + 64D0F5D90FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_clip.png */, + 64D0F5DA0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_noclip.png */, + 64D0F5DB0FD3E68400506CC7 /* GTMUIImage+Resize_100x100.png */, + 64D0F5DC0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_40x60.png */, + 64D0F5DD0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_50x50.png */, + ); + name = TestData; + sourceTree = "<group>"; + }; 8BA5F4060E75669000798036 /* iPhone */ = { isa = PBXGroup; children = ( F4E3B3D60EB5EF2400CB713D /* GTMUIFont+LineHeight.h */, F4E3B3D70EB5EF2400CB713D /* GTMUIFont+LineHeight.m */, F4E3B3E10EB5EF9A00CB713D /* GTMUIFont+LineHeightTest.m */, + 64D0F5C50FD3E65C00506CC7 /* GTMUIImage+Resize.h */, + 64D0F5C70FD3E65C00506CC7 /* GTMUIImage+Resize.m */, + 64D0F5C60FD3E65C00506CC7 /* GTMUIImage+ResizeTest.m */, + 64D0F5CB0FD3E66A00506CC7 /* TestData */, ); path = iPhone; sourceTree = "<group>"; @@ -554,6 +624,24 @@ 8BC04DE80DB023D400C2D1CA /* ReleaseNotes.txt in Resources */, 8BFE15C70FB0F764001BE894 /* GTMABAddressBook.strings in Resources */, 8BFE15C90FB0F764001BE894 /* phone.png in Resources */, + 64D0F5DE0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_60x40.png in Resources */, + 64D0F5DF0FD3E68400506CC7 /* GTMUIImage+Resize_50x100.png in Resources */, + 64D0F5E00FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_clip.png in Resources */, + 64D0F5E10FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_40x60_noclip.png in Resources */, + 64D0F5E20FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_clip.png in Resources */, + 64D0F5E30FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_50x50_noclip.png in Resources */, + 64D0F5E40FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_clip.png in Resources */, + 64D0F5E50FD3E68400506CC7 /* GTMUIImage+Resize_50x100_to_60x40_noclip.png in Resources */, + 64D0F5E60FD3E68400506CC7 /* GTMUIImage+Resize_100x50.png in Resources */, + 64D0F5E70FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_clip.png in Resources */, + 64D0F5E80FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_40x60_noclip.png in Resources */, + 64D0F5E90FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_clip.png in Resources */, + 64D0F5EA0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_50x50_noclip.png in Resources */, + 64D0F5EB0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_clip.png in Resources */, + 64D0F5EC0FD3E68400506CC7 /* GTMUIImage+Resize_100x50_to_60x40_noclip.png in Resources */, + 64D0F5ED0FD3E68400506CC7 /* GTMUIImage+Resize_100x100.png in Resources */, + 64D0F5EE0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_40x60.png in Resources */, + 64D0F5EF0FD3E68400506CC7 /* GTMUIImage+Resize_100x100_to_50x50.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -646,6 +734,8 @@ 8BD35C930FB234E1009058F5 /* GTMNSScanner+JSONTest.m in Sources */, 8BF4D4180FC74998009ABC3F /* GTMGoogleSearchTest.m in Sources */, 8BF4D4190FC7499D009ABC3F /* GTMGoogleSearch.m in Sources */, + 64D0F5C80FD3E65C00506CC7 /* GTMUIImage+ResizeTest.m in Sources */, + 64D0F5C90FD3E65C00506CC7 /* GTMUIImage+Resize.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index c3e1036..9e6efd4 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -299,6 +299,9 @@ Changes since 1.5.1 - Added GTMGoogleSearch to foundation to make doing google searches easier. +- Added GTMUIImage+Resize for iPhone to conveniently handle generating resized + UIImages while preserving aspect ratios. + Release 1.5.1 Changes since 1.5.0 16-June-2008 diff --git a/iPhone/GTMUIImage+Resize.h b/iPhone/GTMUIImage+Resize.h new file mode 100644 index 0000000..25e2125 --- /dev/null +++ b/iPhone/GTMUIImage+Resize.h @@ -0,0 +1,40 @@ +// +// GTMUIImage+Resize.h +// +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import <UIKit/UIKit.h> + +@interface UIImage (GTMUIImageResizeAdditions) + +// Returns an image resized to |targetSize|. +// +// If |preserveAspectRatio| is YES, the original image aspect ratio is +// preserved. +// +// When |preserveAspectRatio| is YES and if |targetSize|'s aspect ratio +// is different from the image, the resulting image will be shrunken to +// a size that is within |targetSize|. +// +// To preserve the |targetSize| when |preserveAspectRatio| is YES, set +// |trimToFit| to YES. The resulting image will be the largest proportion +// of the receiver that fits in the targetSize, aligned to center of the image. +// +// Image interpolation level for resizing is set to kCGInterpolationDefault. +- (UIImage *)gtm_imageByResizingToSize:(CGSize)targetSize + preserveAspectRatio:(BOOL)preserveAspectRatio + trimToFit:(BOOL)trimToFit; +@end diff --git a/iPhone/GTMUIImage+Resize.m b/iPhone/GTMUIImage+Resize.m new file mode 100644 index 0000000..f93a521 --- /dev/null +++ b/iPhone/GTMUIImage+Resize.m @@ -0,0 +1,100 @@ +// +// GTMUIImage+Resize.m +// +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMUIImage+Resize.h" + +@implementation UIImage (GTMUIImageResizeAdditions) + +- (UIImage *)gtm_imageByResizingToSize:(CGSize)targetSize + preserveAspectRatio:(BOOL)preserveAspectRatio + trimToFit:(BOOL)trimToFit { + CGSize imageSize = [self size]; + if (imageSize.height < 1 || imageSize.width < 1) { + return nil; + } + if (targetSize.height < 1 || targetSize.width < 1) { + return nil; + } + CGFloat aspectRatio = imageSize.width / imageSize.height; + CGFloat targetAspectRatio = targetSize.width / targetSize.height; + + CGRect projectTo = CGRectZero; + if (preserveAspectRatio) { + if (trimToFit) { + // Scale and clip image so that the aspect ratio is preserved and the + // target size is filled. + if (targetAspectRatio < aspectRatio) { + // clip the x-axis. + projectTo.size.width = targetSize.height * aspectRatio; + projectTo.size.height = targetSize.height; + projectTo.origin.x = (targetSize.width - projectTo.size.width) / 2; + projectTo.origin.y = 0; + } else { + // clip the y-axis. + projectTo.size.width = targetSize.width; + projectTo.size.height = targetSize.width / aspectRatio; + projectTo.origin.x = 0; + projectTo.origin.y = (targetSize.height - projectTo.size.height) / 2; + } + } else { + // Scale image to ensure it fits inside the specified targetSize. + if (targetAspectRatio < aspectRatio) { + // target is less wide than the original. + projectTo.size.width = targetSize.width; + projectTo.size.height = projectTo.size.width / aspectRatio; + targetSize = projectTo.size; + } else { + // target is wider than the original. + projectTo.size.height = targetSize.height; + projectTo.size.width = projectTo.size.height * aspectRatio; + targetSize = projectTo.size; + } + } // if (clip) + } else { + // Don't preserve the aspect ratio. + projectTo.size = targetSize; + } + + projectTo = CGRectIntegral(projectTo); + // There's no CGSizeIntegral, so we fake our own. + CGRect integralRect = CGRectZero; + integralRect.size = targetSize; + targetSize = CGRectIntegral(integralRect).size; + + // iPhone recommended settings. + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + CGBitmapInfo bitmapInfo = (kCGBitmapByteOrder32Little | + kCGImageAlphaPremultipliedFirst); + + CGContextRef context = CGBitmapContextCreate(nil, // data + targetSize.width, + targetSize.height, + 8, // bitsPerPixel + 0, // bytesPerRow + colorSpace, + bitmapInfo); + CGColorSpaceRelease(colorSpace); + // Produce the image + CGContextDrawImage(context, projectTo, [self CGImage]); + CGImageRef resizedRef = CGBitmapContextCreateImage(context); + CGContextRelease(context); + UIImage *resized = [UIImage imageWithCGImage:resizedRef]; + CGImageRelease(resizedRef); + return resized; +} +@end diff --git a/iPhone/GTMUIImage+ResizeTest.m b/iPhone/GTMUIImage+ResizeTest.m new file mode 100644 index 0000000..4678511 --- /dev/null +++ b/iPhone/GTMUIImage+ResizeTest.m @@ -0,0 +1,256 @@ +// +// GTMUIImage+ResizeTest.m +// +// Copyright 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMNSObject+UnitTesting.h" +#import "GTMSenTestCase.h" +#import "GTMUIImage+Resize.h" + +#define GTMUIImageResizeAssertImageEqual(imageObject, imageSuffix) \ + GTMAssertObjectImageEqualToImageNamed(imageObject, \ + @"GTMUIImage+Resize_" imageSuffix,\ + @"Resized image mismatched.") + +@interface GTMUIImage_ResizeTest : SenTestCase +@end + +@implementation GTMUIImage_ResizeTest + +- (void)testNilImage { + UIImage *image = [[UIImage alloc] init]; + UIImage *actual = [image gtm_imageByResizingToSize:CGSizeMake(100, 100) + preserveAspectRatio:YES + trimToFit:NO]; + STAssertNil(actual, @"Invalid inputs should return nil"); +} + +- (void)testInvalidInput { + UIImage *actual; + UIImage *image + = [UIImage imageNamed:@"GTMUIImage+Resize_100x50.png"]; + actual = [image gtm_imageByResizingToSize:CGSizeZero + preserveAspectRatio:YES + trimToFit:NO]; + STAssertNil(actual, @"CGSizeZero resize should be ignored."); + + actual = [image gtm_imageByResizingToSize:CGSizeMake(0.1, 0.1) + preserveAspectRatio:YES + trimToFit:NO]; + STAssertNil(actual, @"Invalid size should be ignored."); + + actual = [image gtm_imageByResizingToSize:CGSizeMake(-100, -100) + preserveAspectRatio:YES + trimToFit:NO]; + STAssertNil(actual, @"Invalid size should be ignored."); +} + +- (void)testImageByResizingWithoutPreservingAspectRatio { + UIImage *actual = nil; + // Square image. + UIImage *originalImage + = [UIImage imageNamed:@"GTMUIImage+Resize_100x100.png"]; + STAssertNotNil(originalImage, @"Unable to read image."); + + // Resize with same aspect ratio. + CGSize size50x50 = CGSizeMake(50, 50); + actual = [originalImage gtm_imageByResizingToSize:size50x50 + preserveAspectRatio:NO + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], size50x50), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size50x50), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x100_to_50x50"); + + // Resize with different aspect ratio + CGSize size60x40 = CGSizeMake(60, 40); + actual = [originalImage gtm_imageByResizingToSize:size60x40 + preserveAspectRatio:NO + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], size60x40), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size60x40), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x100_to_60x40"); + + CGSize size40x60 = CGSizeMake(40, 60); + actual = [originalImage gtm_imageByResizingToSize:size40x60 + preserveAspectRatio:NO + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], size40x60), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size40x60), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x100_to_40x60"); +} + +- (void)testImageByResizingPreservingAspectRatioWithoutClip { + UIImage *actual = nil; + UIImage *landscapeImage = + [UIImage imageNamed:@"GTMUIImage+Resize_100x50.png"]; + STAssertNotNil(landscapeImage, @"Unable to read image."); + + // Landscape resize to 50x50, but clipped to 50x25. + CGSize size50x50 = CGSizeMake(50, 50); + CGSize expected50x25 = CGSizeMake(50, 25); + actual = [landscapeImage gtm_imageByResizingToSize:size50x50 + preserveAspectRatio:YES + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], expected50x25), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(expected50x25), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x50_to_50x50_noclip"); + + // Landscape resize to 60x40, but clipped to 60x30. + CGSize size60x40 = CGSizeMake(60, 40); + CGSize expected60x30 = CGSizeMake(60, 30); + + actual = [landscapeImage gtm_imageByResizingToSize:size60x40 + preserveAspectRatio:YES + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], expected60x30), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(expected60x30), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x50_to_60x40_noclip"); + + // Landscape resize to 40x60, but clipped to 40x20. + CGSize expected40x20 = CGSizeMake(40, 20); + CGSize size40x60 = CGSizeMake(40, 60); + actual = [landscapeImage gtm_imageByResizingToSize:size40x60 + preserveAspectRatio:YES + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], expected40x20), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(expected40x20), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x50_to_40x60_noclip"); + + // Portrait Image + UIImage *portraitImage = + [UIImage imageNamed:@"GTMUIImage+Resize_50x100.png"]; + + // Portrait resize to 50x50, but clipped to 25x50. + CGSize expected25x50 = CGSizeMake(25, 50); + actual = [portraitImage gtm_imageByResizingToSize:size50x50 + preserveAspectRatio:YES + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], expected25x50), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(expected25x50), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"50x100_to_50x50_noclip"); + + // Portrait resize to 60x40, but clipped to 20x40. + CGSize expected20x40 = CGSizeMake(20, 40); + actual = [portraitImage gtm_imageByResizingToSize:size60x40 + preserveAspectRatio:YES + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], expected20x40), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(expected20x40), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"50x100_to_60x40_noclip"); + + // Portrait resize to 40x60, but clipped to 30x60. + CGSize expected30x60 = CGSizeMake(30, 60); + actual = [portraitImage gtm_imageByResizingToSize:size40x60 + preserveAspectRatio:YES + trimToFit:NO]; + STAssertTrue(CGSizeEqualToSize([actual size], expected30x60), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(expected30x60), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"50x100_to_40x60_noclip"); +} + +- (void)testImageByResizingPreservingAspectRatioWithClip { + UIImage *actual = nil; + UIImage *landscapeImage = + [UIImage imageNamed:@"GTMUIImage+Resize_100x50.png"]; + STAssertNotNil(landscapeImage, @"Unable to read image."); + + // Landscape resize to 50x50 + CGSize size50x50 = CGSizeMake(50, 50); + actual = [landscapeImage gtm_imageByResizingToSize:size50x50 + preserveAspectRatio:YES + trimToFit:YES]; + STAssertTrue(CGSizeEqualToSize([actual size], size50x50), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size50x50), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x50_to_50x50_clip"); + + // Landscape resize to 60x40 + CGSize size60x40 = CGSizeMake(60, 40); + actual = [landscapeImage gtm_imageByResizingToSize:size60x40 + preserveAspectRatio:YES + trimToFit:YES]; + STAssertTrue(CGSizeEqualToSize([actual size], size60x40), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size60x40), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x50_to_60x40_clip"); + + // Landscape resize to 40x60 + CGSize size40x60 = CGSizeMake(40, 60); + actual = [landscapeImage gtm_imageByResizingToSize:size40x60 + preserveAspectRatio:YES + trimToFit:YES]; + STAssertTrue(CGSizeEqualToSize([actual size], size40x60), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size40x60), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"100x50_to_40x60_clip"); + + // Portrait Image. + UIImage *portraitImage = + [UIImage imageNamed:@"GTMUIImage+Resize_50x100.png"]; + + // Portrait resize to 50x50 + actual = [portraitImage gtm_imageByResizingToSize:size50x50 + preserveAspectRatio:YES + trimToFit:YES]; + STAssertTrue(CGSizeEqualToSize([actual size], size50x50), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size50x50), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"50x100_to_50x50_clip"); + + // Portrait resize to 60x40 + actual = [portraitImage gtm_imageByResizingToSize:size60x40 + preserveAspectRatio:YES + trimToFit:YES]; + STAssertTrue(CGSizeEqualToSize([actual size], size60x40), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size60x40), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"50x100_to_60x40_clip"); + + // Portrait resize to 40x60. + actual = [portraitImage gtm_imageByResizingToSize:size40x60 + preserveAspectRatio:YES + trimToFit:YES]; + STAssertTrue(CGSizeEqualToSize([actual size], size40x60), + @"Resized image should equal size: %@ actual: %@", + NSStringFromCGSize(size40x60), + NSStringFromCGSize([actual size])); + GTMUIImageResizeAssertImageEqual(actual, @"50x100_to_40x60_clip"); +} + +@end diff --git a/iPhone/TestData/GTMUIImage+Resize_100x100.png b/iPhone/TestData/GTMUIImage+Resize_100x100.png Binary files differnew file mode 100644 index 0000000..93a4a38 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x100.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x100_to_40x60.png b/iPhone/TestData/GTMUIImage+Resize_100x100_to_40x60.png Binary files differnew file mode 100644 index 0000000..10223fd --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x100_to_40x60.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x100_to_50x50.png b/iPhone/TestData/GTMUIImage+Resize_100x100_to_50x50.png Binary files differnew file mode 100644 index 0000000..ab71afb --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x100_to_50x50.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x100_to_60x40.png b/iPhone/TestData/GTMUIImage+Resize_100x100_to_60x40.png Binary files differnew file mode 100644 index 0000000..5683f17 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x100_to_60x40.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x50.png b/iPhone/TestData/GTMUIImage+Resize_100x50.png Binary files differnew file mode 100644 index 0000000..8681e02 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x50.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x50_to_40x60_clip.png b/iPhone/TestData/GTMUIImage+Resize_100x50_to_40x60_clip.png Binary files differnew file mode 100644 index 0000000..b7721ad --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x50_to_40x60_clip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x50_to_40x60_noclip.png b/iPhone/TestData/GTMUIImage+Resize_100x50_to_40x60_noclip.png Binary files differnew file mode 100644 index 0000000..0e2c9de --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x50_to_40x60_noclip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x50_to_50x50_clip.png b/iPhone/TestData/GTMUIImage+Resize_100x50_to_50x50_clip.png Binary files differnew file mode 100644 index 0000000..e913bfb --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x50_to_50x50_clip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x50_to_50x50_noclip.png b/iPhone/TestData/GTMUIImage+Resize_100x50_to_50x50_noclip.png Binary files differnew file mode 100644 index 0000000..365dfb6 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x50_to_50x50_noclip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x50_to_60x40_clip.png b/iPhone/TestData/GTMUIImage+Resize_100x50_to_60x40_clip.png Binary files differnew file mode 100644 index 0000000..b8e6c06 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x50_to_60x40_clip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_100x50_to_60x40_noclip.png b/iPhone/TestData/GTMUIImage+Resize_100x50_to_60x40_noclip.png Binary files differnew file mode 100644 index 0000000..14d96b0 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_100x50_to_60x40_noclip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_50x100.png b/iPhone/TestData/GTMUIImage+Resize_50x100.png Binary files differnew file mode 100644 index 0000000..adf8c22 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_50x100.png diff --git a/iPhone/TestData/GTMUIImage+Resize_50x100_to_40x60_clip.png b/iPhone/TestData/GTMUIImage+Resize_50x100_to_40x60_clip.png Binary files differnew file mode 100644 index 0000000..3c68940 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_50x100_to_40x60_clip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_50x100_to_40x60_noclip.png b/iPhone/TestData/GTMUIImage+Resize_50x100_to_40x60_noclip.png Binary files differnew file mode 100644 index 0000000..8a9b227 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_50x100_to_40x60_noclip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_50x100_to_50x50_clip.png b/iPhone/TestData/GTMUIImage+Resize_50x100_to_50x50_clip.png Binary files differnew file mode 100644 index 0000000..6d6a5df --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_50x100_to_50x50_clip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_50x100_to_50x50_noclip.png b/iPhone/TestData/GTMUIImage+Resize_50x100_to_50x50_noclip.png Binary files differnew file mode 100644 index 0000000..c79873a --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_50x100_to_50x50_noclip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_50x100_to_60x40_clip.png b/iPhone/TestData/GTMUIImage+Resize_50x100_to_60x40_clip.png Binary files differnew file mode 100644 index 0000000..87feb9b --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_50x100_to_60x40_clip.png diff --git a/iPhone/TestData/GTMUIImage+Resize_50x100_to_60x40_noclip.png b/iPhone/TestData/GTMUIImage+Resize_50x100_to_60x40_noclip.png Binary files differnew file mode 100644 index 0000000..be94dc6 --- /dev/null +++ b/iPhone/TestData/GTMUIImage+Resize_50x100_to_60x40_noclip.png |