From 9379797434208ef48c9a511a3d748b2852ae97f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Sun, 23 Jul 2023 18:46:47 +0200 Subject: [PATCH 1/4] refactor(ios): use latest SDK, remove outdated files, fix all warnings --- ios/Classes/TiImagefactoryModule.m | 25 +-- ios/Classes/TiImagefactoryModuleAssets.m | 4 +- ios/Classes/UIImage/TiUIImage+Resize.m | 27 ++- ios/LICENSE | 219 --------------------- ios/README | 151 -------------- ios/build.py | 200 ------------------- ios/hooks/README | 1 - ios/hooks/add.py | 35 ---- ios/hooks/install.py | 19 -- ios/hooks/remove.py | 34 ---- ios/hooks/uninstall.py | 18 -- ios/imagefactory.xcodeproj/project.pbxproj | 46 +++-- ios/manifest | 7 +- ios/platform/README | 3 - ios/titanium.xcconfig | 2 +- 15 files changed, 69 insertions(+), 722 deletions(-) delete mode 100644 ios/LICENSE delete mode 100644 ios/README delete mode 100755 ios/build.py delete mode 100644 ios/hooks/README delete mode 100644 ios/hooks/add.py delete mode 100644 ios/hooks/install.py delete mode 100644 ios/hooks/remove.py delete mode 100644 ios/hooks/uninstall.py delete mode 100644 ios/platform/README diff --git a/ios/Classes/TiImagefactoryModule.m b/ios/Classes/TiImagefactoryModule.m index 9584310..c81cd3f 100644 --- a/ios/Classes/TiImagefactoryModule.m +++ b/ios/Classes/TiImagefactoryModule.m @@ -15,30 +15,17 @@ @implementation TiImagefactoryModule #pragma mark Internal -// this is generated for your module, please do not change it - (id)moduleGUID { return @"0aab25d7-0486-40ab-94a3-ed4f9a293414"; } -// this is generated for your module, please do not change it - (NSString *)moduleId { return @"ti.imagefactory"; } -#pragma mark Lifecycle - -- (void)startup -{ - // this method is called when the module is first loaded - // you *must* call the superclass - [super startup]; - - NSLog(@"[DEBUG] %@ loaded", self); -} - -#pragma system properties +#pragma System Properties typedef enum { kTransformNone = 0, @@ -184,8 +171,8 @@ - (id)imageTransform:(id)args - (id)compress:(id)args { - TiBlob *blob; - NSNumber *qualityObject; + TiBlob *blob = nil; + NSNumber *qualityObject = @-1; ENSURE_ARG_AT_INDEX(blob, args, 0, TiBlob); ENSURE_ARG_AT_INDEX(qualityObject, args, 1, NSNumber); @@ -199,9 +186,9 @@ - (id)compress:(id)args - (id)compressToFile:(id)args { // Fetch arguments. - TiBlob *blob; - NSNumber *qualityObject; - NSString *filePath; + TiBlob *blob = nil;; + NSNumber *qualityObject = @-1; + NSString *filePath = nil; ENSURE_ARG_AT_INDEX(blob, args, 0, TiBlob); ENSURE_ARG_AT_INDEX(qualityObject, args, 1, NSNumber); ENSURE_ARG_AT_INDEX(filePath, args, 2, NSString); diff --git a/ios/Classes/TiImagefactoryModuleAssets.m b/ios/Classes/TiImagefactoryModuleAssets.m index 4f432b8..f8f6308 100644 --- a/ios/Classes/TiImagefactoryModuleAssets.m +++ b/ios/Classes/TiImagefactoryModuleAssets.m @@ -3,18 +3,20 @@ */ #import "TiImagefactoryModuleAssets.h" -extern NSData *filterDataInRange(NSData *thedata, NSRange range); +extern NSData* filterDataInRange(NSData* thedata, NSRange range); @implementation TiImagefactoryModuleAssets - (NSData *)moduleAsset { + return nil; } - (NSData *)resolveModuleAsset:(NSString *)path { + return nil; } diff --git a/ios/Classes/UIImage/TiUIImage+Resize.m b/ios/Classes/UIImage/TiUIImage+Resize.m index f7bedae..607aca0 100644 --- a/ios/Classes/UIImage/TiUIImage+Resize.m +++ b/ios/Classes/UIImage/TiUIImage+Resize.m @@ -32,10 +32,15 @@ + (UIImage *)resizedImage:(CGSize)newSize #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 if ([TiUIImageResize isIOS4OrGreater]) { scale = [image scale]; - // Force scaling to 2.0 - if ([TiUtils isRetinaDisplay] && hires) { - scale = 2.0; - } + + // Force scaling to 2x/3x + if (hires) { + if ([TiUtils is2xRetina]) { + scale = 2.0; + }else if ([TiUtils is3xRetina]) { + scale = 3.0; + } + } } #endif @@ -101,10 +106,22 @@ + (CGAffineTransform)transformForOrientation:(CGSize)newSize image:(UIImage*)ima transform = CGAffineTransformTranslate(transform, 0, newSize.height); transform = CGAffineTransformRotate(transform, -M_PI_2); break; + case UIImageOrientationUp: + break; + case UIImageOrientationUpMirrored: + break; } switch (image.imageOrientation) { - case UIImageOrientationUpMirrored: // EXIF = 2 + case UIImageOrientationUp: + break; + case UIImageOrientationDown: + break; + case UIImageOrientationLeft: + break; + case UIImageOrientationRight: + break; + case UIImageOrientationUpMirrored: // EXIF = 2 case UIImageOrientationDownMirrored: // EXIF = 4 transform = CGAffineTransformTranslate(transform, newSize.width, 0); transform = CGAffineTransformScale(transform, -1, 1); diff --git a/ios/LICENSE b/ios/LICENSE deleted file mode 100644 index f52ddb7..0000000 --- a/ios/LICENSE +++ /dev/null @@ -1,219 +0,0 @@ -Copyright 2013 Appcelerator, 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 - - (or the full text of the license is below) - - 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. - - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -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. \ No newline at end of file diff --git a/ios/README b/ios/README deleted file mode 100644 index 34a06e4..0000000 --- a/ios/README +++ /dev/null @@ -1,151 +0,0 @@ -Appcelerator Titanium iPhone Module Project -=========================================== - -This is a skeleton Titanium Mobile iPhone module project. Modules can be -used to extend the functionality of Titanium by providing additional native -code that is compiled into your application at build time and can expose certain -APIs into JavaScript. - -MODULE NAMING --------------- - -Choose a unique module id for your module. This ID usually follows a namespace -convention using DNS notation. For example, com.appcelerator.module.test. This -ID can only be used once by all public modules in Titanium. - - -COMPONENTS ------------ - -Components that are exposed by your module must follow a special naming convention. -A component (widget, proxy, etc) must be named with the pattern: - - TiProxy - -For example, if you component was called Foo, your proxy would be named: - - TiMyfirstFooProxy - -For view proxies or widgets, you must create both a view proxy and a view implementation. -If you widget was named proxy, you would create the following files: - - TiMyfirstFooProxy.h - TiMyfirstFooProxy.m - TiMyfirstFoo.h - TiMyfirstFoo.m - -The view implementation is named the same except it does contain the suffix `Proxy`. - -View implementations extend the Titanium base class `TiUIView`. View Proxies extend the -Titanium base class `TiUIViewProxy` or `TiUIWidgetProxy`. - -For proxies that are simply native objects that can be returned to JavaScript, you can -simply extend `TiProxy` and no view implementation is required. - - -GET STARTED ------------- - -1. Edit manifest with the appropriate details about your module. -2. Edit LICENSE to add your license details. -3. Place any assets (such as PNG files) that are required in the assets folder. -4. Edit the titanium.xcconfig and make sure you're building for the right Titanium version. -5. Code and build. - -BUILD TIME COMPILER CONFIG --------------------------- - -You can edit the file `module.xcconfig` to include any build time settings that should be -set during application compilation that your module requires. This file will automatically get `#include` in the main application project. - -For more information about this file, please see the Apple documentation at: - - - - -DOCUMENTATION FOR YOUR MODULE ------------------------------ - -You should provide at least minimal documentation for your module in `documentation` folder using the Markdown syntax. - -For more information on the Markdown syntax, refer to this documentation at: - - - - -TEST HARNESS EXAMPLE FOR YOUR MODULE ------------------------------------- - -The `example` directory contains a skeleton application test harness that can be -used for testing and providing an example of usage to the users of your module. - - -INSTALL YOUR MODULE --------------------- - -1. Run `build.py` which creates your distribution -2. cd to `/Library/Application Support/Titanium` -3. copy this zip file into the folder of your Titanium SDK - -REGISTER YOUR MODULE ---------------------- - -Register your module with your application by editing `tiapp.xml` and adding your module. -Example: - - - ti.imagefactory - - -When you run your project, the compiler will know automatically compile in your module -dependencies and copy appropriate image assets into the application. - -USING YOUR MODULE IN CODE -------------------------- - -To use your module in code, you will need to require it. - -For example, - - var my_module = require('ti.imagefactory'); - my_module.foo(); - -WRITING PURE JS NATIVE MODULES ------------------------------- - -You can write a pure JavaScript "natively compiled" module. This is nice if you -want to distribute a JS module pre-compiled. - -To create a module, create a file named ti.imagefactory.js under the assets folder. -This file must be in the Common JS format. For example: - - exports.echo = function(s) - { - return s; - }; - -Any functions and properties that are exported will be made available as part of your -module. All other code inside your JS will be private to your module. - -For pure JS module, you don't need to modify any of the Objective-C module code. You -can leave it as-is and build. - -TESTING YOUR MODULE -------------------- - -Run the `titanium.py` script to test your module or test from within XCode. -To test with the script, execute: - - titanium run --dir=YOURMODULEDIR - - -This will execute the app.js in the example folder as a Titanium application. - - -DISTRIBUTING YOUR MODULE -------------------------- - -Currently, you will need to manually distribution your module distribution zip file directly. However, in the near future, we will make module distribution and sharing built-in to Titanium Developer and in the Titanium Marketplace! - - -Cheers! diff --git a/ios/build.py b/ios/build.py deleted file mode 100755 index 8a3f372..0000000 --- a/ios/build.py +++ /dev/null @@ -1,200 +0,0 @@ -#!/usr/bin/env python -# -# Appcelerator Titanium Module Packager -# -# -import os, sys, glob, string -import zipfile -from datetime import date - -cwd = os.path.abspath(os.path.dirname(sys._getframe(0).f_code.co_filename)) -os.chdir(cwd) -required_module_keys = ['name','version','moduleid','description','copyright','license','copyright','platform','minsdk'] -module_defaults = { - 'description':'My module', - 'author': 'Your Name', - 'license' : 'Specify your license', - 'copyright' : 'Copyright (c) %s by Your Company' % str(date.today().year), -} -module_license_default = "TODO: place your license here and we'll include it in the module distribution" - -def replace_vars(config,token): - idx = token.find('$(') - while idx != -1: - idx2 = token.find(')',idx+2) - if idx2 == -1: break - key = token[idx+2:idx2] - if not config.has_key(key): break - token = token.replace('$(%s)' % key, config[key]) - idx = token.find('$(') - return token - - -def read_ti_xcconfig(): - contents = open(os.path.join(cwd,'titanium.xcconfig')).read() - config = {} - for line in contents.splitlines(False): - line = line.strip() - if line[0:2]=='//': continue - idx = line.find('=') - if idx > 0: - key = line[0:idx].strip() - value = line[idx+1:].strip() - config[key] = replace_vars(config,value) - return config - -def generate_doc(config): - docdir = os.path.join(cwd,'documentation') - if not os.path.exists(docdir): - print "Couldn't find documentation file at: %s" % docdir - return None - sdk = config['TITANIUM_SDK'] - support_dir = os.path.join(sdk,'module','support') - sys.path.append(support_dir) - try: - import markdown2 as markdown - except ImportError: - import markdown - documentation = [] - for file in os.listdir(docdir): - if file in ignoreFiles or os.path.isdir(os.path.join(docdir, file)): - continue - md = open(os.path.join(docdir,file)).read() - html = markdown.markdown(md) - documentation.append({file:html}); - return documentation - -def compile_js(manifest,config): - js_file = os.path.join(cwd,'assets','ti.imagefactory.js') - if not os.path.exists(js_file): return - - sdk = config['TITANIUM_SDK'] - iphone_dir = os.path.join(sdk,'iphone') - sys.path.insert(0,iphone_dir) - from compiler import Compiler - - path = os.path.basename(js_file) - metadata = Compiler.make_function_from_file(path,js_file) - method = metadata['method'] - eq = path.replace('.','_') - method = ' return %s;' % method - - f = os.path.join(cwd,'Classes','TiImagefactoryModuleAssets.m') - c = open(f).read() - idx = c.find('return ') - before = c[0:idx] - after = """ -} - -@end - """ - newc = before + method + after - - if newc!=c: - x = open(f,'w') - x.write(newc) - x.close() - -def die(msg): - print msg - sys.exit(1) - -def warn(msg): - print "[WARN] %s" % msg - -def validate_license(): - c = open(os.path.join(cwd,'LICENSE')).read() - if c.find(module_license_default)!=1: - warn('please update the LICENSE file with your license text before distributing') - -def validate_manifest(): - path = os.path.join(cwd,'manifest') - f = open(path) - if not os.path.exists(path): die("missing %s" % path) - manifest = {} - for line in f.readlines(): - line = line.strip() - if line[0:1]=='#': continue - if line.find(':') < 0: continue - key,value = line.split(':') - manifest[key.strip()]=value.strip() - for key in required_module_keys: - if not manifest.has_key(key): die("missing required manifest key '%s'" % key) - if module_defaults.has_key(key): - defvalue = module_defaults[key] - curvalue = manifest[key] - if curvalue==defvalue: warn("please update the manifest key: '%s' to a non-default value" % key) - return manifest,path - -ignoreFiles = ['.DS_Store','.gitignore','libTitanium.a','titanium.jar','README','ti.imagefactory.js'] -ignoreDirs = ['.DS_Store','.svn','.git','CVSROOT'] - -def zip_dir(zf,dir,basepath,ignore=[]): - for root, dirs, files in os.walk(dir): - for name in ignoreDirs: - if name in dirs: - dirs.remove(name) # don't visit ignored directories - for file in files: - if file in ignoreFiles: continue - e = os.path.splitext(file) - if len(e)==2 and e[1]=='.pyc':continue - from_ = os.path.join(root, file) - to_ = from_.replace(dir, basepath, 1) - zf.write(from_, to_) - -def glob_libfiles(): - files = [] - for libfile in glob.glob('build/**/*.a'): - if libfile.find('Release-')!=-1: - files.append(libfile) - return files - -def build_module(manifest,config): - rc = os.system("xcodebuild -sdk iphoneos -configuration Release") - if rc != 0: - die("xcodebuild failed") - rc = os.system("xcodebuild -sdk iphonesimulator -configuration Release") - if rc != 0: - die("xcodebuild failed") - # build the merged library using lipo - moduleid = manifest['moduleid'] - libpaths = '' - for libfile in glob_libfiles(): - libpaths+='%s ' % libfile - - os.system("lipo %s -create -output build/lib%s.a" %(libpaths,moduleid)) - -def package_module(manifest,mf,config): - name = manifest['name'].lower() - moduleid = manifest['moduleid'].lower() - version = manifest['version'] - modulezip = '%s-iphone-%s.zip' % (moduleid,version) - if os.path.exists(modulezip): os.remove(modulezip) - zf = zipfile.ZipFile(modulezip, 'w', zipfile.ZIP_DEFLATED) - modulepath = 'modules/iphone/%s/%s' % (moduleid,version) - zf.write(mf,'%s/manifest' % modulepath) - libname = 'lib%s.a' % moduleid - zf.write('build/%s' % libname, '%s/%s' % (modulepath,libname)) - docs = generate_doc(config) - if docs!=None: - for doc in docs: - for file, html in doc.iteritems(): - filename = string.replace(file,'.md','.html') - zf.writestr('%s/documentation/%s'%(modulepath,filename),html) - for dn in ('assets','example','platform'): - if os.path.exists(dn): - zip_dir(zf,dn,'%s/%s' % (modulepath,dn),['README']) - zf.write('LICENSE','%s/LICENSE' % modulepath) - zf.write('module.xcconfig','%s/module.xcconfig' % modulepath) - zf.close() - - -if __name__ == '__main__': - manifest,mf = validate_manifest() - validate_license() - config = read_ti_xcconfig() - compile_js(manifest,config) - build_module(manifest,config) - package_module(manifest,mf,config) - sys.exit(0) - diff --git a/ios/hooks/README b/ios/hooks/README deleted file mode 100644 index 66b10a8..0000000 --- a/ios/hooks/README +++ /dev/null @@ -1 +0,0 @@ -These files are not yet supported as of 1.4.0 but will be in a near future release. diff --git a/ios/hooks/add.py b/ios/hooks/add.py deleted file mode 100644 index 04e1c1d..0000000 --- a/ios/hooks/add.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python -# -# This is the module project add hook that will be -# called when your module is added to a project -# -import os, sys - -def dequote(s): - if s[0:1] == '"': - return s[1:-1] - return s - -def main(args,argc): - # You will get the following command line arguments - # in the following order: - # - # project_dir = the full path to the project root directory - # project_type = the type of project (desktop, mobile, ipad) - # project_name = the name of the project - # - project_dir = dequote(os.path.expanduser(args[1])) - project_type = dequote(args[2]) - project_name = dequote(args[3]) - - # TODO: write your add hook here (optional) - - - # exit - sys.exit(0) - - - -if __name__ == '__main__': - main(sys.argv,len(sys.argv)) - diff --git a/ios/hooks/install.py b/ios/hooks/install.py deleted file mode 100644 index b423fe9..0000000 --- a/ios/hooks/install.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python -# -# This is the module install hook that will be -# called when your module is first installed -# -import os, sys - -def main(args,argc): - - # TODO: write your install hook here (optional) - - # exit - sys.exit(0) - - - -if __name__ == '__main__': - main(sys.argv,len(sys.argv)) - diff --git a/ios/hooks/remove.py b/ios/hooks/remove.py deleted file mode 100644 index f92a234..0000000 --- a/ios/hooks/remove.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -# This is the module project remove hook that will be -# called when your module is remove from a project -# -import os, sys - -def dequote(s): - if s[0:1] == '"': - return s[1:-1] - return s - -def main(args,argc): - # You will get the following command line arguments - # in the following order: - # - # project_dir = the full path to the project root directory - # project_type = the type of project (desktop, mobile, ipad) - # project_name = the name of the project - # - project_dir = dequote(os.path.expanduser(args[1])) - project_type = dequote(args[2]) - project_name = dequote(args[3]) - - # TODO: write your remove hook here (optional) - - # exit - sys.exit(0) - - - -if __name__ == '__main__': - main(sys.argv,len(sys.argv)) - diff --git a/ios/hooks/uninstall.py b/ios/hooks/uninstall.py deleted file mode 100644 index a7ffd91..0000000 --- a/ios/hooks/uninstall.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python -# -# This is the module uninstall hook that will be -# called when your module is uninstalled -# -import os, sys - -def main(args,argc): - - # TODO: write your uninstall hook here (optional) - - # exit - sys.exit(0) - - -if __name__ == '__main__': - main(sys.argv,len(sys.argv)) - diff --git a/ios/imagefactory.xcodeproj/project.pbxproj b/ios/imagefactory.xcodeproj/project.pbxproj index cafd73b..1f77590 100644 --- a/ios/imagefactory.xcodeproj/project.pbxproj +++ b/ios/imagefactory.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 53; objects = { /* Begin PBXAggregateTarget section */ @@ -193,17 +193,16 @@ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0830; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1430; }; buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "imagefactory" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 1; knownRegions = ( - English, - Japanese, - French, - German, + en, + Base, ); mainGroup = 0867D691FE84028FC02AAC07 /* imagefactory */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; @@ -264,6 +263,7 @@ buildSettings = { "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)"; "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)"; + CLANG_ENABLE_OBJC_WEAK = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DSTROOT = /tmp/TiImagefactory.dst; @@ -284,7 +284,7 @@ GCC_WARN_UNUSED_VALUE = NO; GCC_WARN_UNUSED_VARIABLE = NO; INSTALL_PATH = /usr/local/lib; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; OTHER_CFLAGS = ( "-DDEBUG", "-DTI_POST_1_2", @@ -308,6 +308,7 @@ ALWAYS_SEARCH_USER_PATHS = NO; "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)"; "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)"; + CLANG_ENABLE_OBJC_WEAK = YES; DSTROOT = /tmp/TiImagefactory.dst; GCC_C_LANGUAGE_STANDARD = c99; GCC_MODEL_TUNING = G5; @@ -326,8 +327,7 @@ GCC_WARN_UNUSED_VALUE = NO; GCC_WARN_UNUSED_VARIABLE = NO; INSTALL_PATH = /usr/local/lib; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - "IPHONEOS_DEPLOYMENT_TARGET[sdk=iphoneos*]" = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; OTHER_CFLAGS = "-DTI_POST_1_2"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = TiImagefactory; @@ -348,12 +348,21 @@ ARCHS = "$(ARCHS_STANDARD)"; "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)"; "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -361,6 +370,7 @@ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; DSTROOT = /tmp/TiImagefactory.dst; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = c99; GCC_NO_COMMON_BLOCKS = YES; GCC_OPTIMIZATION_LEVEL = 0; @@ -382,7 +392,7 @@ GCC_WARN_UNUSED_VALUE = NO; GCC_WARN_UNUSED_VARIABLE = NO; INSTALL_PATH = /usr/local/lib; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = ( "-DDEBUG", @@ -408,12 +418,21 @@ ARCHS = "$(ARCHS_STANDARD)"; "ARCHS[sdk=iphoneos*]" = "$(ARCHS_STANDARD)"; "ARCHS[sdk=iphonesimulator*]" = "$(ARCHS_STANDARD)"; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -440,7 +459,7 @@ GCC_WARN_UNUSED_VALUE = NO; GCC_WARN_UNUSED_VARIABLE = NO; INSTALL_PATH = /usr/local/lib; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; OTHER_CFLAGS = "-DTI_POST_1_2"; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = TiImagefactory; @@ -458,6 +477,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -470,9 +490,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 24DD6D1B1134B66800162E58 /* titanium.xcconfig */; buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; COPY_PHASE_STRIP = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_THUMB_SUPPORT = NO; PRODUCT_NAME = "Build & test"; ZERO_LINK = NO; diff --git a/ios/manifest b/ios/manifest index 50be950..bcadb78 100644 --- a/ios/manifest +++ b/ios/manifest @@ -2,13 +2,13 @@ # this is your module manifest and used by Titanium # during compilation, packaging, distribution, etc. # -version: 2.1.0 +version: 3.0.0 apiversion: 2 -architectures: armv7 i386 x86_64 arm64 +architectures: x86_64 arm64 description: Image Factory author: Jeff English license: Apache License, Version 2.0 -copyright: Copyright (c) 2013-2021 by Appcelerator, Inc. +copyright: Copyright (c) 2013-2022 by Appcelerator, Inc., 2022-present TiDev, Inc. # these should not be edited name: imagefactory @@ -16,3 +16,4 @@ moduleid: ti.imagefactory guid: 0aab25d7-0486-40ab-94a3-ed4f9a293414 platform: iphone minsdk: 9.2.0 +mac: false \ No newline at end of file diff --git a/ios/platform/README b/ios/platform/README deleted file mode 100644 index 7ac991c..0000000 --- a/ios/platform/README +++ /dev/null @@ -1,3 +0,0 @@ -You can place platform-specific files here in sub-folders named "android" and/or "iphone", just as you can with normal Titanium Mobile SDK projects. Any folders and files you place here will be merged with the platform-specific files in a Titanium Mobile project that uses this module. - -When a Titanium Mobile project that uses this module is built, the files from this platform/ folder will be treated the same as files (if any) from the Titanium Mobile project's platform/ folder. diff --git a/ios/titanium.xcconfig b/ios/titanium.xcconfig index 258993f..58f68b2 100644 --- a/ios/titanium.xcconfig +++ b/ios/titanium.xcconfig @@ -4,7 +4,7 @@ // OF YOUR TITANIUM SDK YOU'RE BUILDING FOR // // -TITANIUM_SDK_VERSION = 9.3.2.GA +TITANIUM_SDK_VERSION = 12.0.0.GA // From f0d970c4db1d71034adc0d0cd068093ced0b7646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Sun, 23 Jul 2023 18:54:24 +0200 Subject: [PATCH 2/4] feat(ios): perform compression on the background thread --- ios/Classes/TiImagefactoryModule.m | 35 +++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/ios/Classes/TiImagefactoryModule.m b/ios/Classes/TiImagefactoryModule.m index c81cd3f..802be7b 100644 --- a/ios/Classes/TiImagefactoryModule.m +++ b/ios/Classes/TiImagefactoryModule.m @@ -171,16 +171,35 @@ - (id)imageTransform:(id)args - (id)compress:(id)args { - TiBlob *blob = nil; - NSNumber *qualityObject = @-1; - ENSURE_ARG_AT_INDEX(blob, args, 0, TiBlob); - ENSURE_ARG_AT_INDEX(qualityObject, args, 1, NSNumber); + if (![args[0] isKindOfClass:[NSDictionary class]]) { + DEPRECATED_REPLACED(@"ImageFactory.compress(file, quality)", @"3.0.0",@"ImageFactory.compress({ file, quality, callback })"); - UIImage *image = [blob image]; - image = [TiImageFactory imageUpright:image]; + TiBlob *blob = nil; + NSNumber *qualityObject = @-1; + ENSURE_ARG_AT_INDEX(blob, args, 0, TiBlob); + ENSURE_ARG_AT_INDEX(qualityObject, args, 1, NSNumber); + + UIImage *image = [blob image]; + image = [TiImageFactory imageUpright:image]; - float qualityValue = [TiUtils floatValue:qualityObject def:1.0]; - return [[[TiBlob alloc] initWithData:UIImageJPEGRepresentation(image, qualityValue) mimetype:@"image/jpeg"] autorelease]; + float qualityValue = [TiUtils floatValue:qualityObject def:1.0]; + return [[[TiBlob alloc] initWithData:UIImageJPEGRepresentation(image, qualityValue) mimetype:@"image/jpeg"] autorelease]; + } + + ENSURE_SINGLE_ARG(args, NSDictionary); + + dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ + TiBlob *blob = args[@"file"]; + float quality = [TiUtils floatValue:@"quality" properties:args def:1.0]; + KrollCallback *callback = args[@"callback"]; + + UIImage *image = [TiImageFactory imageUpright:[blob image]]; + TiBlob *result = [[[TiBlob alloc] initWithData:UIImageJPEGRepresentation(image, quality) mimetype:@"image/jpeg"] autorelease]; + + TiThreadPerformOnMainThread(^{ + [callback call:@[@{ @"file": result }] thisObject:self]; + }, NO); + }); } - (id)compressToFile:(id)args From ff64e858b1f517fcbdd6d738a53f030559c89535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Sun, 23 Jul 2023 18:56:59 +0200 Subject: [PATCH 3/4] chore: format --- ios/Classes/TiImagefactoryModule.m | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/ios/Classes/TiImagefactoryModule.m b/ios/Classes/TiImagefactoryModule.m index 802be7b..b02b3ca 100644 --- a/ios/Classes/TiImagefactoryModule.m +++ b/ios/Classes/TiImagefactoryModule.m @@ -172,7 +172,7 @@ - (id)imageTransform:(id)args - (id)compress:(id)args { if (![args[0] isKindOfClass:[NSDictionary class]]) { - DEPRECATED_REPLACED(@"ImageFactory.compress(file, quality)", @"3.0.0",@"ImageFactory.compress({ file, quality, callback })"); + DEPRECATED_REPLACED(@"ImageFactory.compress(file, quality)", @"3.0.0", @"ImageFactory.compress({ file, quality, callback })"); TiBlob *blob = nil; NSNumber *qualityObject = @-1; @@ -185,7 +185,7 @@ - (id)compress:(id)args float qualityValue = [TiUtils floatValue:qualityObject def:1.0]; return [[[TiBlob alloc] initWithData:UIImageJPEGRepresentation(image, qualityValue) mimetype:@"image/jpeg"] autorelease]; } - + ENSURE_SINGLE_ARG(args, NSDictionary); dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), ^{ @@ -195,17 +195,19 @@ - (id)compress:(id)args UIImage *image = [TiImageFactory imageUpright:[blob image]]; TiBlob *result = [[[TiBlob alloc] initWithData:UIImageJPEGRepresentation(image, quality) mimetype:@"image/jpeg"] autorelease]; - - TiThreadPerformOnMainThread(^{ - [callback call:@[@{ @"file": result }] thisObject:self]; - }, NO); + + TiThreadPerformOnMainThread( + ^{ + [callback call:@[ @{ @"file" : result } ] thisObject:self]; + }, + NO); }); } - (id)compressToFile:(id)args { // Fetch arguments. - TiBlob *blob = nil;; + TiBlob *blob = nil; NSNumber *qualityObject = @-1; NSString *filePath = nil; ENSURE_ARG_AT_INDEX(blob, args, 0, TiBlob); From fcdc3957fba8104f6c1d0620ef26dd8978940a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans=20Kn=C3=B6chel?= Date: Sun, 23 Jul 2023 18:57:35 +0200 Subject: [PATCH 4/4] chore: format all files --- ios/Classes/UIImage/TiUIImage+Alpha.m | 198 +++++----- ios/Classes/UIImage/TiUIImage+Resize.m | 353 +++++++++--------- ios/Classes/UIImage/TiUIImage+Rotate.m | 51 ++- ios/Classes/UIImage/TiUIImage+RoundedCorner.m | 101 +++-- 4 files changed, 349 insertions(+), 354 deletions(-) diff --git a/ios/Classes/UIImage/TiUIImage+Alpha.m b/ios/Classes/UIImage/TiUIImage+Alpha.m index b84f183..23b4f31 100644 --- a/ios/Classes/UIImage/TiUIImage+Alpha.m +++ b/ios/Classes/UIImage/TiUIImage+Alpha.m @@ -8,121 +8,119 @@ @implementation TiUIImageAlpha // Returns true if the image has an alpha layer -+ (BOOL)hasAlpha:(UIImage*)image { - CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image.CGImage); - return (alpha == kCGImageAlphaFirst || - alpha == kCGImageAlphaLast || - alpha == kCGImageAlphaPremultipliedFirst || - alpha == kCGImageAlphaPremultipliedLast); ++ (BOOL)hasAlpha:(UIImage *)image +{ + CGImageAlphaInfo alpha = CGImageGetAlphaInfo(image.CGImage); + return (alpha == kCGImageAlphaFirst || alpha == kCGImageAlphaLast || alpha == kCGImageAlphaPremultipliedFirst || alpha == kCGImageAlphaPremultipliedLast); } // Returns a copy of the given image, adding an alpha channel if it doesn't already have one -+ (UIImage *)imageWithAlpha:(UIImage*)image { - if ([TiUIImageAlpha hasAlpha:image]) { - return image; - } - - CGImageRef imageRef = image.CGImage; - size_t width = CGImageGetWidth(imageRef); - size_t height = CGImageGetHeight(imageRef); - - // The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error - CGContextRef offscreenContext = CGBitmapContextCreate(NULL, - width, - height, - 8, - 0, - CGImageGetColorSpace(imageRef), - kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); - - // Draw the image into the context and retrieve the new image, which will now have an alpha layer - CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), imageRef); - CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(offscreenContext); - UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha]; - - // Clean up - CGContextRelease(offscreenContext); - CGImageRelease(imageRefWithAlpha); - - return imageWithAlpha; ++ (UIImage *)imageWithAlpha:(UIImage *)image +{ + if ([TiUIImageAlpha hasAlpha:image]) { + return image; + } + + CGImageRef imageRef = image.CGImage; + size_t width = CGImageGetWidth(imageRef); + size_t height = CGImageGetHeight(imageRef); + + // The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error + CGContextRef offscreenContext = CGBitmapContextCreate(NULL, + width, + height, + 8, + 0, + CGImageGetColorSpace(imageRef), + kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst); + + // Draw the image into the context and retrieve the new image, which will now have an alpha layer + CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), imageRef); + CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(offscreenContext); + UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha]; + + // Clean up + CGContextRelease(offscreenContext); + CGImageRelease(imageRefWithAlpha); + + return imageWithAlpha; } // Creates a mask that makes the outer edges transparent and everything else opaque // The size must include the entire mask (opaque part + transparent border) // The caller is responsible for releasing the returned reference by calling CGImageRelease -+ (CGImageRef)newBorderMask:(NSUInteger)borderSize size:(CGSize)size image:(UIImage*)image ++ (CGImageRef)newBorderMask:(NSUInteger)borderSize size:(CGSize)size image:(UIImage *)image { - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); - - // Build a context that's the same dimensions as the new size - CGContextRef maskContext = CGBitmapContextCreate(NULL, - size.width, - size.height, - 8, // 8-bit grayscale - 0, - colorSpace, - kCGBitmapByteOrderDefault | kCGImageAlphaNone); - - // Start with a mask that's entirely transparent - CGContextSetFillColorWithColor(maskContext, [UIColor blackColor].CGColor); - CGContextFillRect(maskContext, CGRectMake(0, 0, size.width, size.height)); - - // Make the inner part (within the border) opaque - CGContextSetFillColorWithColor(maskContext, [UIColor whiteColor].CGColor); - CGContextFillRect(maskContext, CGRectMake(borderSize, borderSize, size.width - borderSize * 2, size.height - borderSize * 2)); - - // Get an image of the context - CGImageRef maskImageRef = CGBitmapContextCreateImage(maskContext); - - // Clean up - CGContextRelease(maskContext); - CGColorSpaceRelease(colorSpace); - - return maskImageRef; -} + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + + // Build a context that's the same dimensions as the new size + CGContextRef maskContext = CGBitmapContextCreate(NULL, + size.width, + size.height, + 8, // 8-bit grayscale + 0, + colorSpace, + kCGBitmapByteOrderDefault | kCGImageAlphaNone); + + // Start with a mask that's entirely transparent + CGContextSetFillColorWithColor(maskContext, [UIColor blackColor].CGColor); + CGContextFillRect(maskContext, CGRectMake(0, 0, size.width, size.height)); + + // Make the inner part (within the border) opaque + CGContextSetFillColorWithColor(maskContext, [UIColor whiteColor].CGColor); + CGContextFillRect(maskContext, CGRectMake(borderSize, borderSize, size.width - borderSize * 2, size.height - borderSize * 2)); + + // Get an image of the context + CGImageRef maskImageRef = CGBitmapContextCreateImage(maskContext); + // Clean up + CGContextRelease(maskContext); + CGColorSpaceRelease(colorSpace); + + return maskImageRef; +} // Returns a copy of the image with a transparent border of the given size added around its edges. // If the image has no alpha layer, one will be added to it. -+ (UIImage *)transparentBorderImage:(NSUInteger)borderSize image:(UIImage*)image_{ - // If the image does not have an alpha layer, add one - UIImage *image = [TiUIImageAlpha imageWithAlpha:image_]; - - CGRect newRect = CGRectMake(0, 0, image.size.width + borderSize * 2, image.size.height + borderSize * 2); - - // Build a context that's the same dimensions as the new size - CGContextRef bitmap = CGBitmapContextCreate(NULL, - newRect.size.width, - newRect.size.height, - CGImageGetBitsPerComponent(image_.CGImage), - 0, - CGImageGetColorSpace(image_.CGImage), - CGImageGetBitmapInfo(image_.CGImage)); - - // Draw the image in the center of the context, leaving a gap around the edges - CGRect imageLocation = CGRectMake(borderSize, borderSize, image.size.width, image.size.height); - CGContextDrawImage(bitmap, imageLocation, image_.CGImage); - CGImageRef borderImageRef = CGBitmapContextCreateImage(bitmap); - - // Create a mask to make the border transparent, and combine it with the image - CGImageRef maskImageRef = [TiUIImageAlpha newBorderMask:borderSize size:newRect.size image:image_]; - if((maskImageRef == NULL) || (borderImageRef == NULL)) - { - CGContextRelease(bitmap); - CGImageRelease(maskImageRef); - CGImageRelease(borderImageRef); - return nil; - } - CGImageRef transparentBorderImageRef = CGImageCreateWithMask(borderImageRef, maskImageRef); - UIImage *transparentBorderImage = [UIImage imageWithCGImage:transparentBorderImageRef]; - - // Clean up ++ (UIImage *)transparentBorderImage:(NSUInteger)borderSize image:(UIImage *)image_ +{ + // If the image does not have an alpha layer, add one + UIImage *image = [TiUIImageAlpha imageWithAlpha:image_]; + + CGRect newRect = CGRectMake(0, 0, image.size.width + borderSize * 2, image.size.height + borderSize * 2); + + // Build a context that's the same dimensions as the new size + CGContextRef bitmap = CGBitmapContextCreate(NULL, + newRect.size.width, + newRect.size.height, + CGImageGetBitsPerComponent(image_.CGImage), + 0, + CGImageGetColorSpace(image_.CGImage), + CGImageGetBitmapInfo(image_.CGImage)); + + // Draw the image in the center of the context, leaving a gap around the edges + CGRect imageLocation = CGRectMake(borderSize, borderSize, image.size.width, image.size.height); + CGContextDrawImage(bitmap, imageLocation, image_.CGImage); + CGImageRef borderImageRef = CGBitmapContextCreateImage(bitmap); + + // Create a mask to make the border transparent, and combine it with the image + CGImageRef maskImageRef = [TiUIImageAlpha newBorderMask:borderSize size:newRect.size image:image_]; + if ((maskImageRef == NULL) || (borderImageRef == NULL)) { CGContextRelease(bitmap); - CGImageRelease(borderImageRef); CGImageRelease(maskImageRef); - CGImageRelease(transparentBorderImageRef); - - return transparentBorderImage; + CGImageRelease(borderImageRef); + return nil; + } + CGImageRef transparentBorderImageRef = CGImageCreateWithMask(borderImageRef, maskImageRef); + UIImage *transparentBorderImage = [UIImage imageWithCGImage:transparentBorderImageRef]; + + // Clean up + CGContextRelease(bitmap); + CGImageRelease(borderImageRef); + CGImageRelease(maskImageRef); + CGImageRelease(transparentBorderImageRef); + + return transparentBorderImage; } @end diff --git a/ios/Classes/UIImage/TiUIImage+Resize.m b/ios/Classes/UIImage/TiUIImage+Resize.m index 607aca0..d8c679e 100644 --- a/ios/Classes/UIImage/TiUIImage+Resize.m +++ b/ios/Classes/UIImage/TiUIImage+Resize.m @@ -3,16 +3,16 @@ // Free for personal or commercial use, with or without modification. // No warranty is expressed or implied. +#import "TiUIImage+Alpha.h" #import "TiUIImage+Resize.h" #import "TiUIImage+RoundedCorner.h" -#import "TiUIImage+Alpha.h" #import "TiUtils.h" @implementation TiUIImageResize + (BOOL)isIOS4OrGreater { - return [UIView instancesRespondToSelector:@selector(contentScaleFactor)]; + return [UIView instancesRespondToSelector:@selector(contentScaleFactor)]; } // Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size @@ -21,135 +21,133 @@ + (BOOL)isIOS4OrGreater + (UIImage *)resizedImage:(CGSize)newSize transform:(CGAffineTransform)transform drawTransposed:(BOOL)transpose - interpolationQuality:(CGInterpolationQuality)quality - image:(UIImage*)image - hires:(BOOL)hires + interpolationQuality:(CGInterpolationQuality)quality + image:(UIImage *)image + hires:(BOOL)hires { - CGImageRef imageRef = image.CGImage; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - - CGFloat scale = 1.0; + CGImageRef imageRef = image.CGImage; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + + CGFloat scale = 1.0; #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0 - if ([TiUIImageResize isIOS4OrGreater]) { - scale = [image scale]; + if ([TiUIImageResize isIOS4OrGreater]) { + scale = [image scale]; - // Force scaling to 2x/3x + // Force scaling to 2x/3x if (hires) { if ([TiUtils is2xRetina]) { scale = 2.0; - }else if ([TiUtils is3xRetina]) { + } else if ([TiUtils is3xRetina]) { scale = 3.0; } } - } -#endif - - CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width*scale, newSize.height*scale)); - CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width); - - // Build a context that's the same dimensions as the new size - CGContextRef bitmap = CGBitmapContextCreate(NULL, - newRect.size.width, - newRect.size.height, - 8, - 0, - colorSpace, - kCGImageAlphaPremultipliedLast); - - // Rotate and/or flip the image if required by its orientation - CGContextConcatCTM(bitmap, transform); - - // Set the quality level to use when rescaling - CGContextSetInterpolationQuality(bitmap, quality); - - // Draw into the context; this scales the image - CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef); - - // Get the resized image from the context and a UIImage - CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); - UIImage *newImage = nil; - if ([TiUIImageResize isIOS4OrGreater]) { - newImage = [UIImage imageWithCGImage:newImageRef scale:scale orientation:UIImageOrientationUp]; - } - else { - newImage = [UIImage imageWithCGImage:newImageRef]; - } - - // Clean up - CGContextRelease(bitmap); - CGImageRelease(newImageRef); - CGColorSpaceRelease(colorSpace); - - return newImage; + } +#endif + + CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width * scale, newSize.height * scale)); + CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width); + + // Build a context that's the same dimensions as the new size + CGContextRef bitmap = CGBitmapContextCreate(NULL, + newRect.size.width, + newRect.size.height, + 8, + 0, + colorSpace, + kCGImageAlphaPremultipliedLast); + + // Rotate and/or flip the image if required by its orientation + CGContextConcatCTM(bitmap, transform); + + // Set the quality level to use when rescaling + CGContextSetInterpolationQuality(bitmap, quality); + + // Draw into the context; this scales the image + CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef); + + // Get the resized image from the context and a UIImage + CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); + UIImage *newImage = nil; + if ([TiUIImageResize isIOS4OrGreater]) { + newImage = [UIImage imageWithCGImage:newImageRef scale:scale orientation:UIImageOrientationUp]; + } else { + newImage = [UIImage imageWithCGImage:newImageRef]; + } + + // Clean up + CGContextRelease(bitmap); + CGImageRelease(newImageRef); + CGColorSpaceRelease(colorSpace); + + return newImage; } // Returns an affine transform that takes into account the image orientation when drawing a scaled image -+ (CGAffineTransform)transformForOrientation:(CGSize)newSize image:(UIImage*)image ++ (CGAffineTransform)transformForOrientation:(CGSize)newSize image:(UIImage *)image { - CGAffineTransform transform = CGAffineTransformIdentity; - - switch (image.imageOrientation) { - case UIImageOrientationDown: // EXIF = 3 - case UIImageOrientationDownMirrored: // EXIF = 4 - transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height); - transform = CGAffineTransformRotate(transform, M_PI); - break; - - case UIImageOrientationLeft: // EXIF = 6 - case UIImageOrientationLeftMirrored: // EXIF = 5 - transform = CGAffineTransformTranslate(transform, newSize.width, 0); - transform = CGAffineTransformRotate(transform, M_PI_2); - break; - - case UIImageOrientationRight: // EXIF = 8 - case UIImageOrientationRightMirrored: // EXIF = 7 - transform = CGAffineTransformTranslate(transform, 0, newSize.height); - transform = CGAffineTransformRotate(transform, -M_PI_2); - break; - case UIImageOrientationUp: - break; - case UIImageOrientationUpMirrored: - break; - } - - switch (image.imageOrientation) { - case UIImageOrientationUp: - break; - case UIImageOrientationDown: - break; - case UIImageOrientationLeft: - break; - case UIImageOrientationRight: - break; - case UIImageOrientationUpMirrored: // EXIF = 2 - case UIImageOrientationDownMirrored: // EXIF = 4 - transform = CGAffineTransformTranslate(transform, newSize.width, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - - case UIImageOrientationLeftMirrored: // EXIF = 5 - case UIImageOrientationRightMirrored: // EXIF = 7 - transform = CGAffineTransformTranslate(transform, newSize.height, 0); - transform = CGAffineTransformScale(transform, -1, 1); - break; - } - - return transform; + CGAffineTransform transform = CGAffineTransformIdentity; + + switch (image.imageOrientation) { + case UIImageOrientationDown: // EXIF = 3 + case UIImageOrientationDownMirrored: // EXIF = 4 + transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height); + transform = CGAffineTransformRotate(transform, M_PI); + break; + + case UIImageOrientationLeft: // EXIF = 6 + case UIImageOrientationLeftMirrored: // EXIF = 5 + transform = CGAffineTransformTranslate(transform, newSize.width, 0); + transform = CGAffineTransformRotate(transform, M_PI_2); + break; + + case UIImageOrientationRight: // EXIF = 8 + case UIImageOrientationRightMirrored: // EXIF = 7 + transform = CGAffineTransformTranslate(transform, 0, newSize.height); + transform = CGAffineTransformRotate(transform, -M_PI_2); + break; + case UIImageOrientationUp: + break; + case UIImageOrientationUpMirrored: + break; + } + + switch (image.imageOrientation) { + case UIImageOrientationUp: + break; + case UIImageOrientationDown: + break; + case UIImageOrientationLeft: + break; + case UIImageOrientationRight: + break; + case UIImageOrientationUpMirrored: // EXIF = 2 + case UIImageOrientationDownMirrored: // EXIF = 4 + transform = CGAffineTransformTranslate(transform, newSize.width, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + + case UIImageOrientationLeftMirrored: // EXIF = 5 + case UIImageOrientationRightMirrored: // EXIF = 7 + transform = CGAffineTransformTranslate(transform, newSize.height, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + } + + return transform; } // Returns a copy of this image that is cropped to the given bounds. // The bounds will be adjusted using CGRectIntegral. // This method ignores the image's imageOrientation setting. -+ (UIImage *)croppedImage:(CGRect)bounds image:(UIImage*)image ++ (UIImage *)croppedImage:(CGRect)bounds image:(UIImage *)image { - if(image == nil) - { - return nil; - } - CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, bounds); - UIImage *croppedImage = [UIImage imageWithCGImage:imageRef]; - CGImageRelease(imageRef); - return croppedImage; + if (image == nil) { + return nil; + } + CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, bounds); + UIImage *croppedImage = [UIImage imageWithCGImage:imageRef]; + CGImageRelease(imageRef); + return croppedImage; } // Returns a copy of this image that is squared to the thumbnail size. @@ -157,82 +155,83 @@ + (UIImage *)croppedImage:(CGRect)bounds image:(UIImage*)image + (UIImage *)thumbnailImage:(NSInteger)thumbnailSize transparentBorder:(NSUInteger)borderSize cornerRadius:(NSUInteger)cornerRadius - interpolationQuality:(CGInterpolationQuality)quality - image:(UIImage*)image { - UIImage *resizedImage = [TiUIImageResize resizedImageWithContentMode:UIViewContentModeScaleAspectFill - bounds:CGSizeMake(thumbnailSize, thumbnailSize) - interpolationQuality:quality - image:image]; - - // Crop out any part of the image that's larger than the thumbnail size - // The cropped rect must be centered on the resized image - // Round the origin points so that the size isn't altered when CGRectIntegral is later invoked - CGRect cropRect = CGRectMake(round((resizedImage.size.width - thumbnailSize) / 2), - round((resizedImage.size.height - thumbnailSize) / 2), - thumbnailSize, - thumbnailSize); - UIImage *croppedImage = [TiUIImageResize croppedImage:cropRect image:resizedImage]; - - UIImage *transparentBorderImage = borderSize ? [TiUIImageAlpha transparentBorderImage:borderSize image:croppedImage] : croppedImage; - - return [TiUIImageRoundedCorner roundedCornerImage:cornerRadius borderSize:borderSize image:transparentBorderImage]; + interpolationQuality:(CGInterpolationQuality)quality + image:(UIImage *)image +{ + UIImage *resizedImage = [TiUIImageResize resizedImageWithContentMode:UIViewContentModeScaleAspectFill + bounds:CGSizeMake(thumbnailSize, thumbnailSize) + interpolationQuality:quality + image:image]; + + // Crop out any part of the image that's larger than the thumbnail size + // The cropped rect must be centered on the resized image + // Round the origin points so that the size isn't altered when CGRectIntegral is later invoked + CGRect cropRect = CGRectMake(round((resizedImage.size.width - thumbnailSize) / 2), + round((resizedImage.size.height - thumbnailSize) / 2), + thumbnailSize, + thumbnailSize); + UIImage *croppedImage = [TiUIImageResize croppedImage:cropRect image:resizedImage]; + + UIImage *transparentBorderImage = borderSize ? [TiUIImageAlpha transparentBorderImage:borderSize image:croppedImage] : croppedImage; + + return [TiUIImageRoundedCorner roundedCornerImage:cornerRadius borderSize:borderSize image:transparentBorderImage]; } // Returns a rescaled copy of the image, taking into account its orientation // The image will be scaled disproportionately if necessary to fit the bounds specified by the parameter -+ (UIImage *)resizedImage:(CGSize)newSize - interpolationQuality:(CGInterpolationQuality)quality - image:(UIImage*)image - hires:(BOOL)hires ++ (UIImage *)resizedImage:(CGSize)newSize + interpolationQuality:(CGInterpolationQuality)quality + image:(UIImage *)image + hires:(BOOL)hires { - BOOL drawTransposed; - - switch (image.imageOrientation) { - case UIImageOrientationLeft: - case UIImageOrientationLeftMirrored: - case UIImageOrientationRight: - case UIImageOrientationRightMirrored: - drawTransposed = YES; - break; - - default: - drawTransposed = NO; - } - - return [TiUIImageResize resizedImage:newSize - transform:[TiUIImageResize transformForOrientation:newSize image:image] - drawTransposed:drawTransposed - interpolationQuality:quality - image:image - hires:hires]; + BOOL drawTransposed; + + switch (image.imageOrientation) { + case UIImageOrientationLeft: + case UIImageOrientationLeftMirrored: + case UIImageOrientationRight: + case UIImageOrientationRightMirrored: + drawTransposed = YES; + break; + + default: + drawTransposed = NO; + } + + return [TiUIImageResize resizedImage:newSize + transform:[TiUIImageResize transformForOrientation:newSize image:image] + drawTransposed:drawTransposed + interpolationQuality:quality + image:image + hires:hires]; } // Resizes the image according to the given content mode, taking into account the image's orientation + (UIImage *)resizedImageWithContentMode:(UIViewContentMode)contentMode bounds:(CGSize)bounds - interpolationQuality:(CGInterpolationQuality)quality - image:(UIImage*)image + interpolationQuality:(CGInterpolationQuality)quality + image:(UIImage *)image { - CGFloat horizontalRatio = bounds.width / image.size.width; - CGFloat verticalRatio = bounds.height / image.size.height; - CGFloat ratio; - - switch (contentMode) { - case UIViewContentModeScaleAspectFill: - ratio = MAX(horizontalRatio, verticalRatio); - break; - - case UIViewContentModeScaleAspectFit: - ratio = MIN(horizontalRatio, verticalRatio); - break; - - default: - [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %ld", (long)contentMode]; - } - - CGSize newSize = CGSizeMake(image.size.width * ratio, image.size.height * ratio); - - return [TiUIImageResize resizedImage:newSize interpolationQuality:quality image:image hires:NO]; + CGFloat horizontalRatio = bounds.width / image.size.width; + CGFloat verticalRatio = bounds.height / image.size.height; + CGFloat ratio; + + switch (contentMode) { + case UIViewContentModeScaleAspectFill: + ratio = MAX(horizontalRatio, verticalRatio); + break; + + case UIViewContentModeScaleAspectFit: + ratio = MIN(horizontalRatio, verticalRatio); + break; + + default: + [NSException raise:NSInvalidArgumentException format:@"Unsupported content mode: %ld", (long)contentMode]; + } + + CGSize newSize = CGSizeMake(image.size.width * ratio, image.size.height * ratio); + + return [TiUIImageResize resizedImage:newSize interpolationQuality:quality image:image hires:NO]; } @end diff --git a/ios/Classes/UIImage/TiUIImage+Rotate.m b/ios/Classes/UIImage/TiUIImage+Rotate.m index 1324161..4d3a96c 100644 --- a/ios/Classes/UIImage/TiUIImage+Rotate.m +++ b/ios/Classes/UIImage/TiUIImage+Rotate.m @@ -10,35 +10,34 @@ @implementation TiUIImageRotate -static CGFloat DegreesToRadians(CGFloat degrees) {return degrees * M_PI / 180;}; +static CGFloat DegreesToRadians(CGFloat degrees) { return degrees * M_PI / 180; }; -+ (UIImage *)rotateImage:(CGFloat)degrees image:(UIImage*)image ++ (UIImage *)rotateImage:(CGFloat)degrees image:(UIImage *)image { - - // calculate the size of the rotated view's containing box for our drawing space - UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0,0,image.size.width, image.size.height)]; - CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees)); - rotatedViewBox.transform = t; - CGSize rotatedSize = rotatedViewBox.frame.size; - - // Create the bitmap context - UIGraphicsBeginImageContext(rotatedSize); - CGContextRef bitmap = UIGraphicsGetCurrentContext(); - - // Move the origin to the middle of the image so we will rotate and scale around the center. - CGContextTranslateCTM(bitmap, rotatedSize.width/2, rotatedSize.height/2); - - // // Rotate the image context - CGContextRotateCTM(bitmap, DegreesToRadians(degrees)); - - // Now, draw the rotated/scaled image into the context - CGContextScaleCTM(bitmap, 1.0, -1.0); - CGContextDrawImage(bitmap, CGRectMake(-image.size.width / 2, -image.size.height / 2, image.size.width, image.size.height), [image CGImage]); - - UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); - UIGraphicsEndImageContext(); - return newImage; + // calculate the size of the rotated view's containing box for our drawing space + UIView *rotatedViewBox = [[UIView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)]; + CGAffineTransform t = CGAffineTransformMakeRotation(DegreesToRadians(degrees)); + rotatedViewBox.transform = t; + CGSize rotatedSize = rotatedViewBox.frame.size; + + // Create the bitmap context + UIGraphicsBeginImageContext(rotatedSize); + CGContextRef bitmap = UIGraphicsGetCurrentContext(); + + // Move the origin to the middle of the image so we will rotate and scale around the center. + CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2); + + // // Rotate the image context + CGContextRotateCTM(bitmap, DegreesToRadians(degrees)); + + // Now, draw the rotated/scaled image into the context + CGContextScaleCTM(bitmap, 1.0, -1.0); + CGContextDrawImage(bitmap, CGRectMake(-image.size.width / 2, -image.size.height / 2, image.size.width, image.size.height), [image CGImage]); + + UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImage; } @end diff --git a/ios/Classes/UIImage/TiUIImage+RoundedCorner.m b/ios/Classes/UIImage/TiUIImage+RoundedCorner.m index 46b884b..d645287 100644 --- a/ios/Classes/UIImage/TiUIImage+RoundedCorner.m +++ b/ios/Classes/UIImage/TiUIImage+RoundedCorner.m @@ -3,72 +3,71 @@ // Free for personal or commercial use, with or without modification. // No warranty is expressed or implied. -#import "TiUIImage+RoundedCorner.h" #import "TiUIImage+Alpha.h" +#import "TiUIImage+RoundedCorner.h" @implementation TiUIImageRoundedCorner // Adds a rectangular path to the given context and rounds its corners by the given extents // Original author: Björn Sållarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/ -+ (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight ++ (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight { - if (ovalWidth == 0 || ovalHeight == 0) { - CGContextAddRect(context, rect); - return; - } - CGContextSaveGState(context); - CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); - CGContextScaleCTM(context, ovalWidth, ovalHeight); - CGFloat fw = CGRectGetWidth(rect) / ovalWidth; - CGFloat fh = CGRectGetHeight(rect) / ovalHeight; - CGContextMoveToPoint(context, fw, fh/2); - CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1); - CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1); - CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1); - CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); - CGContextClosePath(context); - CGContextRestoreGState(context); + if (ovalWidth == 0 || ovalHeight == 0) { + CGContextAddRect(context, rect); + return; + } + CGContextSaveGState(context); + CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); + CGContextScaleCTM(context, ovalWidth, ovalHeight); + CGFloat fw = CGRectGetWidth(rect) / ovalWidth; + CGFloat fh = CGRectGetHeight(rect) / ovalHeight; + CGContextMoveToPoint(context, fw, fh / 2); + CGContextAddArcToPoint(context, fw, fh, fw / 2, fh, 1); + CGContextAddArcToPoint(context, 0, fh, 0, fh / 2, 1); + CGContextAddArcToPoint(context, 0, 0, fw / 2, 0, 1); + CGContextAddArcToPoint(context, fw, 0, fw, fh / 2, 1); + CGContextClosePath(context); + CGContextRestoreGState(context); } // Creates a copy of this image with rounded corners // If borderSize is non-zero, a transparent border of the given size will also be added // Original author: Björn Sållarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/ -+ (UIImage *)roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize image:(UIImage*)image_ ++ (UIImage *)roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize image:(UIImage *)image_ { - // If the image does not have an alpha layer, add one - UIImage *image = [TiUIImageAlpha imageWithAlpha:image_]; - - // Build a context that's the same dimensions as the new size - CGContextRef context = CGBitmapContextCreate(NULL, - image.size.width, - image.size.height, - CGImageGetBitsPerComponent(image.CGImage), - 0, - CGImageGetColorSpace(image.CGImage), - CGImageGetBitmapInfo(image.CGImage)); + // If the image does not have an alpha layer, add one + UIImage *image = [TiUIImageAlpha imageWithAlpha:image_]; - // Create a clipping path with rounded corners - CGContextBeginPath(context); - [self addRoundedRectToPath:CGRectMake(borderSize, borderSize, image.size.width - borderSize * 2, image.size.height - borderSize * 2) - context:context - ovalWidth:cornerSize - ovalHeight:cornerSize]; - CGContextClosePath(context); - CGContextClip(context); + // Build a context that's the same dimensions as the new size + CGContextRef context = CGBitmapContextCreate(NULL, + image.size.width, + image.size.height, + CGImageGetBitsPerComponent(image.CGImage), + 0, + CGImageGetColorSpace(image.CGImage), + CGImageGetBitmapInfo(image.CGImage)); - // Draw the image to the context; the clipping path will make anything outside the rounded rect transparent - CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); - - // Create a CGImage from the context - CGImageRef clippedImage = CGBitmapContextCreateImage(context); - CGContextRelease(context); - - // Create a UIImage from the CGImage - UIImage *roundedImage = [UIImage imageWithCGImage:clippedImage]; - CGImageRelease(clippedImage); - - return roundedImage; -} + // Create a clipping path with rounded corners + CGContextBeginPath(context); + [self addRoundedRectToPath:CGRectMake(borderSize, borderSize, image.size.width - borderSize * 2, image.size.height - borderSize * 2) + context:context + ovalWidth:cornerSize + ovalHeight:cornerSize]; + CGContextClosePath(context); + CGContextClip(context); + + // Draw the image to the context; the clipping path will make anything outside the rounded rect transparent + CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage); + // Create a CGImage from the context + CGImageRef clippedImage = CGBitmapContextCreateImage(context); + CGContextRelease(context); + + // Create a UIImage from the CGImage + UIImage *roundedImage = [UIImage imageWithCGImage:clippedImage]; + CGImageRelease(clippedImage); + + return roundedImage; +} @end