Add image transformer protocol and class. Add UIImage+Transformer category including common image geometry, tinting, blur effect processor.

This commit is contained in:
DreamPiggy 2018-02-11 23:36:58 +08:00
parent 5ff999f3d1
commit b4ea87f6c5
6 changed files with 1138 additions and 0 deletions

View File

@ -388,6 +388,30 @@
32CF1C101FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */; };
32CF1C111FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */; };
32CF1C121FA496B000004BD1 /* SDWebImageCoderHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */; };
32F7C06F2030114C00873181 /* SDWebImageTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C06D2030114C00873181 /* SDWebImageTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
32F7C0702030114C00873181 /* SDWebImageTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C06D2030114C00873181 /* SDWebImageTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
32F7C0712030114C00873181 /* SDWebImageTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C06D2030114C00873181 /* SDWebImageTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
32F7C0722030114C00873181 /* SDWebImageTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C06D2030114C00873181 /* SDWebImageTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
32F7C0732030114C00873181 /* SDWebImageTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C06D2030114C00873181 /* SDWebImageTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
32F7C0742030114C00873181 /* SDWebImageTransformer.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C06D2030114C00873181 /* SDWebImageTransformer.h */; settings = {ATTRIBUTES = (Public, ); }; };
32F7C0752030114C00873181 /* SDWebImageTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C06E2030114C00873181 /* SDWebImageTransformer.m */; };
32F7C0762030114C00873181 /* SDWebImageTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C06E2030114C00873181 /* SDWebImageTransformer.m */; };
32F7C0772030114C00873181 /* SDWebImageTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C06E2030114C00873181 /* SDWebImageTransformer.m */; };
32F7C0782030114C00873181 /* SDWebImageTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C06E2030114C00873181 /* SDWebImageTransformer.m */; };
32F7C0792030114C00873181 /* SDWebImageTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C06E2030114C00873181 /* SDWebImageTransformer.m */; };
32F7C07A2030114C00873181 /* SDWebImageTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C06E2030114C00873181 /* SDWebImageTransformer.m */; };
32F7C07E2030719600873181 /* UIImage+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C07C2030719600873181 /* UIImage+Transform.m */; };
32F7C07F2030719600873181 /* UIImage+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C07C2030719600873181 /* UIImage+Transform.m */; };
32F7C0802030719600873181 /* UIImage+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C07C2030719600873181 /* UIImage+Transform.m */; };
32F7C0812030719600873181 /* UIImage+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C07C2030719600873181 /* UIImage+Transform.m */; };
32F7C0822030719600873181 /* UIImage+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C07C2030719600873181 /* UIImage+Transform.m */; };
32F7C0832030719600873181 /* UIImage+Transform.m in Sources */ = {isa = PBXBuildFile; fileRef = 32F7C07C2030719600873181 /* UIImage+Transform.m */; };
32F7C0842030719600873181 /* UIImage+Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C07D2030719600873181 /* UIImage+Transform.h */; };
32F7C0852030719600873181 /* UIImage+Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C07D2030719600873181 /* UIImage+Transform.h */; };
32F7C0862030719600873181 /* UIImage+Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C07D2030719600873181 /* UIImage+Transform.h */; };
32F7C0872030719600873181 /* UIImage+Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C07D2030719600873181 /* UIImage+Transform.h */; };
32F7C0882030719600873181 /* UIImage+Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C07D2030719600873181 /* UIImage+Transform.h */; };
32F7C0892030719600873181 /* UIImage+Transform.h in Headers */ = {isa = PBXBuildFile; fileRef = 32F7C07D2030719600873181 /* UIImage+Transform.h */; };
4314D1231D0E0E3B004B36C9 /* SDImageCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D86148C56230056699D /* SDImageCache.m */; };
4314D1311D0E0E3B004B36C9 /* SDWebImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 53922D8C148C56230056699D /* SDWebImageDownloader.m */; };
4314D1341D0E0E3B004B36C9 /* UIImage+WebP.m in Sources */ = {isa = PBXBuildFile; fileRef = 53EDFB921762547C00698166 /* UIImage+WebP.m */; };
@ -1391,6 +1415,10 @@
32C0FDE02013426C001B8F2D /* SDWebImageIndicator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageIndicator.m; sourceTree = "<group>"; };
32CF1C051FA496B000004BD1 /* SDWebImageCoderHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageCoderHelper.h; sourceTree = "<group>"; };
32CF1C061FA496B000004BD1 /* SDWebImageCoderHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageCoderHelper.m; sourceTree = "<group>"; };
32F7C06D2030114C00873181 /* SDWebImageTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTransformer.h; sourceTree = "<group>"; };
32F7C06E2030114C00873181 /* SDWebImageTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTransformer.m; sourceTree = "<group>"; };
32F7C07C2030719600873181 /* UIImage+Transform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Transform.m"; sourceTree = "<group>"; };
32F7C07D2030719600873181 /* UIImage+Transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Transform.h"; sourceTree = "<group>"; };
4314D1991D0E0E3B004B36C9 /* libSDWebImage watchOS static.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libSDWebImage watchOS static.a"; sourceTree = BUILT_PRODUCTS_DIR; };
431BB7031D06D2C1006A3455 /* SDWebImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4369C2751D9807EC007E863A /* UIView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+WebCache.h"; path = "SDWebImage/UIView+WebCache.h"; sourceTree = "<group>"; };
@ -1835,6 +1863,8 @@
53EDFB921762547C00698166 /* UIImage+WebP.m */,
321E60BC1F38E91700405457 /* UIImage+ForceDecode.h */,
321E60BD1F38E91700405457 /* UIImage+ForceDecode.m */,
32F7C07D2030719600873181 /* UIImage+Transform.h */,
32F7C07C2030719600873181 /* UIImage+Transform.m */,
4397D2F41D0DE2DF00BB2784 /* NSImage+Additions.h */,
4397D2F51D0DE2DF00BB2784 /* NSImage+Additions.m */,
AB615301192DA24600A2D8E9 /* UIView+WebCacheOperation.h */,
@ -1878,6 +1908,8 @@
325312C7200F09910046BF1E /* SDWebImageTransition.m */,
32C0FDDF2013426C001B8F2D /* SDWebImageIndicator.h */,
32C0FDE02013426C001B8F2D /* SDWebImageIndicator.m */,
32F7C06D2030114C00873181 /* SDWebImageTransformer.h */,
32F7C06E2030114C00873181 /* SDWebImageTransformer.m */,
);
name = Utils;
sourceTree = "<group>";
@ -2125,11 +2157,13 @@
80377C5F1F2F666300F89830 /* utils.h in Headers */,
80377C5B1F2F666300F89830 /* rescaler_utils.h in Headers */,
323F8BF91F38EF770092B609 /* animi.h in Headers */,
32F7C0872030719600873181 /* UIImage+Transform.h in Headers */,
80377C4F1F2F666300F89830 /* filters_utils.h in Headers */,
80377C4C1F2F666300F89830 /* color_cache_utils.h in Headers */,
32C0FDE42013426C001B8F2D /* SDWebImageIndicator.h in Headers */,
321E60C11F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
80377DBE1F2F66A700F89830 /* dsp.h in Headers */,
32F7C0722030114C00873181 /* SDWebImageTransformer.h in Headers */,
80377EB81F2F66D400F89830 /* alphai_dec.h in Headers */,
00733A6D1BC4880E00A5A117 /* UIImage+GIF.h in Headers */,
80377C551F2F666300F89830 /* quant_levels_dec_utils.h in Headers */,
@ -2152,8 +2186,10 @@
321E60BF1F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
80377EA61F2F66D400F89830 /* webpi_dec.h in Headers */,
807A12291F89636300EC2A9B /* SDWebImageCodersManager.h in Headers */,
32F7C0852030719600873181 /* UIImage+Transform.h in Headers */,
80377C141F2F666300F89830 /* bit_reader_utils.h in Headers */,
323F8C0F1F38EF770092B609 /* muxi.h in Headers */,
32F7C0702030114C00873181 /* SDWebImageTransformer.h in Headers */,
80377C2B1F2F666300F89830 /* utils.h in Headers */,
4314D1621D0E0E3B004B36C9 /* mux_types.h in Headers */,
4314D1631D0E0E3B004B36C9 /* demux.h in Headers */,
@ -2255,6 +2291,7 @@
80377E211F2F66A800F89830 /* neon.h in Headers */,
80377C711F2F666400F89830 /* quant_levels_utils.h in Headers */,
323F8B541F38EF770092B609 /* backward_references_enc.h in Headers */,
32F7C0882030719600873181 /* UIImage+Transform.h in Headers */,
43A62A1F1D0E0A800089D7DD /* mux.h in Headers */,
431BB6E91D06D2C1006A3455 /* SDWebImageDownloaderOperation.h in Headers */,
80377ED41F2F66D500F89830 /* vp8li_dec.h in Headers */,
@ -2289,6 +2326,7 @@
323F8BFA1F38EF770092B609 /* animi.h in Headers */,
431BB6F91D06D2C1006A3455 /* UIImage+GIF.h in Headers */,
321E60B41F38E90100405457 /* SDWebImageWebPCoder.h in Headers */,
32F7C0732030114C00873181 /* SDWebImageTransformer.h in Headers */,
431BB6FA1D06D2C1006A3455 /* SDWebImageDownloader.h in Headers */,
80377DF51F2F66A800F89830 /* common_sse2.h in Headers */,
323F8BDC1F38EF770092B609 /* vp8i_enc.h in Headers */,
@ -2305,6 +2343,7 @@
80377ED81F2F66D500F89830 /* alphai_dec.h in Headers */,
321E60A71F38E8F600405457 /* SDWebImageGIFCoder.h in Headers */,
324DF4B9200A14DC008A84CC /* SDWebImageDefine.h in Headers */,
32F7C0892030719600873181 /* UIImage+Transform.h in Headers */,
80377EDA1F2F66D500F89830 /* common_dec.h in Headers */,
80377EE61F2F66D500F89830 /* webpi_dec.h in Headers */,
4397D2BA1D0DDD8C00BB2784 /* demux.h in Headers */,
@ -2340,6 +2379,7 @@
32CF1C0C1FA496B000004BD1 /* SDWebImageCoderHelper.h in Headers */,
43A918691D8308FE00B3925F /* SDImageCacheConfig.h in Headers */,
4397D2D81D0DDD8C00BB2784 /* UIButton+WebCache.h in Headers */,
32F7C0742030114C00873181 /* SDWebImageTransformer.h in Headers */,
80377E641F2F66A800F89830 /* mips_macro.h in Headers */,
323F8BDD1F38EF770092B609 /* vp8i_enc.h in Headers */,
323F8B671F38EF770092B609 /* cost_enc.h in Headers */,
@ -2452,11 +2492,13 @@
4A2CAE311AB4BB7500B6BC39 /* UIImage+WebP.h in Headers */,
323F8BF81F38EF770092B609 /* animi.h in Headers */,
80377C351F2F666300F89830 /* filters_utils.h in Headers */,
32F7C0862030719600873181 /* UIImage+Transform.h in Headers */,
80377C321F2F666300F89830 /* color_cache_utils.h in Headers */,
321E60C01F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
32C0FDE32013426C001B8F2D /* SDWebImageIndicator.h in Headers */,
80377D791F2F66A700F89830 /* dsp.h in Headers */,
80377EA81F2F66D400F89830 /* alphai_dec.h in Headers */,
32F7C0712030114C00873181 /* SDWebImageTransformer.h in Headers */,
4A2CAE2D1AB4BB7500B6BC39 /* UIImage+GIF.h in Headers */,
80377C3B1F2F666300F89830 /* quant_levels_dec_utils.h in Headers */,
80377EB11F2F66D400F89830 /* vp8_dec.h in Headers */,
@ -2473,6 +2515,7 @@
431738BE1CDFC2660008FEB9 /* demux.h in Headers */,
80377BFC1F2F665300F89830 /* bit_writer_utils.h in Headers */,
32CF1C071FA496B000004BD1 /* SDWebImageCoderHelper.h in Headers */,
32F7C0842030719600873181 /* UIImage+Transform.h in Headers */,
431738BF1CDFC2660008FEB9 /* encode.h in Headers */,
53761316155AD0D5005750A4 /* SDImageCache.h in Headers */,
323F8C0E1F38EF770092B609 /* muxi.h in Headers */,
@ -2507,6 +2550,7 @@
80377C0F1F2F665300F89830 /* thread_utils.h in Headers */,
321E60BE1F38E91700405457 /* UIImage+ForceDecode.h in Headers */,
5376131E155AD0D5005750A4 /* SDWebImagePrefetcher.h in Headers */,
32F7C06F2030114C00873181 /* SDWebImageTransformer.h in Headers */,
324DF4B4200A14DC008A84CC /* SDWebImageDefine.h in Headers */,
80377CE11F2F66A100F89830 /* common_sse2.h in Headers */,
80377C0B1F2F665300F89830 /* random_utils.h in Headers */,
@ -2796,6 +2840,7 @@
80377DAE1F2F66A700F89830 /* argb_sse2.c in Sources */,
323F8B7D1F38EF770092B609 /* frame_enc.c in Sources */,
80377EBB1F2F66D500F89830 /* frame_dec.c in Sources */,
32F7C0782030114C00873181 /* SDWebImageTransformer.m in Sources */,
80377DAF1F2F66A700F89830 /* argb.c in Sources */,
323F8B6B1F38EF770092B609 /* delta_palettization_enc.c in Sources */,
00733A5B1BC4880000A5A117 /* NSData+ImageContentType.m in Sources */,
@ -2828,6 +2873,7 @@
80377DE31F2F66A700F89830 /* upsampling_mips_dsp_r2.c in Sources */,
80377EBD1F2F66D500F89830 /* io_dec.c in Sources */,
80377EBC1F2F66D500F89830 /* idec_dec.c in Sources */,
32F7C0812030719600873181 /* UIImage+Transform.m in Sources */,
323F8B991F38EF770092B609 /* near_lossless_enc.c in Sources */,
80377DE81F2F66A700F89830 /* yuv_mips_dsp_r2.c in Sources */,
80377EC31F2F66D500F89830 /* vp8l_dec.c in Sources */,
@ -3016,6 +3062,7 @@
3237F9EC20161AE000A88143 /* NSImage+Additions.m in Sources */,
4314D1411D0E0E3B004B36C9 /* SDWebImageDownloaderOperation.m in Sources */,
80377D561F2F66A700F89830 /* rescaler_neon.c in Sources */,
32F7C0762030114C00873181 /* SDWebImageTransformer.m in Sources */,
80377D551F2F66A700F89830 /* rescaler_msa.c in Sources */,
80377D5E1F2F66A700F89830 /* yuv_mips_dsp_r2.c in Sources */,
80377D611F2F66A700F89830 /* yuv.c in Sources */,
@ -3056,6 +3103,7 @@
4314D1551D0E0E3B004B36C9 /* UIImageView+HighlightedWebCache.m in Sources */,
80377E991F2F66D400F89830 /* buffer_dec.c in Sources */,
80377C201F2F666300F89830 /* quant_levels_dec_utils.c in Sources */,
32F7C07F2030719600873181 /* UIImage+Transform.m in Sources */,
80377D471F2F66A700F89830 /* lossless_enc_sse2.c in Sources */,
80377C1E1F2F666300F89830 /* huffman_utils.c in Sources */,
);
@ -3164,6 +3212,7 @@
3237F9EA20161AE000A88143 /* NSImage+Additions.m in Sources */,
431BB6B61D06D2C1006A3455 /* UIImage+WebP.m in Sources */,
80377E251F2F66A800F89830 /* rescaler_neon.c in Sources */,
32F7C0792030114C00873181 /* SDWebImageTransformer.m in Sources */,
80377E241F2F66A800F89830 /* rescaler_msa.c in Sources */,
80377E2D1F2F66A800F89830 /* yuv_mips_dsp_r2.c in Sources */,
431BB6B91D06D2C1006A3455 /* UIButton+WebCache.m in Sources */,
@ -3204,6 +3253,7 @@
80377C6E1F2F666400F89830 /* quant_levels_dec_utils.c in Sources */,
80377EC91F2F66D500F89830 /* buffer_dec.c in Sources */,
80377C6C1F2F666400F89830 /* huffman_utils.c in Sources */,
32F7C0822030719600873181 /* UIImage+Transform.m in Sources */,
80377E161F2F66A800F89830 /* lossless_enc_sse2.c in Sources */,
431BB6C71D06D2C1006A3455 /* UIImageView+HighlightedWebCache.m in Sources */,
);
@ -3348,6 +3398,7 @@
32C0FDEC2013426C001B8F2D /* SDWebImageIndicator.m in Sources */,
80377E5E1F2F66A800F89830 /* lossless_mips_dsp_r2.c in Sources */,
80377E3D1F2F66A800F89830 /* cost_sse2.c in Sources */,
32F7C0832030719600873181 /* UIImage+Transform.m in Sources */,
321E60BB1F38E90100405457 /* SDWebImageWebPCoder.m in Sources */,
80377E3C1F2F66A800F89830 /* cost_mips32.c in Sources */,
80377E421F2F66A800F89830 /* dec_mips32.c in Sources */,
@ -3355,6 +3406,7 @@
323F8B851F38EF770092B609 /* histogram_enc.c in Sources */,
80377EE51F2F66D500F89830 /* webp_dec.c in Sources */,
4397D2B01D0DDD8C00BB2784 /* SDImageCache.m in Sources */,
32F7C07A2030114C00873181 /* SDWebImageTransformer.m in Sources */,
80377E4F1F2F66A800F89830 /* enc_sse41.c in Sources */,
80377E701F2F66A800F89830 /* upsampling_sse2.c in Sources */,
);
@ -3395,6 +3447,7 @@
80377D6A1F2F66A700F89830 /* argb.c in Sources */,
323F8B7C1F38EF770092B609 /* frame_enc.c in Sources */,
80377EAB1F2F66D400F89830 /* frame_dec.c in Sources */,
32F7C0772030114C00873181 /* SDWebImageTransformer.m in Sources */,
43C8929D1D9D6DD90022038D /* anim_decode.c in Sources */,
323F8B6A1F38EF770092B609 /* delta_palettization_enc.c in Sources */,
43CE75D41CFE98E0006C64D0 /* FLAnimatedImageView+WebCache.m in Sources */,
@ -3427,6 +3480,7 @@
4A2CAE1E1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.m in Sources */,
80377EAD1F2F66D400F89830 /* io_dec.c in Sources */,
80377EAC1F2F66D400F89830 /* idec_dec.c in Sources */,
32F7C0802030719600873181 /* UIImage+Transform.m in Sources */,
323F8B981F38EF770092B609 /* near_lossless_enc.c in Sources */,
80377D6F1F2F66A700F89830 /* cost.c in Sources */,
80377EB31F2F66D400F89830 /* vp8l_dec.c in Sources */,
@ -3547,6 +3601,7 @@
323F8B7A1F38EF770092B609 /* frame_enc.c in Sources */,
80377E8B1F2F66D000F89830 /* frame_dec.c in Sources */,
43C8929A1D9D6DD70022038D /* anim_decode.c in Sources */,
32F7C0752030114C00873181 /* SDWebImageTransformer.m in Sources */,
323F8B681F38EF770092B609 /* delta_palettization_enc.c in Sources */,
43CE75D31CFE98E0006C64D0 /* FLAnimatedImageView+WebCache.m in Sources */,
323F8B5C1F38EF770092B609 /* cost_enc.c in Sources */,
@ -3579,6 +3634,7 @@
80377E8D1F2F66D000F89830 /* io_dec.c in Sources */,
80377E8C1F2F66D000F89830 /* idec_dec.c in Sources */,
323F8B961F38EF770092B609 /* near_lossless_enc.c in Sources */,
32F7C07E2030719600873181 /* UIImage+Transform.m in Sources */,
80377CE51F2F66A100F89830 /* cost.c in Sources */,
80377E931F2F66D000F89830 /* vp8l_dec.c in Sources */,
321E609A1F38E8ED00405457 /* SDWebImageImageIOCoder.m in Sources */,

View File

@ -59,6 +59,9 @@
#ifndef UIView
#define UIView NSView
#endif
#ifndef UIColor
#define UIColor NSColor
#endif
#else
#if __IPHONE_OS_VERSION_MIN_REQUIRED != 20000 && __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_5_0
#error SDWebImage doesn't support Deployment Target version < 5.0

View File

@ -0,0 +1,139 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageCompat.h"
#import "UIImage+Transform.h"
/**
A transformer protocol to transform the image load from cache or from download.
For cache, it store the transformed image to memory cache if the memory cache missed (Default).
For download, it store the transformed image to disk cache and memory cache if the download finished (Default).
@note The transform process is called from a global queue in order to not to block the main queue.
*/
@protocol SDWebImageTransformer <NSObject>
@required
/**
For each transformer, it must contains its cache key to used to store the image cache or query from the cache. This key will be appened after the original cache key generated by URL or from user.
@return The cache key to appended after the original cache key. Should not be nil.
*/
- (nonnull NSString *)transformerKey;
/**
Transform the image to another image.
@param image The image to be transformed
@param key The cache key associated to the image
@return The transformed image, or nil if transform failed
*/
- (nullable UIImage *)transformedImageWithImage:(nonnull UIImage *)image forKey:(nonnull NSString *)key;
@end
#pragma mark - Pipeline
// Pipeline transformer. Which you can bind multiple transformers together to let the image to be transformed one by one and generate the final image.
@interface SDWebImagePipelineTransformer : NSObject <SDWebImageTransformer>
@property (nonatomic, copy, readonly, nonnull) NSArray<id<SDWebImageTransformer>> *transformers;
- (nonnull instancetype)initWithTransformers:(nonnull NSArray<id<SDWebImageTransformer>> *)transformers;
- (void)addTransformer:(nonnull id<SDWebImageTransformer>)transformer;
- (void)removeTransformer:(nonnull id<SDWebImageTransformer>)transformer;
@end
// There are some build-in transformer based on the `UIImage+Transformer` category to provide the common image geometry, image blending and image effect process. Those transform are useful for static image only but you can create your own to support animated image as well.
#pragma mark - Image Geometry
// Image round corner transformer
@interface SDWebImageRoundCornerTransformer: NSObject <SDWebImageTransformer>
@property (nonatomic, assign, readonly) CGFloat cornerRadius;
@property (nonatomic, assign, readonly) SDRectCorner corners;
@property (nonatomic, assign, readonly) CGFloat borderWidth;
@property (nonatomic, strong, readonly, nullable) UIColor *borderColor;
- (nonnull instancetype)initWithRadius:(CGFloat)cornerRadius corners:(SDRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(nullable UIColor *)borderColor;
@end
// Image resizing transformer
@interface SDWebImageResizingTransformer : NSObject <SDWebImageTransformer>
@property (nonatomic, assign, readonly) CGSize size;
@property (nonatomic, assign, readonly) SDImageScaleMode scaleMode;
- (nonnull instancetype)initWithSize:(CGSize)size scaleMode:(SDImageScaleMode)scaleMode;
@end
// Image cropping transformer
@interface SDWebImageCroppingTransformer : NSObject <SDWebImageTransformer>
@property (nonatomic, assign, readonly) CGRect rect;
- (nonnull instancetype)initWithRect:(CGRect)rect;
@end
// Image flipping transformer
@interface SDWebImageFlippingTransformer : NSObject <SDWebImageTransformer>
@property (nonatomic, assign, readonly) BOOL horizontal;
@property (nonatomic, assign, readonly) BOOL vertical;
- (nonnull instancetype)initWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical;
@end
// Image rotation transformer
@interface SDWebImageRotationTransformer : NSObject <SDWebImageTransformer>
@property (nonatomic, assign, readonly) CGFloat angle;
@property (nonatomic, assign, readonly) BOOL fitSize;
- (nonnull instancetype)initWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize;
@end
#pragma mark - Image Blending
// Image tint color transformer
@interface SDWebImageTintTransformer : NSObject <SDWebImageTransformer>
@property (nonatomic, strong, readonly, nonnull) UIColor *tintColor;
- (nonnull instancetype)initWithColor:(nonnull UIColor *)tintColor;
@end
#pragma mark - Image Effect
// Image blur effect transformer
@interface SDWebImageBlurTransformer : NSObject <SDWebImageTransformer>
@property (nonatomic, assign, readonly) CGFloat blurRadius;
- (nonnull instancetype)initWithRadius:(CGFloat)blurRadius;
@end
#if SD_UIKIT || SD_MAC
// Core Image filter transformer
@interface SDWebImageFilterTransformer: NSObject <SDWebImageTransformer>
@property (nonatomic, strong, readonly, nonnull) CIFilter *filter;
- (nonnull instancetype)initWithFilter:(nonnull CIFilter *)filter;
@end
#endif

View File

@ -0,0 +1,309 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageTransformer.h"
#if SD_UIKIT || SD_MAC
#import <CoreImage/CoreImage.h>
#endif
@interface UIColor (Additions)
@property (nonatomic, copy, readonly, nonnull) NSString *sd_hexString;
@end
@implementation UIColor (Additions)
- (NSString *)sd_hexString {
CGFloat red, green, blue, alpha;
#if SD_UIKIT
if (![self getRed:&red green:&green blue:&blue alpha:&alpha]) {
[self getWhite:&red alpha:&alpha];
green = red;
blue = red;
}
#else
@try {
[self getRed:&red green:&green blue:&blue alpha:&alpha];
}
@catch (NSException *exception) {
[self getWhite:&red alpha:&alpha];
green = red;
blue = red;
}
#endif
red = roundf(red * 255.f);
green = roundf(green * 255.f);
blue = roundf(blue * 255.f);
alpha = roundf(alpha * 255.f);
uint hex = ((uint)alpha << 24) | ((uint)red << 16) | ((uint)green << 8) | ((uint)blue);
return [NSString stringWithFormat:@"0x%08x", hex];
}
@end
@interface SDWebImagePipelineTransformer ()
@property (nonatomic, copy, readwrite, nonnull) NSArray<id<SDWebImageTransformer>> *transformers;
@property (nonatomic, copy, readwrite) NSString *transformerKey;
@end
@implementation SDWebImagePipelineTransformer
- (instancetype)initWithTransformers:(NSArray<id<SDWebImageTransformer>> *)transformers {
self = [super init];
if (self) {
_transformers = [transformers copy];
_transformerKey = [[self class] cacheKeyForTransformers:transformers];
}
return self;
}
+ (NSString *)cacheKeyForTransformers:(NSArray<id<SDWebImageTransformer>> *)transformers {
if (transformers.count == 0) {
return @"";
}
NSMutableArray<NSString *> *cacheKeys = [NSMutableArray arrayWithCapacity:transformers.count];
[transformers enumerateObjectsUsingBlock:^(id<SDWebImageTransformer> _Nonnull transformer, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *cacheKey = transformer.transformerKey;
[cacheKeys addObject:cacheKey];
}];
return [cacheKeys componentsJoinedByString:@"@"];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
UIImage *transformedImage = image;
for (id<SDWebImageTransformer> transformer in self.transformers) {
transformedImage = [transformer transformedImageWithImage:transformedImage forKey:key];
}
return transformedImage;
}
- (void)addTransformer:(id<SDWebImageTransformer>)transformer {
if (!transformer) {
return;
}
self.transformers = [self.transformers arrayByAddingObject:transformer];
}
- (void)removeTransformer:(id<SDWebImageTransformer>)transformer {
if (!transformer) {
return;
}
NSMutableArray<id<SDWebImageTransformer>> *transformers = [self.transformers mutableCopy];
[transformers removeObject:transformer];
self.transformers = [transformers copy];
}
@end
@implementation SDWebImageRoundCornerTransformer
- (instancetype)initWithRadius:(CGFloat)cornerRadius corners:(SDRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(nullable UIColor *)borderColor {
self = [super init];
if (self) {
_cornerRadius = cornerRadius;
_corners = corners;
_borderWidth = borderWidth;
_borderColor = borderColor;
}
return self;
}
- (NSString *)transformerKey {
return [NSString stringWithFormat:@"SDWebImageRoundCornerTransformer(%f,%lu,%f,%@)", self.cornerRadius, (unsigned long)self.corners, self.borderWidth, self.borderColor.sd_hexString];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_roundedCornerImageWithRadius:self.cornerRadius corners:self.corners borderWidth:self.borderWidth borderColor:self.borderColor];
}
@end
@implementation SDWebImageResizingTransformer
- (instancetype)initWithSize:(CGSize)size scaleMode:(SDImageScaleMode)scaleMode {
self = [super init];
if (self) {
_size = size;
_scaleMode = scaleMode;
}
return self;
}
- (NSString *)transformerKey {
CGSize size = self.size;
return [NSString stringWithFormat:@"SDWebImageResizingTransformer({%f,%f},%lu)", size.width, size.height, (unsigned long)self.scaleMode];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_resizedImageWithSize:self.size scaleMode:self.scaleMode];
}
@end
@implementation SDWebImageCroppingTransformer
- (instancetype)initWithRect:(CGRect)rect {
self = [super init];
if (self) {
_rect = rect;
}
return self;
}
- (NSString *)transformerKey {
CGRect rect = self.rect;
return [NSString stringWithFormat:@"SDWebImageCroppingTransformer({%f,%f,%f,%f})", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_croppedImageWithRect:self.rect];
}
@end
@implementation SDWebImageFlippingTransformer
- (instancetype)initWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical {
self = [super init];
if (self) {
_horizontal = horizontal;
_vertical = vertical;
}
return self;
}
- (NSString *)transformerKey {
return [NSString stringWithFormat:@"SDWebImageFlippingTransformer(%d,%d)", self.horizontal, self.vertical];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_flippedImageWithHorizontal:self.horizontal vertical:self.vertical];
}
@end
@implementation SDWebImageRotationTransformer
- (instancetype)initWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize {
self = [super init];
if (self) {
_angle = angle;
_fitSize = fitSize;
}
return self;
}
- (NSString *)transformerKey {
return [NSString stringWithFormat:@"SDWebImageRotationTransformer(%f,%d)", self.angle, self.fitSize];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_rotatedImageWithAngle:self.angle fitSize:self.fitSize];
}
@end
#pragma mark - Image Blending
@implementation SDWebImageTintTransformer
- (instancetype)initWithColor:(UIColor *)tintColor {
self = [super init];
if (self) {
_tintColor = tintColor;
}
return self;
}
- (NSString *)transformerKey {
return [NSString stringWithFormat:@"SDWebImageTintTransformer(%@)", self.tintColor.sd_hexString];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_tintedImageWithColor:self.tintColor];
}
@end
#pragma mark - Image Effect
@implementation SDWebImageBlurTransformer
- (instancetype)initWithRadius:(CGFloat)blurRadius {
self = [super init];
if (self) {
_blurRadius = blurRadius;
}
return self;
}
- (NSString *)transformerKey {
return [NSString stringWithFormat:@"SDWebImageBlurTransformer(%f)", self.blurRadius];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_blurredImageWithRadius:self.blurRadius];
}
@end
#if SD_UIKIT || SD_MAC
@implementation SDWebImageFilterTransformer
- (instancetype)initWithFilter:(CIFilter *)filter {
self = [super init];
if (self) {
_filter = filter;
}
return self;
}
- (NSString *)transformerKey {
return [NSString stringWithFormat:@"SDWebImageFilterTransformer(%@)", self.filter.name];
}
- (UIImage *)transformedImageWithImage:(UIImage *)image forKey:(NSString *)key {
if (!image) {
return nil;
}
return [image sd_filteredImageWithFilter:self.filter];
}
@end
#endif

View File

@ -0,0 +1,126 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageCompat.h"
typedef NS_ENUM(NSUInteger, SDImageScaleMode) {
SDImageScaleModeFill = 0,
SDImageScaleModeAspectFit = 1,
SDImageScaleModeAspectFill = 2
};
#if SD_UIKIT || SD_WATCH
typedef UIRectCorner SDRectCorner;
#else
typedef NS_OPTIONS(NSUInteger, SDRectCorner) {
SDRectCornerTopLeft = 1 << 0,
SDRectCornerTopRight = 1 << 1,
SDRectCornerBottomLeft = 1 << 2,
SDRectCornerBottomRight = 1 << 3,
SDRectCornerAllCorners = ~0UL
};
#endif
/**
Provide some commen method for `UIImage`.
Image process is based on Core Graphics and vImage.
*/
@interface UIImage (Transform)
#pragma mark - Image Geometry
/**
Returns a new image which is scaled from this image.
The image content will be changed with the scale mode.
@param size The new size to be scaled, values should be positive.
@param contentMode The scale mode for image content.
@return The new image with the given size.
*/
- (nullable UIImage *)sd_resizedImageWithSize:(CGSize)size scaleMode:(SDImageScaleMode)scaleMode;
/**
Returns a new image which is cropped from this image.
@param cropRect Image's inner rect.
@return The new image with the cropping rect.
*/
- (nullable UIImage *)sd_croppedImageWithRect:(CGRect)rect;
/**
Rounds a new image with a given corner radius and corners.
@param cornerRadius The radius of each corner oval. Values larger than half the
rectangle's width or height are clamped appropriately to
half the width or height.
@param corners A bitmask value that identifies the corners that you want
rounded. You can use this parameter to round only a subset
of the corners of the rectangle.
@param borderWidth The inset border line width. Values larger than half the rectangle's
width or height are clamped appropriately to half the width
or height.
@param borderColor The border stroke color. nil means clear color.
@return The new image with the round corner.
*/
- (nullable UIImage *)sd_roundedCornerImageWithRadius:(CGFloat)cornerRadius
corners:(SDRectCorner)corners
borderWidth:(CGFloat)borderWidth
borderColor:(nullable UIColor *)borderColor;
/**
Returns a new rotated image (relative to the center).
@param radians Rotated radians in counterclockwise.
@param fitSize YES: new image's size is extend to fit all content.
NO: image's size will not change, content may be clipped.
@return The new image with the rotation.
*/
- (nullable UIImage *)sd_rotatedImageWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize;
/**
Returns a new horizontally(vertically) flipped image.
@param horizontal YES to flip the image horizontally.
@param vertical YES to flip the image vertically.
@return The new image with the flipping.
*/
- (nullable UIImage *)sd_flippedImageWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical;
#pragma mark - Image Blending
/**
Return a tinted image in alpha channel with the given color.
@param tintColor The color.
@return The new image with the tint color.
*/
- (nullable UIImage *)sd_tintedImageWithColor:(nonnull UIColor *)tintColor;
#pragma mark - Image Effect
/**
Return a new image applied a blur effect.
@param blurRadius The radius of the blur in points, 0 means no blur effect.
@return The new image with blur effect, or nil if an error occurs (e.g. no enough memory).
*/
- (nullable UIImage *)sd_blurredImageWithRadius:(CGFloat)blurRadius;
#if SD_UIKIT || SD_MAC
/**
Return a new image applied a CIFilter.
@param filter The CIFilter to be applied to the image.
@return The new image with the CIFilter, or nil if an error occurs (e.g. no
enough memory).
*/
- (nullable UIImage *)sd_filteredImageWithFilter:(nonnull CIFilter *)filter;
#endif
@end

View File

@ -0,0 +1,505 @@
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "UIImage+Transform.h"
#import "NSImage+Additions.h"
#import <Accelerate/Accelerate.h>
#if SD_UIKIT || SD_MAC
#import <CoreImage/CoreImage.h>
#endif
#ifndef SD_SWAP // swap two value
#define SD_SWAP(_a_, _b_) do { __typeof__(_a_) _tmp_ = (_a_); (_a_) = (_b_); (_b_) = _tmp_; } while (0)
#endif
#if SD_MAC
static CGContextRef SDCGContextCreateARGBBitmapContext(CGSize size, BOOL opaque, CGFloat scale) {
size_t width = ceil(size.width * scale);
size_t height = ceil(size.height * scale);
if (width < 1 || height < 1) return NULL;
//pre-multiplied ARGB, 8-bits per component
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGImageAlphaInfo alphaInfo = (opaque ? kCGImageAlphaNoneSkipFirst : kCGImageAlphaPremultipliedFirst);
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, kCGBitmapByteOrderDefault | alphaInfo);
CGColorSpaceRelease(space);
return context;
}
#endif
static void SDGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) {
#if SD_UIKIT || SD_WATCH
UIGraphicsBeginImageContextWithOptions(size, opaque, scale);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, 0, -size.height);
CGContextScaleCTM(context, scale, -scale);
#else
CGContextRef context = SDCGContextCreateARGBBitmapContext(size, opaque, scale);
if (!context) {
return;
}
NSGraphicsContext *graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
CGContextRelease(context);
[NSGraphicsContext saveGraphicsState];
NSGraphicsContext.currentContext = graphicsContext;
#endif
}
static CGContextRef SDGraphicsGetCurrentContext(void) {
#if SD_UIKIT || SD_WATCH
return UIGraphicsGetCurrentContext();
#else
return NSGraphicsContext.currentContext.CGContext;
#endif
}
static void SDGraphicsEndImageContext(void) {
#if SD_UIKIT || SD_WATCH
UIGraphicsEndImageContext();
#else
[NSGraphicsContext restoreGraphicsState];
#endif
}
static UIImage * SDGraphicsGetImageFromCurrentImageContext(void) {
#if SD_UIKIT || SD_WATCH
return UIGraphicsGetImageFromCurrentImageContext();
#else
CGContextRef context = SDGraphicsGetCurrentContext();
if (!context) {
return nil;
}
CGImageRef imageRef = CGBitmapContextCreateImage(context);
if (!imageRef) {
return nil;
}
NSImage *image = [[NSImage alloc] initWithCGImage:imageRef size:NSZeroSize];
CGImageRelease(imageRef);
return image;
#endif
}
static SDRectCorner SDRectCornerConvertCounterClockwise(SDRectCorner corners) {
#if SD_UIKIT || SD_WATCH
if (corners != UIRectCornerAllCorners) {
UIRectCorner tmp = 0;
if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft;
if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight;
if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft;
if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight;
corners = tmp;
}
#else
if (corners != SDRectCornerAllCorners) {
SDRectCorner tmp = 0;
if (corners & SDRectCornerTopLeft) tmp |= SDRectCornerBottomLeft;
if (corners & SDRectCornerTopRight) tmp |= SDRectCornerBottomRight;
if (corners & SDRectCornerBottomLeft) tmp |= SDRectCornerTopLeft;
if (corners & SDRectCornerBottomRight) tmp |= SDRectCornerTopRight;
corners = tmp;
}
#endif
return corners;
}
static CGRect SDCGRectFitWithScaleMode(CGRect rect, CGSize size, SDImageScaleMode scaleMode) {
rect = CGRectStandardize(rect);
size.width = size.width < 0 ? -size.width : size.width;
size.height = size.height < 0 ? -size.height : size.height;
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
switch (scaleMode) {
case SDImageScaleModeAspectFit:
case SDImageScaleModeAspectFill: {
if (rect.size.width < 0.01 || rect.size.height < 0.01 ||
size.width < 0.01 || size.height < 0.01) {
rect.origin = center;
rect.size = CGSizeZero;
} else {
CGFloat scale;
if (scaleMode == SDImageScaleModeAspectFit) {
if (size.width / size.height < rect.size.width / rect.size.height) {
scale = rect.size.height / size.height;
} else {
scale = rect.size.width / size.width;
}
} else {
if (size.width / size.height < rect.size.width / rect.size.height) {
scale = rect.size.width / size.width;
} else {
scale = rect.size.height / size.height;
}
}
size.width *= scale;
size.height *= scale;
rect.size = size;
rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5);
}
} break;
case SDImageScaleModeFill:
default: {
rect = rect;
}
}
return rect;
}
#if SD_MAC
@interface NSBezierPath (Compatibility)
+ (instancetype)bezierPathWithRoundedRect:(NSRect)rect byRoundingCorners:(SDRectCorner)corners cornerRadius:(CGFloat)cornerRadius;
@end
@implementation NSBezierPath (Compatibility)
+ (instancetype)bezierPathWithRoundedRect:(NSRect)rect byRoundingCorners:(SDRectCorner)corners cornerRadius:(CGFloat)cornerRadius {
NSBezierPath *path = [NSBezierPath bezierPath];
CGFloat maxCorner = MIN(NSWidth(rect), NSHeight(rect)) / 2;
CGFloat topLeftRadius = MIN(maxCorner, (corners & SDRectCornerTopLeft) ? cornerRadius : 0);
CGFloat topRightRadius = MIN(maxCorner, (corners & SDRectCornerTopRight) ? cornerRadius : 0);
CGFloat bottomLeftRadius = MIN(maxCorner, (corners & SDRectCornerBottomLeft) ? cornerRadius : 0);
CGFloat bottomRightRadius = MIN(maxCorner, (corners & SDRectCornerBottomRight) ? cornerRadius : 0);
NSPoint topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect));
NSPoint topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
NSPoint bottomLeft = NSMakePoint(NSMinX(rect), NSMinY(rect));
NSPoint bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect));
[path moveToPoint:NSMakePoint(NSMidX(rect), NSMaxY(rect))];
[path appendBezierPathWithArcFromPoint:topLeft toPoint:bottomLeft radius:topLeftRadius];
[path appendBezierPathWithArcFromPoint:bottomLeft toPoint:bottomRight radius:bottomLeftRadius];
[path appendBezierPathWithArcFromPoint:bottomRight toPoint:topRight radius:bottomRightRadius];
[path appendBezierPathWithArcFromPoint:topRight toPoint:topLeft radius:topRightRadius];
[path closePath];
return path;
}
@end
#endif
@implementation UIImage (Transform)
- (void)sd_drawInRect:(CGRect)rect withScaleMode:(SDImageScaleMode)scaleMode clipsToBounds:(BOOL)clips {
CGRect drawRect = SDCGRectFitWithScaleMode(rect, self.size, scaleMode);
if (drawRect.size.width == 0 || drawRect.size.height == 0) return;
if (clips) {
CGContextRef context = SDGraphicsGetCurrentContext();
if (context) {
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
[self drawInRect:drawRect];
CGContextRestoreGState(context);
}
} else {
[self drawInRect:drawRect];
}
}
- (UIImage *)sd_resizedImageWithSize:(CGSize)size scaleMode:(SDImageScaleMode)scaleMode {
if (size.width <= 0 || size.height <= 0) return nil;
SDGraphicsBeginImageContextWithOptions(size, NO, self.scale);
[self sd_drawInRect:CGRectMake(0, 0, size.width, size.height) withScaleMode:scaleMode clipsToBounds:NO];
UIImage *image = SDGraphicsGetImageFromCurrentImageContext();
SDGraphicsEndImageContext();
return image;
}
- (UIImage *)sd_croppedImageWithRect:(CGRect)rect {
if (!self.CGImage) return nil;
rect.origin.x *= self.scale;
rect.origin.y *= self.scale;
rect.size.width *= self.scale;
rect.size.height *= self.scale;
if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
if (!imageRef) {
return nil;
}
#if SD_UIKIT || SD_WATCH
UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
#else
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:self.scale];
#endif
CGImageRelease(imageRef);
return image;
}
- (UIImage *)sd_roundedCornerImageWithRadius:(CGFloat)cornerRadius corners:(SDRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor {
if (!self.CGImage) return nil;
corners = SDRectCornerConvertCounterClockwise(corners);
SDGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGContextRef context = SDGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
CGFloat minSize = MIN(self.size.width, self.size.height);
if (borderWidth < minSize / 2) {
#if SD_UIKIT || SD_WATCH
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
#else
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadius:cornerRadius];
#endif
[path closePath];
CGContextSaveGState(context);
[path addClip];
CGContextDrawImage(context, rect, self.CGImage);
CGContextRestoreGState(context);
}
if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) {
CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale;
CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset);
CGFloat strokeRadius = cornerRadius > self.scale / 2 ? cornerRadius - self.scale / 2 : 0;
#if SD_UIKIT || SD_WATCH
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, strokeRadius)];
#else
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadius:strokeRadius];
#endif
[path closePath];
path.lineWidth = borderWidth;
[borderColor setStroke];
[path stroke];
}
UIImage *image = SDGraphicsGetImageFromCurrentImageContext();
SDGraphicsEndImageContext();
return image;
}
- (UIImage *)sd_rotatedImageWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize {
if (!self.CGImage) return nil;
size_t width = (size_t)CGImageGetWidth(self.CGImage);
size_t height = (size_t)CGImageGetHeight(self.CGImage);
CGRect newRect = CGRectApplyAffineTransform(CGRectMake(0., 0., width, height),
fitSize ? CGAffineTransformMakeRotation(angle) : CGAffineTransformIdentity);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL,
(size_t)newRect.size.width,
(size_t)newRect.size.height,
8,
(size_t)newRect.size.width * 4,
colorSpace,
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (!context) return nil;
CGContextSetShouldAntialias(context, true);
CGContextSetAllowsAntialiasing(context, true);
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
CGContextTranslateCTM(context, +(newRect.size.width * 0.5), +(newRect.size.height * 0.5));
CGContextRotateCTM(context, angle);
CGContextDrawImage(context, CGRectMake(-(width * 0.5), -(height * 0.5), width, height), self.CGImage);
CGImageRef imgRef = CGBitmapContextCreateImage(context);
#if SD_UIKIT || SD_WATCH
UIImage *img = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
#else
UIImage *img = [[UIImage alloc] initWithCGImage:imgRef scale:self.scale];
#endif
CGImageRelease(imgRef);
CGContextRelease(context);
return img;
}
- (UIImage *)sd_flippedImageWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical {
if (!self.CGImage) return nil;
size_t width = (size_t)CGImageGetWidth(self.CGImage);
size_t height = (size_t)CGImageGetHeight(self.CGImage);
size_t bytesPerRow = width * 4;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (!context) return nil;
CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
UInt8 *data = (UInt8 *)CGBitmapContextGetData(context);
if (!data) {
CGContextRelease(context);
return nil;
}
vImage_Buffer src = { data, height, width, bytesPerRow };
vImage_Buffer dest = { data, height, width, bytesPerRow };
if (vertical) {
vImageVerticalReflect_ARGB8888(&src, &dest, kvImageBackgroundColorFill);
}
if (horizontal) {
vImageHorizontalReflect_ARGB8888(&src, &dest, kvImageBackgroundColorFill);
}
CGImageRef imgRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
#if SD_UIKIT || SD_WATCH
UIImage *img = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
#else
UIImage *img = [[UIImage alloc] initWithCGImage:imgRef scale:self.scale];
#endif
CGImageRelease(imgRef);
return img;
}
#pragma mark - Image Blending
- (UIImage *)sd_tintedImageWithColor:(UIColor *)tintColor {
if (!self.CGImage) return nil;
if (!tintColor.CGColor) return nil;
BOOL hasTint = CGColorGetAlpha(tintColor.CGColor) > __FLT_EPSILON__;
if (!hasTint) {
#if SD_UIKIT || SD_WATCH
return [UIImage imageWithCGImage:self.CGImage scale:self.scale orientation:self.imageOrientation];
#else
return [[UIImage alloc] initWithCGImage:self.CGImage scale:self.scale];
#endif
}
CGSize size = self.size;
CGRect rect = { CGPointZero, size };
CGFloat scale = self.scale;
// blend mode, see https://en.wikipedia.org/wiki/Alpha_compositing
CGBlendMode blendMode = kCGBlendModeSourceAtop;
SDGraphicsBeginImageContextWithOptions(size, NO, scale);
CGContextRef context = SDGraphicsGetCurrentContext();
CGContextDrawImage(context, rect, self.CGImage);
CGContextSetBlendMode(context, blendMode);
CGContextSetFillColorWithColor(context, tintColor.CGColor);
CGContextFillRect(context, rect);
UIImage *image = SDGraphicsGetImageFromCurrentImageContext();
SDGraphicsEndImageContext();
return image;
}
#pragma mark - Image Effect
- (UIImage *)sd_blurredImageWithRadius:(CGFloat)blurRadius {
if (self.size.width < 1 || self.size.height < 1) {
return nil;
}
if (!self.CGImage) {
return nil;
}
BOOL hasBlur = blurRadius > __FLT_EPSILON__;
if (!hasBlur) {
return self;
}
CGFloat scale = self.scale;
CGImageRef imageRef = self.CGImage;
vImage_Buffer effect = {}, scratch = {};
vImage_Buffer *input = NULL, *output = NULL;
vImage_CGImageFormat format = {
.bitsPerComponent = 8,
.bitsPerPixel = 32,
.colorSpace = NULL,
.bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little, //requests a BGRA buffer.
.version = 0,
.decode = NULL,
.renderingIntent = kCGRenderingIntentDefault
};
vImage_Error err;
err = vImageBuffer_InitWithCGImage(&effect, &format, NULL, imageRef, kvImagePrintDiagnosticsToConsole);
if (err != kvImageNoError) {
NSLog(@"UIImage+Transform error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", err, self);
return nil;
}
err = vImageBuffer_Init(&scratch, effect.height, effect.width, format.bitsPerPixel, kvImageNoFlags);
if (err != kvImageNoError) {
NSLog(@"UIImage+Transform error: vImageBuffer_Init returned error code %zi for inputImage: %@", err, self);
return nil;
}
input = &effect;
output = &scratch;
if (hasBlur) {
// A description of how to compute the box kernel width from the Gaussian
// radius (aka standard deviation) appears in the SVG spec:
// http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
//
// For larger values of 's' (s >= 2.0), an approximation can be used: Three
// successive box-blurs build a piece-wise quadratic convolution kernel, which
// approximates the Gaussian kernel to within roughly 3%.
//
// let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
//
// ... if d is odd, use three box-blurs of size 'd', centered on the output pixel.
//
CGFloat inputRadius = blurRadius * scale;
if (inputRadius - 2.0 < __FLT_EPSILON__) inputRadius = 2.0;
uint32_t radius = floor((inputRadius * 3.0 * sqrt(2 * M_PI) / 4 + 0.5) / 2);
radius |= 1; // force radius to be odd so that the three box-blur methodology works.
int iterations;
if (blurRadius * scale < 0.5) iterations = 1;
else if (blurRadius * scale < 1.5) iterations = 2;
else iterations = 3;
NSInteger tempSize = vImageBoxConvolve_ARGB8888(input, output, NULL, 0, 0, radius, radius, NULL, kvImageGetTempBufferSize | kvImageEdgeExtend);
void *temp = malloc(tempSize);
for (int i = 0; i < iterations; i++) {
vImageBoxConvolve_ARGB8888(input, output, temp, 0, 0, radius, radius, NULL, kvImageEdgeExtend);
SD_SWAP(input, output);
}
free(temp);
}
CGImageRef effectCGImage = NULL;
effectCGImage = vImageCreateCGImageFromBuffer(input, &format, NULL, NULL, kvImageNoAllocate, NULL);
if (effectCGImage == NULL) {
effectCGImage = vImageCreateCGImageFromBuffer(input, &format, NULL, NULL, kvImageNoFlags, NULL);
free(input->data);
}
free(output->data);
#if SD_UIKIT || SD_WATCH
UIImage *outputImage = [UIImage imageWithCGImage:effectCGImage scale:self.scale orientation:self.imageOrientation];
#else
UIImage *outputImage = [[UIImage alloc] initWithCGImage:effectCGImage scale:self.scale];
#endif
CGImageRelease(effectCGImage);
return outputImage;
}
#if SD_UIKIT || SD_MAC
- (UIImage *)sd_filteredImageWithFilter:(CIFilter *)filter {
if (!self.CGImage) return nil;
CIContext *context = [CIContext context];
CIImage *inputImage = [CIImage imageWithCGImage:self.CGImage];
if (!inputImage) return nil;
[filter setValue:inputImage forKey:kCIInputImageKey];
CIImage *outputImage = filter.outputImage;
if (!outputImage) return nil;
CGImageRef imageRef = [context createCGImage:outputImage fromRect:outputImage.extent];
if (!imageRef) return nil;
#if SD_UIKIT
UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
#else
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:self.scale];
#endif
CGImageRelease(imageRef);
return image;
}
#endif
@end