Skip to content

Commit

Permalink
Merge pull request twitter-archive#24 from github/remove-tuiimage
Browse files Browse the repository at this point in the history
Kill TUIImage
  • Loading branch information
joshaber committed Jul 26, 2012
2 parents 01f7363 + f23343f commit 70bed0d
Show file tree
Hide file tree
Showing 24 changed files with 485 additions and 737 deletions.
2 changes: 1 addition & 1 deletion ExampleProject/ConcordeExample/ExampleScrollView.m
Expand Up @@ -28,7 +28,7 @@ - (id)initWithFrame:(CGRect)frame
_scrollView.scrollIndicatorStyle = TUIScrollViewIndicatorStyleDark;
[self addSubview:_scrollView];

TUIImageView *imageView = [[TUIImageView alloc] initWithImage:[TUIImage imageNamed:@"large-image.jpeg"]];
TUIImageView *imageView = [[TUIImageView alloc] initWithImage:[NSImage imageNamed:@"large-image.jpeg"]];
[_scrollView addSubview:imageView];
[_scrollView setContentSize:imageView.frame.size];

Expand Down
17 changes: 10 additions & 7 deletions ExampleProject/ConcordeExample/ExampleView.m
Expand Up @@ -67,35 +67,38 @@ Note by default scroll views (and therefore table views) don't
CGRect b = v.bounds;
CGContextRef ctx = TUIGraphicsGetCurrentContext();

TUIImage *image = [TUIImage imageNamed:@"clock.png" cache:YES];
NSImage *image = [NSImage imageNamed:@"clock.png"];
CGRect imageRect = ABIntegralRectWithSizeCenteredInRect([image size], b);

if([v.nsView isTrackingSubviewOfView:v]) { // simple way to check if the mouse is currently down inside of 'v'. See the other methods in TUINSView for more.

// first draw a slight white emboss below
CGContextSaveGState(ctx);
CGContextClipToMask(ctx, CGRectOffset(imageRect, 0, -1), image.CGImage);

CGImageRef cgImage = [image CGImageForProposedRect:&imageRect context:nil hints:nil];
CGContextClipToMask(ctx, CGRectOffset(imageRect, 0, -1), cgImage);

CGContextSetRGBFillColor(ctx, 1, 1, 1, 0.5);
CGContextFillRect(ctx, b);
CGContextRestoreGState(ctx);

// replace image with a dynamically generated fancy inset image
// 1. use the image as a mask to draw a blue gradient
// 2. generate an inner shadow image based on the mask, then overlay that on top
image = [TUIImage imageWithSize:imageRect.size drawing:^(CGContextRef ctx) {
image = [NSImage tui_imageWithSize:imageRect.size drawing:^(CGContextRef ctx) {
CGRect r;
r.origin = CGPointZero;
r.size = imageRect.size;

CGContextClipToMask(ctx, r, image.CGImage);
CGContextClipToMask(ctx, r, image.tui_CGImage);
CGContextDrawLinearGradientBetweenPoints(ctx, CGPointMake(0, r.size.height), (CGFloat[]){0,0,1,1}, CGPointZero, (CGFloat[]){0,0.6,1,1});
TUIImage *innerShadow = [image innerShadowWithOffset:CGSizeMake(0, -1) radius:3.0 color:[TUIColor blackColor] backgroundColor:[TUIColor cyanColor]];
NSImage *innerShadow = [image tui_innerShadowWithOffset:CGSizeMake(0, -1) radius:3.0 color:[TUIColor blackColor] backgroundColor:[TUIColor cyanColor]];
CGContextSetBlendMode(ctx, kCGBlendModeOverlay);
CGContextDrawImage(ctx, r, innerShadow.CGImage);
CGContextDrawImage(ctx, r, innerShadow.tui_CGImage);
}];
}

[image drawInRect:imageRect]; // draw 'image' (might be the regular one, or the dynamically generated one)
[image drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; // draw 'image' (might be the regular one, or the dynamically generated one)

// draw the index
TUIAttributedString *s = [TUIAttributedString stringWithString:[NSString stringWithFormat:@"%ld", v.tag]];
Expand Down
70 changes: 39 additions & 31 deletions TwUI.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions lib/UIKit/NSImage+TUIExtensions.h
@@ -0,0 +1,68 @@
/*
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 <Cocoa/Cocoa.h>
#import "TUIGeometry.h"

@class TUIColor;
@class TUIStretchableImage;

@interface NSImage (TUIExtensions)

+ (NSImage *)tui_imageWithCGImage:(CGImageRef)cgImage;
+ (NSImage *)tui_imageWithSize:(CGSize)size drawing:(void (^)(CGContextRef))draw; // thread safe

/*
* Returns a CGImageRef corresponding to the receiver.
*
* This should only be used with bitmaps. For vector images, use
* -CGImageForProposedRect:context:hints instead.
*/
@property (nonatomic, readonly) CGImageRef tui_CGImage;

/*
* Similar to -CGImageForProposedRect:context:hints:, but accepts a CGContextRef
* instead.
*/
- (CGImageRef)tui_CGImageForProposedRect:(CGRect *)rectPtr CGContext:(CGContextRef)context;

/*
* Draws the whole image originating at the given point.
*/
- (void)tui_drawAtPoint:(CGPoint)point;

/*
* Draws the whole image into the given rectangle.
*/
- (void)tui_drawInRect:(CGRect)rect;

/*
* Creates and returns a new image, based on the receiver, that has the
* specified end cap insets.
*/
- (TUIStretchableImage *)tui_resizableImageWithCapInsets:(TUIEdgeInsets)insets;

- (NSImage *)tui_crop:(CGRect)cropRect;
- (NSImage *)tui_upsideDownCrop:(CGRect)cropRect;
- (NSImage *)tui_scale:(CGSize)size;
- (NSImage *)tui_thumbnail:(CGSize)size;
- (NSImage *)tui_pad:(CGFloat)padding; // can be negative (to crop to center)
- (NSImage *)tui_roundImage:(CGFloat)radius;
- (NSImage *)tui_invertedMask;
- (NSImage *)tui_embossMaskWithOffset:(CGSize)offset; // subtract reciever from itself offset by 'offset', use as a mask to draw emboss
- (NSImage *)tui_innerShadowWithOffset:(CGSize)offset radius:(CGFloat)radius color:(TUIColor *)color backgroundColor:(TUIColor *)backgroundColor; // 'backgroundColor' is used as the color the shadow is drawn with, it is mostly masked out, but a halo will remain, leading to artifacts unless it is close enough to the background color

@end
107 changes: 71 additions & 36 deletions lib/UIKit/TUIImage+Drawing.m → lib/UIKit/NSImage+TUIExtensions.m
Expand Up @@ -14,35 +14,70 @@
limitations under the License.
*/

#import "TUIImage+Drawing.h"
#import "NSImage+TUIExtensions.h"
#import "TUICGAdditions.h"
#import "TUIColor.h"
#import "TUIStretchableImage.h"

@implementation TUIImage (Drawing)
@implementation NSImage (TUIExtensions)

+ (TUIImage *)imageWithSize:(CGSize)size drawing:(void(^)(CGContextRef))draw
+ (NSImage *)tui_imageWithCGImage:(CGImageRef)cgImage {
CGSize size = CGSizeMake(CGImageGetWidth(cgImage), CGImageGetHeight(cgImage));
return [[self alloc] initWithCGImage:cgImage size:size];
}

+ (NSImage *)tui_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);
NSImage *i = TUIGraphicsContextGetImage(ctx);
CGContextRelease(ctx);
return i;
}

- (TUIImage *)scale:(CGSize)size
- (CGImageRef)tui_CGImage
{
return [self CGImageForProposedRect:NULL context:nil hints:nil];
}

- (CGImageRef)tui_CGImageForProposedRect:(CGRect *)rectPtr CGContext:(CGContextRef)context
{
NSGraphicsContext *graphicsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
return [self CGImageForProposedRect:rectPtr context:graphicsContext hints:nil];
}

- (void)tui_drawAtPoint:(CGPoint)point
{
[self drawAtPoint:point fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}

- (void)tui_drawInRect:(CGRect)rect
{
[self drawInRect:rect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
}

- (TUIStretchableImage *)tui_resizableImageWithCapInsets:(TUIEdgeInsets)insets {
TUIStretchableImage *image = [[TUIStretchableImage alloc] init];
[image addRepresentations:self.representations];

image.capInsets = insets;
return image;
}

- (NSImage *)tui_scale:(CGSize)size
{
return [TUIImage imageWithSize:size drawing:^(CGContextRef ctx) {
return [NSImage tui_imageWithSize:size drawing:^(CGContextRef ctx) {
CGRect r;
r.origin = CGPointZero;
r.size = size;
CGContextDrawImage(ctx, r, self.CGImage);
CGContextDrawImage(ctx, r, self.tui_CGImage);
}];
}

- (TUIImage *)crop:(CGRect)cropRect
- (NSImage *)tui_crop:(CGRect)cropRect
{
if((cropRect.size.width < 1) || (cropRect.size.height < 1))
return nil;
Expand All @@ -52,34 +87,34 @@ - (TUIImage *)crop:(CGRect)cropRect
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);
CGImageRef cgimage = CGImageCreateWithImageInRect(self.tui_CGImage, cropRect);
if(!cgimage) {
NSLog(@"CGImageCreateWithImageInRect failed %@ %@", NSStringFromRect(cropRect), NSStringFromSize(s));
return nil;
}
TUIImage *i = [TUIImage imageWithCGImage:cgimage];
NSImage *i = [NSImage tui_imageWithCGImage:cgimage];
CGImageRelease(cgimage);
return i;
} else {
// slow crop - probably doing pad
return [TUIImage imageWithSize:cropRect.size drawing:^(CGContextRef ctx) {
return [NSImage tui_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);
CGContextDrawImage(ctx, imageRect, self.tui_CGImage);
}];
}
}

- (TUIImage *)upsideDownCrop:(CGRect)cropRect
- (NSImage *)tui_upsideDownCrop:(CGRect)cropRect
{
CGSize s = self.size;
cropRect.origin.y = s.height - (cropRect.origin.y + cropRect.size.height);
return [self crop:cropRect];
return [self tui_crop:cropRect];
}

- (TUIImage *)thumbnail:(CGSize)newSize
- (NSImage *)tui_thumbnail:(CGSize)newSize
{
CGSize s = self.size;
float oldProp = s.width / s.height;
Expand All @@ -93,78 +128,78 @@ - (TUIImage *)thumbnail:(CGSize)newSize
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];
return [[self tui_crop:cropRect] tui_scale:newSize];
}

- (TUIImage *)pad:(CGFloat)padding
- (NSImage *)tui_pad:(CGFloat)padding
{
CGSize s = self.size;
return [self crop:CGRectMake(-padding, -padding, s.width + padding*2, s.height + padding*2)];
return [self tui_crop:CGRectMake(-padding, -padding, s.width + padding*2, s.height + padding*2)];
}

- (TUIImage *)roundImage:(CGFloat)radius
- (NSImage *)tui_roundImage:(CGFloat)radius
{
CGRect r;
r.origin = CGPointZero;
r.size = self.size;
return [TUIImage imageWithSize:r.size drawing:^(CGContextRef ctx) {
return [NSImage tui_imageWithSize:r.size drawing:^(CGContextRef ctx) {
CGContextClipToRoundRect(ctx, r, radius);
CGContextDrawImage(ctx, r, self.CGImage);
CGContextDrawImage(ctx, r, self.tui_CGImage);
}];
}

- (TUIImage *)invertedMask
- (NSImage *)tui_invertedMask
{
CGSize s = self.size;
return [TUIImage imageWithSize:s drawing:^(CGContextRef ctx) {
return [NSImage tui_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);
CGContextClipToMask(ctx, rect, self.tui_CGImage);
CGContextClearRect(ctx, rect);
CGContextRestoreGState(ctx);
}];
}

- (TUIImage *)innerShadowWithOffset:(CGSize)offset radius:(CGFloat)radius color:(TUIColor *)color backgroundColor:(TUIColor *)backgroundColor
- (NSImage *)tui_innerShadowWithOffset:(CGSize)offset radius:(CGFloat)radius color:(TUIColor *)color backgroundColor:(TUIColor *)backgroundColor
{
CGFloat padding = ceil(radius);
TUIImage *paddedImage = [self pad:padding];
TUIImage *shadowImage = [TUIImage imageWithSize:paddedImage.size drawing:^(CGContextRef ctx) {
NSImage *paddedImage = [self tui_pad:padding];
NSImage *shadowImage = [NSImage tui_imageWithSize:paddedImage.size drawing:^(CGContextRef ctx) {
CGContextSaveGState(ctx);
CGRect r = CGRectMake(0, 0, paddedImage.size.width, paddedImage.size.height);
CGContextClipToMask(ctx, r, paddedImage.CGImage); // clip to image
CGContextClipToMask(ctx, r, paddedImage.tui_CGImage); // clip to image
CGContextSetShadowWithColor(ctx, offset, radius, color.CGColor);
CGContextBeginTransparencyLayer(ctx, NULL);
{
CGContextClipToMask(ctx, r, [[paddedImage invertedMask] CGImage]); // clip to inverted
CGContextClipToMask(ctx, r, [[paddedImage tui_invertedMask] tui_CGImage]); // clip to inverted
CGContextSetFillColorWithColor(ctx, backgroundColor.CGColor);
CGContextFillRect(ctx, r); // draw with shadow
}
CGContextEndTransparencyLayer(ctx);
CGContextRestoreGState(ctx);
}];

return [shadowImage pad:-padding];
return [shadowImage tui_pad:-padding];
}

- (TUIImage *)embossMaskWithOffset:(CGSize)offset
- (NSImage *)tui_embossMaskWithOffset:(CGSize)offset
{
CGFloat padding = MAX(offset.width, offset.height) + 1;
TUIImage *paddedImage = [self pad:padding];
NSImage *paddedImage = [self tui_pad:padding];
CGSize s = paddedImage.size;
TUIImage *embossedImage = [TUIImage imageWithSize:s drawing:^(CGContextRef ctx) {
NSImage *embossedImage = [NSImage tui_imageWithSize:s drawing:^(CGContextRef ctx) {
CGContextSaveGState(ctx);
CGRect r = CGRectMake(0, 0, s.width, s.height);
CGContextClipToMask(ctx, r, [paddedImage CGImage]);
CGContextClipToMask(ctx, CGRectOffset(r, offset.width, offset.height), [[paddedImage invertedMask] CGImage]);
CGContextClipToMask(ctx, r, [paddedImage tui_CGImage]);
CGContextClipToMask(ctx, CGRectOffset(r, offset.width, offset.height), [[paddedImage tui_invertedMask] tui_CGImage]);
CGContextSetRGBFillColor(ctx, 0, 0, 0, 1);
CGContextFillRect(ctx, r);
CGContextRestoreGState(ctx);
}];

return [embossedImage pad:-padding];
return [embossedImage tui_pad:-padding];
}

@end

0 comments on commit 70bed0d

Please sign in to comment.