170 lines
5.1 KiB
Objective-C
170 lines
5.1 KiB
Objective-C
/*
|
|
Copyright 2011 Twitter, Inc.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this work except in compliance with the License.
|
|
You may obtain a copy of the License in the LICENSE file, or at:
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#import "TUIKit.h"
|
|
#import "TUIImage+Drawing.h"
|
|
|
|
@implementation TUIImage (Drawing)
|
|
|
|
+ (TUIImage *)imageWithSize:(CGSize)size drawing:(void(^)(CGContextRef))draw
|
|
{
|
|
if(size.width < 1 || size.height < 1)
|
|
return nil;
|
|
|
|
CGContextRef ctx = TUICreateGraphicsContextWithOptions(size, NO);
|
|
draw(ctx);
|
|
TUIImage *i = TUIGraphicsContextGetImage(ctx);
|
|
CGContextRelease(ctx);
|
|
return i;
|
|
}
|
|
|
|
- (TUIImage *)scale:(CGSize)size
|
|
{
|
|
return [TUIImage imageWithSize:size drawing:^(CGContextRef ctx) {
|
|
CGRect r;
|
|
r.origin = CGPointZero;
|
|
r.size = size;
|
|
CGContextDrawImage(ctx, r, self.CGImage);
|
|
}];
|
|
}
|
|
|
|
- (TUIImage *)crop:(CGRect)cropRect
|
|
{
|
|
if((cropRect.size.width < 1) || (cropRect.size.height < 1))
|
|
return nil;
|
|
|
|
CGSize s = self.size;
|
|
CGFloat mx = cropRect.origin.x + cropRect.size.width;
|
|
CGFloat my = cropRect.origin.y + cropRect.size.height;
|
|
if((cropRect.origin.x >= 0.0) && (cropRect.origin.y >= 0.0) && (mx <= s.width) && (my <= s.height)) {
|
|
// fast crop
|
|
CGImageRef cgimage = CGImageCreateWithImageInRect(self.CGImage, cropRect);
|
|
if(!cgimage) {
|
|
NSLog(@"CGImageCreateWithImageInRect failed %@ %@", NSStringFromRect(cropRect), NSStringFromSize(s));
|
|
return nil;
|
|
}
|
|
TUIImage *i = [TUIImage imageWithCGImage:cgimage];
|
|
CGImageRelease(cgimage);
|
|
return i;
|
|
} else {
|
|
// slow crop - probably doing pad
|
|
return [TUIImage imageWithSize:cropRect.size drawing:^(CGContextRef ctx) {
|
|
CGRect imageRect;
|
|
imageRect.origin.x = -cropRect.origin.x;
|
|
imageRect.origin.y = -cropRect.origin.y;
|
|
imageRect.size = s;
|
|
CGContextDrawImage(ctx, imageRect, self.CGImage);
|
|
}];
|
|
}
|
|
}
|
|
|
|
- (TUIImage *)upsideDownCrop:(CGRect)cropRect
|
|
{
|
|
CGSize s = self.size;
|
|
cropRect.origin.y = s.height - (cropRect.origin.y + cropRect.size.height);
|
|
return [self crop:cropRect];
|
|
}
|
|
|
|
- (TUIImage *)thumbnail:(CGSize)newSize
|
|
{
|
|
CGSize s = self.size;
|
|
float oldProp = s.width / s.height;
|
|
float newProp = newSize.width / newSize.height;
|
|
CGRect cropRect;
|
|
if (oldProp > newProp) {
|
|
cropRect.size.height = s.height;
|
|
cropRect.size.width = s.height * newProp;
|
|
} else {
|
|
cropRect.size.width = s.width;
|
|
cropRect.size.height = s.width / newProp;
|
|
}
|
|
cropRect.origin = CGPointMake((s.width - cropRect.size.width) / 2.0, (s.height - cropRect.size.height) / 2.0);
|
|
return [[self crop:cropRect] scale:newSize];
|
|
}
|
|
|
|
- (TUIImage *)pad:(CGFloat)padding
|
|
{
|
|
CGSize s = self.size;
|
|
return [self crop:CGRectMake(-padding, -padding, s.width + padding*2, s.height + padding*2)];
|
|
}
|
|
|
|
- (TUIImage *)roundImage:(CGFloat)radius
|
|
{
|
|
CGRect r;
|
|
r.origin = CGPointZero;
|
|
r.size = self.size;
|
|
return [TUIImage imageWithSize:r.size drawing:^(CGContextRef ctx) {
|
|
CGContextClipToRoundRect(ctx, r, radius);
|
|
CGContextDrawImage(ctx, r, self.CGImage);
|
|
}];
|
|
}
|
|
|
|
- (TUIImage *)invertedMask
|
|
{
|
|
CGSize s = self.size;
|
|
return [TUIImage imageWithSize:s drawing:^(CGContextRef ctx) {
|
|
CGRect rect = CGRectMake(0, 0, s.width, s.height);
|
|
CGContextSetRGBFillColor(ctx, 0, 0, 0, 1);
|
|
CGContextFillRect(ctx, rect);
|
|
CGContextSaveGState(ctx);
|
|
CGContextClipToMask(ctx, rect, self.CGImage);
|
|
CGContextClearRect(ctx, rect);
|
|
CGContextRestoreGState(ctx);
|
|
}];
|
|
}
|
|
|
|
- (TUIImage *)innerShadowWithOffset:(CGSize)offset radius:(CGFloat)radius color:(TUIColor *)color backgroundColor:(TUIColor *)backgroundColor
|
|
{
|
|
CGFloat padding = ceil(radius);
|
|
self = [self pad:padding];
|
|
self = [TUIImage imageWithSize:self.size drawing:^(CGContextRef ctx) {
|
|
CGContextSaveGState(ctx);
|
|
CGRect r = CGRectMake(0, 0, self.size.width, self.size.height);
|
|
CGContextClipToMask(ctx, r, self.CGImage); // clip to image
|
|
CGContextSetShadowWithColor(ctx, offset, radius, color.CGColor);
|
|
CGContextBeginTransparencyLayer(ctx, NULL);
|
|
{
|
|
CGContextClipToMask(ctx, r, [[self invertedMask] CGImage]); // clip to inverted
|
|
CGContextSetFillColorWithColor(ctx, backgroundColor.CGColor);
|
|
CGContextFillRect(ctx, r); // draw with shadow
|
|
}
|
|
CGContextEndTransparencyLayer(ctx);
|
|
CGContextRestoreGState(ctx);
|
|
}];
|
|
self = [self pad:-padding];
|
|
return self;
|
|
}
|
|
|
|
- (TUIImage *)embossMaskWithOffset:(CGSize)offset
|
|
{
|
|
CGFloat padding = MAX(offset.width, offset.height) + 1;
|
|
self = [self pad:padding];
|
|
CGSize s = self.size;
|
|
self = [TUIImage imageWithSize:s drawing:^(CGContextRef ctx) {
|
|
CGContextSaveGState(ctx);
|
|
CGRect r = CGRectMake(0, 0, s.width, s.height);
|
|
CGContextClipToMask(ctx, r, [self CGImage]);
|
|
CGContextClipToMask(ctx, CGRectOffset(r, offset.width, offset.height), [[self invertedMask] CGImage]);
|
|
CGContextSetRGBFillColor(ctx, 0, 0, 0, 1);
|
|
CGContextFillRect(ctx, r);
|
|
CGContextRestoreGState(ctx);
|
|
}];
|
|
self = [self pad:-padding];
|
|
return self;
|
|
}
|
|
|
|
@end
|