diff --git a/ExampleProject/ConcordeExample/ExampleScrollView.m b/ExampleProject/ConcordeExample/ExampleScrollView.m index 2638efcd..1c9ad8ec 100644 --- a/ExampleProject/ConcordeExample/ExampleScrollView.m +++ b/ExampleProject/ConcordeExample/ExampleScrollView.m @@ -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]; diff --git a/ExampleProject/ConcordeExample/ExampleView.m b/ExampleProject/ConcordeExample/ExampleView.m index b01f19fe..74fd165f 100644 --- a/ExampleProject/ConcordeExample/ExampleView.m +++ b/ExampleProject/ConcordeExample/ExampleView.m @@ -67,14 +67,17 @@ 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); @@ -82,20 +85,20 @@ Note by default scroll views (and therefore table views) don't // 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]]; diff --git a/TwUI.xcodeproj/project.pbxproj b/TwUI.xcodeproj/project.pbxproj index 462d90a0..d7196a45 100644 --- a/TwUI.xcodeproj/project.pbxproj +++ b/TwUI.xcodeproj/project.pbxproj @@ -33,8 +33,6 @@ 5EE983C813BE7834005F430D /* TUIControl.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C4D13BE6E1900C85CB5 /* TUIControl.m */; }; 5EE983C913BE7834005F430D /* TUIFastIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C4F13BE6E1900C85CB5 /* TUIFastIndexPath.m */; }; 5EE983CB13BE7834005F430D /* TUIGeometry.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5313BE6E1900C85CB5 /* TUIGeometry.m */; }; - 5EE983CC13BE7834005F430D /* TUIImage+Drawing.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5513BE6E1900C85CB5 /* TUIImage+Drawing.m */; }; - 5EE983CD13BE7834005F430D /* TUIImage.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5713BE6E1900C85CB5 /* TUIImage.m */; }; 5EE983CE13BE7834005F430D /* TUIImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5913BE6E1900C85CB5 /* TUIImageView.m */; }; 5EE983CF13BE7834005F430D /* TUILabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5D13BE6E1900C85CB5 /* TUILabel.m */; }; 5EE983D013BE7834005F430D /* TUINSView+Hyperfocus.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5F13BE6E1900C85CB5 /* TUINSView+Hyperfocus.m */; }; @@ -149,8 +147,6 @@ CB5E322B13BE70CA004B7899 /* TUIControl.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C4D13BE6E1900C85CB5 /* TUIControl.m */; }; CB5E322D13BE70CA004B7899 /* TUIFastIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C4F13BE6E1900C85CB5 /* TUIFastIndexPath.m */; }; CB5E323113BE70CA004B7899 /* TUIGeometry.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5313BE6E1900C85CB5 /* TUIGeometry.m */; }; - CB5E323313BE70CA004B7899 /* TUIImage+Drawing.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5513BE6E1900C85CB5 /* TUIImage+Drawing.m */; }; - CB5E323513BE70CA004B7899 /* TUIImage.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5713BE6E1900C85CB5 /* TUIImage.m */; }; CB5E323713BE70CA004B7899 /* TUIImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5913BE6E1900C85CB5 /* TUIImageView.m */; }; CB5E323913BE70CA004B7899 /* TUIKit.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5B13BE6E1900C85CB5 /* TUIKit.m */; }; CB5E323B13BE70CA004B7899 /* TUILabel.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5D13BE6E1900C85CB5 /* TUILabel.m */; }; @@ -207,10 +203,6 @@ CBB74CA613BE6E1900C85CB5 /* TUIFastIndexPath.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C4F13BE6E1900C85CB5 /* TUIFastIndexPath.m */; }; CBB74CA913BE6E1900C85CB5 /* TUIGeometry.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5213BE6E1900C85CB5 /* TUIGeometry.h */; settings = {ATTRIBUTES = (Public, ); }; }; CBB74CAA13BE6E1900C85CB5 /* TUIGeometry.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5313BE6E1900C85CB5 /* TUIGeometry.m */; }; - CBB74CAB13BE6E1900C85CB5 /* TUIImage+Drawing.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5413BE6E1900C85CB5 /* TUIImage+Drawing.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CBB74CAC13BE6E1900C85CB5 /* TUIImage+Drawing.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5513BE6E1900C85CB5 /* TUIImage+Drawing.m */; }; - CBB74CAD13BE6E1900C85CB5 /* TUIImage.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5613BE6E1900C85CB5 /* TUIImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CBB74CAE13BE6E1900C85CB5 /* TUIImage.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5713BE6E1900C85CB5 /* TUIImage.m */; }; CBB74CAF13BE6E1900C85CB5 /* TUIImageView.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5813BE6E1900C85CB5 /* TUIImageView.h */; settings = {ATTRIBUTES = (Public, ); }; }; CBB74CB013BE6E1900C85CB5 /* TUIImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = CBB74C5913BE6E1900C85CB5 /* TUIImageView.m */; }; CBB74CB113BE6E1900C85CB5 /* TUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5A13BE6E1900C85CB5 /* TUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -283,6 +275,18 @@ D040611615B6A7CD00F753ED /* NSTextView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */; }; D040611715B6A7CD00F753ED /* NSTextView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */; }; D040611815B6A7CD00F753ED /* NSTextView+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */; }; + D05D23A015BF7239000ED14F /* NSImage+TUIExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D05D239E15BF7239000ED14F /* NSImage+TUIExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D05D23A115BF7239000ED14F /* NSImage+TUIExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D05D239E15BF7239000ED14F /* NSImage+TUIExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D05D23A215BF7239000ED14F /* NSImage+TUIExtensions.h in Headers */ = {isa = PBXBuildFile; fileRef = D05D239E15BF7239000ED14F /* NSImage+TUIExtensions.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D05D23A315BF7239000ED14F /* NSImage+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D05D239F15BF7239000ED14F /* NSImage+TUIExtensions.m */; }; + D05D23A415BF7239000ED14F /* NSImage+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D05D239F15BF7239000ED14F /* NSImage+TUIExtensions.m */; }; + D05D23A515BF7239000ED14F /* NSImage+TUIExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = D05D239F15BF7239000ED14F /* NSImage+TUIExtensions.m */; }; + D05DEE8C15BF645D005D8769 /* TUIStretchableImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D05DEE8D15BF645D005D8769 /* TUIStretchableImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D05DEE8E15BF645D005D8769 /* TUIStretchableImage.h in Headers */ = {isa = PBXBuildFile; fileRef = D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D05DEE8F15BF645D005D8769 /* TUIStretchableImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */; }; + D05DEE9015BF645D005D8769 /* TUIStretchableImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */; }; + D05DEE9115BF645D005D8769 /* TUIStretchableImage.m in Sources */ = {isa = PBXBuildFile; fileRef = D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */; }; D07AA82315BDD6B600F736C0 /* TUINSView+Hyperfocus.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5E13BE6E1900C85CB5 /* TUINSView+Hyperfocus.h */; settings = {ATTRIBUTES = (Public, ); }; }; D07AA82415BDD6B700F736C0 /* TUINSView+Hyperfocus.h in Headers */ = {isa = PBXBuildFile; fileRef = CBB74C5E13BE6E1900C85CB5 /* TUINSView+Hyperfocus.h */; settings = {ATTRIBUTES = (Public, ); }; }; D07AA82615BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */ = {isa = PBXBuildFile; fileRef = D07AA82515BDD72D00F736C0 /* TUINSView+NSTextInputClient.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -500,10 +504,6 @@ CBB74C4F13BE6E1900C85CB5 /* TUIFastIndexPath.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIFastIndexPath.m; sourceTree = ""; }; CBB74C5213BE6E1900C85CB5 /* TUIGeometry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIGeometry.h; sourceTree = ""; }; CBB74C5313BE6E1900C85CB5 /* TUIGeometry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIGeometry.m; sourceTree = ""; }; - CBB74C5413BE6E1900C85CB5 /* TUIImage+Drawing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TUIImage+Drawing.h"; sourceTree = ""; }; - CBB74C5513BE6E1900C85CB5 /* TUIImage+Drawing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TUIImage+Drawing.m"; sourceTree = ""; }; - CBB74C5613BE6E1900C85CB5 /* TUIImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIImage.h; sourceTree = ""; }; - CBB74C5713BE6E1900C85CB5 /* TUIImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIImage.m; sourceTree = ""; }; CBB74C5813BE6E1900C85CB5 /* TUIImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIImageView.h; sourceTree = ""; }; CBB74C5913BE6E1900C85CB5 /* TUIImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIImageView.m; sourceTree = ""; }; CBB74C5A13BE6E1900C85CB5 /* TUIKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TUIKit.h; path = lib/UIKit/TUIKit.h; sourceTree = ""; }; @@ -565,6 +565,10 @@ D04007ED15BF2C0700FD49DB /* TwUITests-Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TwUITests-Prefix.pch"; sourceTree = ""; }; D040611115B6A7CC00F753ED /* NSTextView+TUIExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSTextView+TUIExtensions.h"; sourceTree = ""; }; D040611215B6A7CC00F753ED /* NSTextView+TUIExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSTextView+TUIExtensions.m"; sourceTree = ""; }; + D05D239E15BF7239000ED14F /* NSImage+TUIExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSImage+TUIExtensions.h"; sourceTree = ""; }; + D05D239F15BF7239000ED14F /* NSImage+TUIExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSImage+TUIExtensions.m"; sourceTree = ""; }; + D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIStretchableImage.h; sourceTree = ""; }; + D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TUIStretchableImage.m; sourceTree = ""; }; D07AA82515BDD72D00F736C0 /* TUINSView+NSTextInputClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TUINSView+NSTextInputClient.h"; sourceTree = ""; }; D0C764EA15B611C200E7AC2C /* TUIBridgedView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIBridgedView.h; sourceTree = ""; }; D0C7650415B6156A00E7AC2C /* TUIHostView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TUIHostView.h; sourceTree = ""; }; @@ -759,6 +763,8 @@ children = ( D0C7655915B6297200E7AC2C /* NSClipView+TUIExtensions.h */, D0C7655A15B6297200E7AC2C /* NSClipView+TUIExtensions.m */, + D05D239E15BF7239000ED14F /* NSImage+TUIExtensions.h */, + D05D239F15BF7239000ED14F /* NSImage+TUIExtensions.m */, D0C7655B15B6297300E7AC2C /* NSScrollView+TUIExtensions.h */, D0C7655C15B6297300E7AC2C /* NSScrollView+TUIExtensions.m */, D0C7653115B624D800E7AC2C /* NSView+TUIExtensions.h */, @@ -792,23 +798,19 @@ CBB74C5213BE6E1900C85CB5 /* TUIGeometry.h */, CBB74C5313BE6E1900C85CB5 /* TUIGeometry.m */, D0C7650415B6156A00E7AC2C /* TUIHostView.h */, - CBB74C5413BE6E1900C85CB5 /* TUIImage+Drawing.h */, - CBB74C5513BE6E1900C85CB5 /* TUIImage+Drawing.m */, - CBB74C5613BE6E1900C85CB5 /* TUIImage.h */, - CBB74C5713BE6E1900C85CB5 /* TUIImage.m */, CBB74C5813BE6E1900C85CB5 /* TUIImageView.h */, CBB74C5913BE6E1900C85CB5 /* TUIImageView.m */, CBB74C5B13BE6E1900C85CB5 /* TUIKit.m */, CBB74C5C13BE6E1900C85CB5 /* TUILabel.h */, CBB74C5D13BE6E1900C85CB5 /* TUILabel.m */, - D0C7650C15B6189D00E7AC2C /* TUINSHostView.h */, - D0C7650D15B6189D00E7AC2C /* TUINSHostView.m */, - 8819794A13E26E5800AA39EB /* TUINSView+Accessibility.h */, - 8819794B13E26E5800AA39EB /* TUINSView+Accessibility.m */, 48A10E7D15B7769A007F9EE3 /* TUILayoutConstraint.h */, 48A10E7E15B7769A007F9EE3 /* TUILayoutConstraint.m */, 48A10E7F15B7769A007F9EE3 /* TUILayoutManager.h */, 48A10E8015B7769A007F9EE3 /* TUILayoutManager.m */, + D0C7650C15B6189D00E7AC2C /* TUINSHostView.h */, + D0C7650D15B6189D00E7AC2C /* TUINSHostView.m */, + 8819794A13E26E5800AA39EB /* TUINSView+Accessibility.h */, + 8819794B13E26E5800AA39EB /* TUINSView+Accessibility.m */, CBB74C5E13BE6E1900C85CB5 /* TUINSView+Hyperfocus.h */, CBB74C5F13BE6E1900C85CB5 /* TUINSView+Hyperfocus.m */, D07AA82515BDD72D00F736C0 /* TUINSView+NSTextInputClient.h */, @@ -826,10 +828,12 @@ CBB74C6613BE6E1900C85CB5 /* TUIResponder.m */, CBB74C6713BE6E1900C85CB5 /* TUIScrollKnob.h */, CBB74C6813BE6E1900C85CB5 /* TUIScrollKnob.m */, - CBB74C6913BE6E1900C85CB5 /* TUIScrollView.h */, - CBB74C6A13BE6E1900C85CB5 /* TUIScrollView.m */, D0C7655015B6294400E7AC2C /* TUIScrollView+TUIBridgedScrollView.h */, D0C7655115B6294400E7AC2C /* TUIScrollView+TUIBridgedScrollView.m */, + CBB74C6913BE6E1900C85CB5 /* TUIScrollView.h */, + CBB74C6A13BE6E1900C85CB5 /* TUIScrollView.m */, + D05DEE8A15BF645D005D8769 /* TUIStretchableImage.h */, + D05DEE8B15BF645D005D8769 /* TUIStretchableImage.m */, CBB74C6B13BE6E1900C85CB5 /* TUIStringDrawing.h */, CBB74C6C13BE6E1900C85CB5 /* TUIStringDrawing.m */, 88D81CFD1577EF0D009D453B /* TUIStyledView.h */, @@ -881,9 +885,9 @@ CBB74C8C13BE6E1900C85CB5 /* TUIView.m */, CBB74C8D13BE6E1900C85CB5 /* TUIViewController.h */, CBB74C8E13BE6E1900C85CB5 /* TUIViewController.m */, + D0C7656D15B6322A00E7AC2C /* TUIViewNSViewContainer+Private.h */, CBB74C8F13BE6E1900C85CB5 /* TUIViewNSViewContainer.h */, CBB74C9013BE6E1900C85CB5 /* TUIViewNSViewContainer.m */, - D0C7656D15B6322A00E7AC2C /* TUIViewNSViewContainer+Private.h */, ); name = UIKit; path = lib/UIKit; @@ -941,6 +945,8 @@ D039724B15B7D7DE0092CD26 /* TUILayoutConstraint.h in Headers */, D07AA82415BDD6B700F736C0 /* TUINSView+Hyperfocus.h in Headers */, D07AA82815BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */, + D05DEE8E15BF645D005D8769 /* TUIStretchableImage.h in Headers */, + D05D23A215BF7239000ED14F /* NSImage+TUIExtensions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -960,9 +966,7 @@ CBB74CA313BE6E1900C85CB5 /* TUIControl.h in Headers */, CBB74CA513BE6E1900C85CB5 /* TUIFastIndexPath.h in Headers */, CBB74CA913BE6E1900C85CB5 /* TUIGeometry.h in Headers */, - CBB74CAB13BE6E1900C85CB5 /* TUIImage+Drawing.h in Headers */, 88D81CFF1577EF0D009D453B /* TUIStyledView.h in Headers */, - CBB74CAD13BE6E1900C85CB5 /* TUIImage.h in Headers */, 887C227B15C1C7BB006EC31D /* NSFont+TUIExtensions.h in Headers */, CBB74CAF13BE6E1900C85CB5 /* TUIImageView.h in Headers */, CBB74CB113BE6E1900C85CB5 /* TUIKit.h in Headers */, @@ -1014,6 +1018,8 @@ 48A10E8315B7769A007F9EE3 /* TUILayoutManager.h in Headers */, 48A10E8B15B77A46007F9EE3 /* TUIView+Layout.h in Headers */, D07AA82615BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */, + D05DEE8C15BF645D005D8769 /* TUIStretchableImage.h in Headers */, + D05D23A015BF7239000ED14F /* NSImage+TUIExtensions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1044,6 +1050,8 @@ D039724A15B7D7DE0092CD26 /* TUILayoutConstraint.h in Headers */, D07AA82315BDD6B600F736C0 /* TUINSView+Hyperfocus.h in Headers */, D07AA82715BDD72F00F736C0 /* TUINSView+NSTextInputClient.h in Headers */, + D05DEE8D15BF645D005D8769 /* TUIStretchableImage.h in Headers */, + D05D23A115BF7239000ED14F /* NSImage+TUIExtensions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1274,8 +1282,6 @@ 5EE983C813BE7834005F430D /* TUIControl.m in Sources */, 5EE983C913BE7834005F430D /* TUIFastIndexPath.m in Sources */, 5EE983CB13BE7834005F430D /* TUIGeometry.m in Sources */, - 5EE983CC13BE7834005F430D /* TUIImage+Drawing.m in Sources */, - 5EE983CD13BE7834005F430D /* TUIImage.m in Sources */, 5EE983CE13BE7834005F430D /* TUIImageView.m in Sources */, 5EE983CF13BE7834005F430D /* TUILabel.m in Sources */, 5EE983D013BE7834005F430D /* TUINSView+Hyperfocus.m in Sources */, @@ -1327,6 +1333,8 @@ D039724315B7D7CE0092CD26 /* TUILayoutManager.m in Sources */, D039724515B7D7D40092CD26 /* TUIView+Layout.m in Sources */, D07AA82B15BDD79A00F736C0 /* TUINSView+NSTextInputClient.m in Sources */, + D05DEE9115BF645D005D8769 /* TUIStretchableImage.m in Sources */, + D05D23A515BF7239000ED14F /* NSImage+TUIExtensions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1347,8 +1355,6 @@ CBB74CA413BE6E1900C85CB5 /* TUIControl.m in Sources */, CBB74CA613BE6E1900C85CB5 /* TUIFastIndexPath.m in Sources */, CBB74CAA13BE6E1900C85CB5 /* TUIGeometry.m in Sources */, - CBB74CAC13BE6E1900C85CB5 /* TUIImage+Drawing.m in Sources */, - CBB74CAE13BE6E1900C85CB5 /* TUIImage.m in Sources */, CBB74CB013BE6E1900C85CB5 /* TUIImageView.m in Sources */, CBB74CB213BE6E1900C85CB5 /* TUIKit.m in Sources */, CBB74CB413BE6E1900C85CB5 /* TUILabel.m in Sources */, @@ -1404,6 +1410,8 @@ 48A10E8415B7769A007F9EE3 /* TUILayoutManager.m in Sources */, 48A10E8915B778E8007F9EE3 /* TUIView+Layout.m in Sources */, D07AA82915BDD79900F736C0 /* TUINSView+NSTextInputClient.m in Sources */, + D05DEE8F15BF645D005D8769 /* TUIStretchableImage.m in Sources */, + D05D23A315BF7239000ED14F /* NSImage+TUIExtensions.m in Sources */, 887C227C15C1C7BB006EC31D /* NSFont+TUIExtensions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1434,8 +1442,6 @@ CB5E322B13BE70CA004B7899 /* TUIControl.m in Sources */, CB5E322D13BE70CA004B7899 /* TUIFastIndexPath.m in Sources */, CB5E323113BE70CA004B7899 /* TUIGeometry.m in Sources */, - CB5E323313BE70CA004B7899 /* TUIImage+Drawing.m in Sources */, - CB5E323513BE70CA004B7899 /* TUIImage.m in Sources */, CB5E323713BE70CA004B7899 /* TUIImageView.m in Sources */, CB5E323913BE70CA004B7899 /* TUIKit.m in Sources */, CB5E323B13BE70CA004B7899 /* TUILabel.m in Sources */, @@ -1488,6 +1494,8 @@ D039724215B7D7CE0092CD26 /* TUILayoutManager.m in Sources */, D039724415B7D7D40092CD26 /* TUIView+Layout.m in Sources */, D07AA82A15BDD79A00F736C0 /* TUINSView+NSTextInputClient.m in Sources */, + D05DEE9015BF645D005D8769 /* TUIStretchableImage.m in Sources */, + D05D23A415BF7239000ED14F /* NSImage+TUIExtensions.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/lib/UIKit/NSImage+TUIExtensions.h b/lib/UIKit/NSImage+TUIExtensions.h new file mode 100644 index 00000000..3adee74e --- /dev/null +++ b/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 +#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 diff --git a/lib/UIKit/TUIImage+Drawing.m b/lib/UIKit/NSImage+TUIExtensions.m similarity index 50% rename from lib/UIKit/TUIImage+Drawing.m rename to lib/UIKit/NSImage+TUIExtensions.m index b808fbca..acacc80f 100644 --- a/lib/UIKit/TUIImage+Drawing.m +++ b/lib/UIKit/NSImage+TUIExtensions.m @@ -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; @@ -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; @@ -93,52 +128,52 @@ - (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 } @@ -146,25 +181,25 @@ - (TUIImage *)innerShadowWithOffset:(CGSize)offset radius:(CGFloat)radius color: 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 diff --git a/lib/UIKit/TUIButton+Content.m b/lib/UIKit/TUIButton+Content.m index cd0e9054..46782f64 100644 --- a/lib/UIKit/TUIButton+Content.m +++ b/lib/UIKit/TUIButton+Content.m @@ -16,34 +16,21 @@ #import "TUIButton.h" #import "TUIControl+Private.h" -#import "TUIImage.h" @interface TUIButtonContent : NSObject -{ - NSString *title; - TUIColor *titleColor; - TUIColor *shadowColor; - TUIImage *image; - TUIImage *backgroundImage; -} - @property (nonatomic, strong) NSString *title; @property (nonatomic, strong) TUIColor *titleColor; @property (nonatomic, strong) TUIColor *shadowColor; -@property (nonatomic, strong) TUIImage *image; -@property (nonatomic, strong) TUIImage *backgroundImage; - +@property (nonatomic, strong) NSImage *image; +@property (nonatomic, strong) NSImage *backgroundImage; @end @implementation TUIButtonContent - -@synthesize title; -@synthesize titleColor; -@synthesize shadowColor; -@synthesize image; -@synthesize backgroundImage; - - +@synthesize title = title; +@synthesize titleColor = titleColor; +@synthesize shadowColor = shadowColor; +@synthesize image = image; +@synthesize backgroundImage = backgroundImage; @end @@ -84,7 +71,7 @@ - (void)setTitleShadowColor:(TUIColor *)color forState:(TUIControlState)state [self _stateDidChange]; } -- (void)setImage:(TUIImage *)i forState:(TUIControlState)state +- (void)setImage:(NSImage *)i forState:(TUIControlState)state { [self _stateWillChange]; [[self _contentForState:state] setImage:i]; @@ -92,7 +79,7 @@ - (void)setImage:(TUIImage *)i forState:(TUIControlState)state [self _stateDidChange]; } -- (void)setBackgroundImage:(TUIImage *)i forState:(TUIControlState)state +- (void)setBackgroundImage:(NSImage *)i forState:(TUIControlState)state { [self _stateWillChange]; [[self _contentForState:state] setBackgroundImage:i]; @@ -115,12 +102,12 @@ - (TUIColor *)titleShadowColorForState:(TUIControlState)state return [[self _contentForState:state] shadowColor]; } -- (TUIImage *)imageForState:(TUIControlState)state +- (NSImage *)imageForState:(TUIControlState)state { return [[self _contentForState:state] image]; } -- (TUIImage *)backgroundImageForState:(TUIControlState)state +- (NSImage *)backgroundImageForState:(TUIControlState)state { return [[self _contentForState:state] backgroundImage]; } @@ -155,9 +142,9 @@ - (TUIColor *)currentTitleShadowColor return color; } -- (TUIImage *)currentImage +- (NSImage *)currentImage { - TUIImage *image = [self imageForState:self.state]; + NSImage *image = [self imageForState:self.state]; if(image == nil) { image = [self imageForState:TUIControlStateNormal]; } @@ -165,9 +152,9 @@ - (TUIImage *)currentImage return image; } -- (TUIImage *)currentBackgroundImage +- (NSImage *)currentBackgroundImage { - TUIImage *image = [self backgroundImageForState:self.state]; + NSImage *image = [self backgroundImageForState:self.state]; if(image == nil) { image = [self backgroundImageForState:TUIControlStateNormal]; } diff --git a/lib/UIKit/TUIButton.h b/lib/UIKit/TUIButton.h index 4b1b47a3..f5401bec 100644 --- a/lib/UIKit/TUIButton.h +++ b/lib/UIKit/TUIButton.h @@ -22,7 +22,6 @@ #import "TUIControl.h" #import "TUIGeometry.h" -@class TUIImage; @class TUILabel; @class TUIImageView; @@ -77,19 +76,18 @@ typedef enum { - (void)setTitle:(NSString *)title forState:(TUIControlState)state; - (void)setTitleColor:(TUIColor *)color forState:(TUIControlState)state; - (void)setTitleShadowColor:(TUIColor *)color forState:(TUIControlState)state; -- (void)setImage:(TUIImage *)image forState:(TUIControlState)state; -- (void)setBackgroundImage:(TUIImage *)image forState:(TUIControlState)state; +- (void)setImage:(NSImage *)image forState:(TUIControlState)state; +- (void)setBackgroundImage:(NSImage *)image forState:(TUIControlState)state; - (NSString *)titleForState:(TUIControlState)state; - (TUIColor *)titleColorForState:(TUIControlState)state; - (TUIColor *)titleShadowColorForState:(TUIControlState)state; -- (TUIImage *)imageForState:(TUIControlState)state; -- (TUIImage *)backgroundImageForState:(TUIControlState)state; - -@property(nonatomic,readonly,retain) NSString *currentTitle; -@property(nonatomic,readonly,retain) TUIColor *currentTitleColor; -@property(nonatomic,readonly,retain) TUIColor *currentTitleShadowColor; -@property(nonatomic,readonly,retain) TUIImage *currentImage; -@property(nonatomic,readonly,retain) TUIImage *currentBackgroundImage; - +- (NSImage *)imageForState:(TUIControlState)state; +- (NSImage *)backgroundImageForState:(TUIControlState)state; + +@property(nonatomic, readonly, strong) NSString *currentTitle; +@property(nonatomic, readonly, strong) TUIColor *currentTitleColor; +@property(nonatomic, readonly, strong) TUIColor *currentTitleShadowColor; +@property(nonatomic, readonly, strong) NSImage *currentImage; +@property(nonatomic, readonly, strong) NSImage *currentBackgroundImage; @end diff --git a/lib/UIKit/TUIButton.m b/lib/UIKit/TUIButton.m index de5e7a50..e5dc0023 100644 --- a/lib/UIKit/TUIButton.m +++ b/lib/UIKit/TUIButton.m @@ -14,7 +14,6 @@ limitations under the License. */ -#import "TUIImage.h" #import "TUIButton.h" #import "TUICGAdditions.h" #import "TUIColor.h" @@ -22,6 +21,7 @@ #import "TUIImageView.h" #import "TUILabel.h" #import "TUINSView.h" +#import "TUIStretchableImage.h" #import "TUITextRenderer.h" @interface TUIButton () @@ -185,14 +185,14 @@ - (void)drawRect:(CGRect)r CGContextFillRect(TUIGraphicsGetCurrentContext(), self.bounds); } - TUIImage *backgroundImage = self.currentBackgroundImage; - TUIImage *image = self.currentImage; + NSImage *backgroundImage = self.currentBackgroundImage; + NSImage *image = self.currentImage; - [backgroundImage drawInRect:[self backgroundRectForBounds:bounds] blendMode:kCGBlendModeNormal alpha:1.0]; + [backgroundImage drawInRect:[self backgroundRectForBounds:bounds] fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; if(image) { CGRect imageRect; - if(image.leftCapWidth || image.topCapHeight) { + if([image isKindOfClass:[TUIStretchableImage class]]) { // stretchable imageRect = self.bounds; } else { @@ -206,7 +206,8 @@ - (void)drawRect:(CGRect)r b.size.height -= _imageEdgeInsets.bottom + _imageEdgeInsets.top; imageRect = ButtonRectRoundOrigin(ButtonRectCenteredInRect(imageRect, b)); } - [image drawInRect:imageRect blendMode:kCGBlendModeNormal alpha:alpha]; + + [image drawInRect:imageRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:alpha]; } NSString *title = self.currentTitle; diff --git a/lib/UIKit/TUICGAdditions.h b/lib/UIKit/TUICGAdditions.h index c8198849..3a093128 100644 --- a/lib/UIKit/TUICGAdditions.h +++ b/lib/UIKit/TUICGAdditions.h @@ -29,7 +29,6 @@ typedef NSUInteger TUICGRoundedRectCorner; #import -@class TUIImage; @class TUIView; extern CGContextRef TUICreateOpaqueGraphicsContext(CGSize size); @@ -55,16 +54,16 @@ extern CGContextRef TUIGraphicsGetCurrentContext(void); extern void TUIGraphicsPushContext(CGContextRef context); extern void TUIGraphicsPopContext(void); -extern TUIImage *TUIGraphicsContextGetImage(CGContextRef ctx); +extern NSImage *TUIGraphicsContextGetImage(CGContextRef ctx); extern void TUIGraphicsBeginImageContext(CGSize size); extern void TUIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale); -extern TUIImage *TUIGraphicsGetImageFromCurrentImageContext(void); +extern NSImage *TUIGraphicsGetImageFromCurrentImageContext(void); extern void TUIGraphicsEndImageContext(void); -extern TUIImage *TUIGraphicsGetImageForView(TUIView *view); +extern NSImage *TUIGraphicsGetImageForView(TUIView *view); -extern TUIImage *TUIGraphicsDrawAsImage(CGSize size, void(^draw)(void)); +extern NSImage *TUIGraphicsDrawAsImage(CGSize size, void(^draw)(void)); /** Draw drawing as a PDF diff --git a/lib/UIKit/TUICGAdditions.m b/lib/UIKit/TUICGAdditions.m index 74f40e3c..d967a5c8 100644 --- a/lib/UIKit/TUICGAdditions.m +++ b/lib/UIKit/TUICGAdditions.m @@ -15,7 +15,6 @@ */ #import "TUICGAdditions.h" -#import "TUIImage.h" #import "TUIView.h" CGContextRef TUICreateOpaqueGraphicsContext(CGSize size) @@ -194,10 +193,11 @@ void TUIGraphicsPopContext(void) [NSGraphicsContext restoreGraphicsState]; } -TUIImage* TUIGraphicsContextGetImage(CGContextRef ctx) +NSImage *TUIGraphicsContextGetImage(CGContextRef ctx) { CGImageRef CGImage = TUICreateCGImageFromBitmapContext(ctx); - TUIImage *image = [TUIImage imageWithCGImage:CGImage]; + CGSize size = CGSizeMake(CGImageGetWidth(CGImage), CGImageGetHeight(CGImage)); + NSImage *image = [[NSImage alloc] initWithCGImage:CGImage size:size]; CGImageRelease(CGImage); return image; @@ -219,17 +219,18 @@ void TUIGraphicsBeginImageContext(CGSize size) TUIGraphicsBeginImageContextWithOptions(size, NO, 1.0f); } -TUIImage* TUIGraphicsGetImageFromCurrentImageContext(void) +NSImage *TUIGraphicsGetImageFromCurrentImageContext(void) { return TUIGraphicsContextGetImage(TUIGraphicsGetCurrentContext()); } -TUIImage* TUIGraphicsGetImageForView(TUIView *view) +NSImage *TUIGraphicsGetImageForView(TUIView *view) { TUIGraphicsBeginImageContext(view.frame.size); [view.layer renderInContext:TUIGraphicsGetCurrentContext()]; - TUIImage *image = TUIGraphicsGetImageFromCurrentImageContext(); + NSImage *image = TUIGraphicsGetImageFromCurrentImageContext(); TUIGraphicsEndImageContext(); + return image; } @@ -238,12 +239,13 @@ void TUIGraphicsEndImageContext(void) TUIGraphicsPopContext(); } -TUIImage *TUIGraphicsDrawAsImage(CGSize size, void(^draw)(void)) +NSImage *TUIGraphicsDrawAsImage(CGSize size, void(^draw)(void)) { TUIGraphicsBeginImageContext(size); draw(); - TUIImage *image = TUIGraphicsGetImageFromCurrentImageContext(); + NSImage *image = TUIGraphicsGetImageFromCurrentImageContext(); TUIGraphicsEndImageContext(); + return image; } diff --git a/lib/UIKit/TUIColor.h b/lib/UIKit/TUIColor.h index 69f74d3f..bdc1aaf0 100644 --- a/lib/UIKit/TUIColor.h +++ b/lib/UIKit/TUIColor.h @@ -16,8 +16,6 @@ #import -@class TUIImage; - @interface TUIColor : NSObject { CGColorRef _cgColor; // backing color @@ -28,12 +26,12 @@ + (TUIColor *)colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; + (TUIColor *)colorWithCGColor:(CGColorRef)cgColor; + (TUIColor *)colorWithNSColor:(NSColor *)nsColor; -+ (TUIColor *)colorWithPatternImage:(TUIImage *)image; ++ (TUIColor *)colorWithPatternImage:(NSImage *)image; - (TUIColor *)initWithWhite:(CGFloat)white alpha:(CGFloat)alpha; - (TUIColor *)initWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha; - (TUIColor *)initWithCGColor:(CGColorRef)cgColor; -- (TUIColor *)initWithPatternImage:(TUIImage *)image; +- (TUIColor *)initWithPatternImage:(NSImage *)image; // cached + (TUIColor *)blackColor; // 0.0 white diff --git a/lib/UIKit/TUIColor.m b/lib/UIKit/TUIColor.m index 23e1b086..04704ded 100644 --- a/lib/UIKit/TUIColor.m +++ b/lib/UIKit/TUIColor.m @@ -16,11 +16,10 @@ #import "TUIColor.h" #import "TUICGAdditions.h" -#import "TUIImage.h" @implementation TUIColor -+ (TUIColor *)colorWithPatternImage:(TUIImage *)image ++ (TUIColor *)colorWithPatternImage:(NSImage *)image { return [[self alloc] initWithPatternImage:image]; } @@ -76,20 +75,23 @@ - (TUIColor *)initWithCGColor:(CGColorRef)cgColor static void patternDraw(void *info, CGContextRef ctx) { - TUIImage *image = (__bridge TUIImage *)info; + NSImage *image = (__bridge NSImage *)info; + CGRect rect; rect.origin = CGPointZero; rect.size = image.size; - CGContextDrawImage(ctx, rect, image.CGImage); + + CGImageRef cgImage = [image CGImageForProposedRect:&rect context:nil hints:nil]; + CGContextDrawImage(ctx, rect, cgImage); } static void patternRelease(void *info) { // transfer the object back to ARC, thus releasing it - (void)(__bridge_transfer TUIImage *)info; + (void)(__bridge_transfer NSImage *)info; } -- (TUIColor *)initWithPatternImage:(TUIImage *)image +- (TUIColor *)initWithPatternImage:(NSImage *)image { if((self = [super init])) { diff --git a/lib/UIKit/TUIImage+Drawing.h b/lib/UIKit/TUIImage+Drawing.h deleted file mode 100644 index 2c50e35a..00000000 --- a/lib/UIKit/TUIImage+Drawing.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - 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 "TUIImage.h" - -@class TUIColor; - -@interface TUIImage (Drawing) - -+ (TUIImage *)imageWithSize:(CGSize)size drawing:(void(^)(CGContextRef))draw; // thread safe - -- (TUIImage *)crop:(CGRect)cropRect; -- (TUIImage *)upsideDownCrop:(CGRect)cropRect; -- (TUIImage *)scale:(CGSize)size; -- (TUIImage *)thumbnail:(CGSize)size; -- (TUIImage *)pad:(CGFloat)padding; // can be negative (to crop to center) -- (TUIImage *)roundImage:(CGFloat)radius; -- (TUIImage *)invertedMask; -- (TUIImage *)embossMaskWithOffset:(CGSize)offset; // subtract reciever from itself offset by 'offset', use as a mask to draw emboss -- (TUIImage *)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 diff --git a/lib/UIKit/TUIImage.h b/lib/UIKit/TUIImage.h deleted file mode 100644 index b59857e7..00000000 --- a/lib/UIKit/TUIImage.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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 - -@interface TUIImage : NSObject - -+ (TUIImage *)imageNamed:(NSString *)name; -+ (TUIImage *)imageNamed:(NSString *)name cache:(BOOL)shouldCache; - -+ (TUIImage *)imageWithData:(NSData *)data; -+ (TUIImage *)imageWithCGImage:(CGImageRef)imageRef; -+ (TUIImage *)imageWithNSImage:(NSImage *)image; - -+ (TUIImage *)_imageWithABImage:(id)abimage __attribute__((deprecated)); // don't use this - -- (id)initWithCGImage:(CGImageRef)imageRef; - -@property (nonatomic, readonly) CGSize size; -@property (nonatomic, readonly) CGFloat scale; -@property (nonatomic, readonly) CGImageRef CGImage; - -- (void)drawAtPoint:(CGPoint)point; // mode = kCGBlendModeNormal, alpha = 1.0 -- (void)drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha; -- (void)drawInRect:(CGRect)rect; // mode = kCGBlendModeNormal, alpha = 1.0 -- (void)drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha; - -- (TUIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight; - -@property (nonatomic, readonly) NSInteger leftCapWidth; // default is 0. if non-zero, horiz. stretchable. right cap is calculated as width - leftCapWidth - 1 -@property (nonatomic, readonly) NSInteger topCapHeight; // default is 0. if non-zero, vert. stretchable. bottom cap is calculated as height - topCapWidth - 1 - -@end - -@interface TUIImage (AppKit) -@property (nonatomic, readonly) id nsImage; // NSImage * -@end - -extern NSData *TUIImagePNGRepresentation(TUIImage *image); -extern NSData *TUIImageJPEGRepresentation(TUIImage *image, CGFloat compressionQuality); - -#import "TUIImage+Drawing.h" diff --git a/lib/UIKit/TUIImage.m b/lib/UIKit/TUIImage.m deleted file mode 100644 index 08f2730d..00000000 --- a/lib/UIKit/TUIImage.m +++ /dev/null @@ -1,462 +0,0 @@ -/* - 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 "TUIImage.h" -#import "TUICGAdditions.h" -#import "TUIView+Private.h" - -static CGImageRef TUICreateImageRefWithData(NSData *data) -{ - if(!data) - return nil; - - CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL); - if(!imageSource) { - return nil; - } - - CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL); - if(!image) { - NSLog(@"could not create image at index 0"); - } - - CFRelease(imageSource); - return image; -} - -static CGImageRef TUICreateImageRefForURL(NSURL *url, BOOL shouldCache) -{ - static NSMutableDictionary *cache = nil; - if(!cache) - cache = [NSMutableDictionary new]; - - if(url) { - CGImageRef image; - - // look up in cache - image = (__bridge CGImageRef)[cache objectForKey:url]; - if(image) - return CGImageRetain(image); - - image = TUICreateImageRefWithData([NSData dataWithContentsOfURL:url]); - if(image && shouldCache) - [cache setObject:(__bridge id)image forKey:url]; - - return image; - } - return NULL; -} - -static NSURL *TUIURLForNameAndScaleFactor(NSString *name, CGFloat scaleFactor) -{ - // simplest thing that works for now - // todo - understand the details of what UIKit does, mimic. - NSString *ext = [name pathExtension]; - NSString *baseName = [name stringByDeletingPathExtension]; - if(scaleFactor == 2.0) { - name = [[baseName stringByAppendingString:@"@2x"] stringByAppendingPathExtension:ext]; - } else { - name = [baseName stringByAppendingPathExtension:ext]; - } - return [[[NSBundle mainBundle] resourceURL] URLByAppendingPathComponent:name]; -} - -static CGImageRef TUICreateImageRefForNameAndScaleFactor(NSString *name, CGFloat scaleFactor, BOOL shouldCache, CGFloat *imageScaleFactor) -{ - if(name) { - CGImageRef i = NULL; - if(scaleFactor == 2.0) { - i = TUICreateImageRefForURL(TUIURLForNameAndScaleFactor(name, scaleFactor), shouldCache); - if(i) { - if(imageScaleFactor != NULL) *imageScaleFactor = scaleFactor; - return i; - } - } - // fallback - i = TUICreateImageRefForURL(TUIURLForNameAndScaleFactor(name, 1.0), shouldCache); - if(imageScaleFactor != NULL) *imageScaleFactor = 1.0f; - return i; - } - - return NULL; -} - -@interface TUIStretchableImage : TUIImage -{ - @public - NSInteger leftCapWidth; - NSInteger topCapHeight; - @private - __strong TUIImage *slices[9]; - struct { - unsigned int haveSlices:1; - } _flags; -} -@end - - -@implementation TUIImage -{ - CGFloat _lastContextScaleFactor; - CGFloat _imageScaleFactor; - NSString *_imageName; - CGImageRef _imageRef; - BOOL _shouldCache; -} - -- (id)init -{ - if(self = [super init]) { - _lastContextScaleFactor = 1.0; - _imageScaleFactor = 1.0f; - } - return self; -} - -- (id)initWithCGImage:(CGImageRef)imageRef -{ - if(self = [self init]) { - if(imageRef) - _imageRef = CGImageRetain(imageRef); - } - return self; -} - -- (id)initWithName:(NSString *)name cache:(BOOL)shouldCache -{ - if(self = [self init]) { - _imageName = name; - _shouldCache = shouldCache; - } - return self; -} - -- (id)initWithData:(NSData *)data -{ - CGImageRef i = TUICreateImageRefWithData(data); - if(i) { - self = [self initWithCGImage:i]; - CGImageRelease(i); - return self; - } - return nil; -} - -+ (TUIImage *)_imageWithABImage:(id)abimage -{ - return [self imageWithCGImage:[abimage CGImage]]; -} - -+ (TUIImage *)imageNamed:(NSString *)name cache:(BOOL)shouldCache -{ - return [[self alloc] initWithName:name cache:shouldCache]; -} - -+ (TUIImage *)imageNamed:(NSString *)name -{ - return [self imageNamed:name cache:NO]; // differs from default UIKit, use explicit cache: for caching -} - -+ (TUIImage *)imageWithData:(NSData *)data -{ - return [[self alloc] initWithData:data]; -} - -- (void)dealloc -{ - if(_imageRef) - CGImageRelease(_imageRef); -} - -+ (TUIImage *)imageWithCGImage:(CGImageRef)imageRef -{ - return [[self alloc] initWithCGImage:imageRef]; -} - -/** - * @brief Create a new TUIImage from an NSImage - * - * @note Don't use this method in -drawRect: if you use a NSGraphicsContext. This method may - * change the current context in order to convert the image and will not restore any previous - * context. - * - * @param image an NSImage - * @return TUIImage - */ -+ (TUIImage *)imageWithNSImage:(NSImage *)image -{ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 - return [self imageWithCGImage:[image CGImageForProposedRect:NULL context:NULL hints:nil]]; -#elif MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 - // first, attempt to find an NSBitmapImageRep representation, (the easy way) - for(NSImageRep *rep in [image representations]){ - CGImageRef cgImage; - if([rep isKindOfClass:[NSBitmapImageRep class]] && (cgImage = [(NSBitmapImageRep *)rep CGImage]) != nil){ - return [[TUIImage alloc] initWithCGImage:cgImage]; - } - } -#endif - - // if that didn't work, we have to render the image to a context and create the CGImage - // from that (the hard way) - TUIImage *result = nil; - - CGColorSpaceRef colorspace = NULL; - CGContextRef context = NULL; - CGBitmapInfo info = kCGImageAlphaPremultipliedLast; - - size_t width = (size_t)ceil(image.size.width); - size_t height = (size_t)ceil(image.size.height); - size_t bytesPerPixel = 4; - size_t bitsPerComponent = 8; - - // create a colorspace for our image - if((colorspace = CGColorSpaceCreateDeviceRGB()) != NULL){ - // create a context for our image using premultiplied RGBA - if((context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, width * bytesPerPixel, colorspace, info)) != NULL){ - - // setup an NSGraphicsContext for our bitmap context and render our NSImage into it - [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:FALSE]]; - [image drawAtPoint:CGPointMake(0, 0) fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1]; - - // create an image from the context and use that to create our TUIImage - CGImageRef cgImage; - if((cgImage = CGBitmapContextCreateImage(context)) != NULL){ - result = [[TUIImage alloc] initWithCGImage:cgImage]; - CFRelease(cgImage); - } - - CFRelease(context); - } - CFRelease(colorspace); - } - - // return the (hopefully sucessfully initialized) TUIImage - return result; -} - -- (CGSize)size -{ - CGImageRef cgImage = [self CGImage]; - CGFloat inv = 1.0f / _imageScaleFactor; // must call -CGImage first (will update _lastScaleFactor) - if(cgImage) - return CGSizeMake(CGImageGetWidth(cgImage) * inv, CGImageGetHeight(cgImage) * inv); - return CGSizeZero; -} - -- (CGFloat)scale -{ - [self CGImage]; // update _lastScaleFactor if needed - return _lastContextScaleFactor; -} - -- (CGImageRef)CGImage -{ - if(_imageName) { // lazy image - CGFloat currentScaleFactor = TUICurrentContextScaleFactor(); - if(!_imageRef || (_lastContextScaleFactor != currentScaleFactor)) { - // if we haven't loaded an image yet, or the scale factor changed, load a new one - if(_imageRef) - CGImageRelease(_imageRef); - _imageRef = CGImageRetain(TUICreateImageRefForNameAndScaleFactor(_imageName, currentScaleFactor, _shouldCache, &_imageScaleFactor)); - _lastContextScaleFactor = currentScaleFactor; - } - } - return _imageRef; -} - -- (void)drawAtPoint:(CGPoint)point // mode = kCGBlendModeNormal, alpha = 1.0 -{ - [self drawAtPoint:point blendMode:kCGBlendModeNormal alpha:1.0]; -} - -- (void)drawAtPoint:(CGPoint)point blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha -{ - CGRect rect; - rect.origin = point; - rect.size = [self size]; - [self drawInRect:rect blendMode:blendMode alpha:alpha]; -} - -- (void)drawInRect:(CGRect)rect // mode = kCGBlendModeNormal, alpha = 1.0 -{ - [self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0]; -} - -- (void)drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha -{ - CGImageRef cgImage = [self CGImage]; // -CGImage will automatically update itself with the correct image - if(cgImage) { - CGContextRef ctx = TUIGraphicsGetCurrentContext(); - CGContextSaveGState(ctx); - CGContextSetAlpha(ctx, alpha); - CGContextSetBlendMode(ctx, blendMode); - CGContextDrawImage(ctx, rect, cgImage); - CGContextRestoreGState(ctx); - } -} - -- (NSInteger)leftCapWidth -{ - return 0; -} - -- (NSInteger)topCapHeight -{ - return 0; -} - -- (TUIImage *)stretchableImageWithLeftCapWidth:(NSInteger)leftCapWidth topCapHeight:(NSInteger)topCapHeight -{ - TUIStretchableImage *i = (TUIStretchableImage *)[TUIStretchableImage imageWithCGImage:[self CGImage]]; - i->leftCapWidth = leftCapWidth; - i->topCapHeight = topCapHeight; - return i; -} - -- (NSData *)dataRepresentationForType:(NSString *)type compression:(CGFloat)compressionQuality -{ - CGImageRef cgImage = [self CGImage]; - if(cgImage) { - NSMutableData *mutableData = [NSMutableData data]; - CGImageDestinationRef destination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)mutableData, (__bridge CFStringRef)type, 1, NULL); - - NSDictionary *properties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat:compressionQuality], kCGImageDestinationLossyCompressionQuality, nil]; - CGImageDestinationAddImage(destination, cgImage, (__bridge CFDictionaryRef)properties); - - CGImageDestinationFinalize(destination); - CFRelease(destination); - return mutableData; - } - return nil; -} - -@end - - - - -@implementation TUIStretchableImage - -- (NSInteger)leftCapWidth -{ - return leftCapWidth; -} - -- (NSInteger)topCapHeight -{ - return topCapHeight; -} - -/* - - x0 x1 x2 x3 - +-------+-------+-------+ y3 - | | | | - | 6 | 7 | 8 | - | | | | - +-------+-------+-------+ y2 - | | | | - | 3 | 4 | 5 | - | | | | - +-------+-------+-------+ y1 - | | | | - | 0 | 1 | 2 | - | | | | - +-------+-------+-------+ y0 - - */ - -#define STRETCH_COORDS(X0, Y0, W, H, TOP, LEFT, BOTTOM, RIGHT) \ - CGFloat x0 = X0; \ - CGFloat x1 = X0 + LEFT; \ - CGFloat x2 = X0 + W - RIGHT; \ - CGFloat x3 = X0 + W; \ - CGFloat y0 = Y0; \ - CGFloat y1 = Y0 + BOTTOM; \ - CGFloat y2 = Y0 + H - TOP; \ - CGFloat y3 = Y0 + H; \ - CGRect r[9]; \ - r[0] = CGRectMake(x0, y0, x1-x0, y1-y0); \ - r[1] = CGRectMake(x1, y0, x2-x1, y1-y0); \ - r[2] = CGRectMake(x2, y0, x3-x2, y1-y0); \ - r[3] = CGRectMake(x0, y1, x1-x0, y2-y1); \ - r[4] = CGRectMake(x1, y1, x2-x1, y2-y1); \ - r[5] = CGRectMake(x2, y1, x3-x2, y2-y1); \ - r[6] = CGRectMake(x0, y2, x1-x0, y3-y2); \ - r[7] = CGRectMake(x1, y2, x2-x1, y3-y2); \ - r[8] = CGRectMake(x2, y2, x3-x2, y3-y2); - -- (void)drawInRect:(CGRect)rect blendMode:(CGBlendMode)blendMode alpha:(CGFloat)alpha -{ - CGImageRef cgImage = [self CGImage]; - if(cgImage) { - CGSize s = self.size; - CGFloat t = topCapHeight; - CGFloat l = leftCapWidth; - - if(t*2 > s.height-1) t -= 1; - if(l*2 > s.width-1) l -= 1; - - if(!_flags.haveSlices) { - STRETCH_COORDS(0.0, 0.0, s.width, s.height, t, l, t, l) - #define X(I) slices[I] = [self upsideDownCrop:r[I]]; - X(0) X(1) X(2) - X(3) X(4) X(5) - X(6) X(7) X(8) - #undef X - _flags.haveSlices = 1; - } - - CGContextRef ctx = TUIGraphicsGetCurrentContext(); - CGContextSaveGState(ctx); - CGContextSetAlpha(ctx, alpha); - CGContextSetBlendMode(ctx, blendMode); - STRETCH_COORDS(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, t, l, t, l) - #define X(I) CGContextDrawImage(ctx, r[I], slices[I].CGImage); - X(0) X(1) X(2) - X(3) X(4) X(5) - X(6) X(7) X(8) - #undef X - CGContextRestoreGState(ctx); - } -} - -#undef STRETCH_COORDS - -@end - -NSData *TUIImagePNGRepresentation(TUIImage *image) -{ - return [image dataRepresentationForType:(NSString *)kUTTypePNG compression:1.0]; -} - -NSData *TUIImageJPEGRepresentation(TUIImage *image, CGFloat compressionQuality) -{ - return [image dataRepresentationForType:(NSString *)kUTTypeJPEG compression:compressionQuality]; -} - -#import - -@implementation TUIImage (AppKit) - -- (id)nsImage -{ - return [[NSImage alloc] initWithCGImage:self.CGImage size:NSZeroSize]; -} - -@end - diff --git a/lib/UIKit/TUIImageView.h b/lib/UIKit/TUIImageView.h index deceafaf..0ecd41e8 100644 --- a/lib/UIKit/TUIImageView.h +++ b/lib/UIKit/TUIImageView.h @@ -16,15 +16,10 @@ #import "TUIView.h" -@class TUIImage; - @interface TUIImageView : TUIView -{ - TUIImage *_image; -} -- (id)initWithImage:(TUIImage *)image; +- (id)initWithImage:(NSImage *)image; -@property(nonatomic,strong) TUIImage *image; +@property(nonatomic, strong) NSImage *image; @end diff --git a/lib/UIKit/TUIImageView.m b/lib/UIKit/TUIImageView.m index b6ab76f6..87c4551f 100644 --- a/lib/UIKit/TUIImageView.m +++ b/lib/UIKit/TUIImageView.m @@ -15,30 +15,28 @@ */ #import "TUIImageView.h" -#import "TUIImage.h" @implementation TUIImageView +@synthesize image = _image; -- (id)initWithImage:(TUIImage *)image +- (void)setImage:(NSImage *)i { - if((self = [self initWithFrame:(image != nil) ? CGRectMake(0, 0, image.size.width, image.size.height) : CGRectZero]) != nil) - { - self.userInteractionEnabled = NO; - _image = image; - } - return self; + _image = i; + [self setNeedsDisplay]; } - -- (TUIImage *)image +- (id)initWithImage:(NSImage *)image { - return _image; -} + CGRect frame = CGRectZero; + if (image) frame = CGRectMake(0, 0, image.size.width, image.size.height); -- (void)setImage:(TUIImage *)i -{ - _image = i; - [self setNeedsDisplay]; + self = [super initWithFrame:frame]; + if (self == nil) return nil; + + self.userInteractionEnabled = NO; + _image = image; + + return self; } - (void)drawRect:(CGRect)rect @@ -47,7 +45,7 @@ - (void)drawRect:(CGRect)rect if (_image == nil) return; - [_image drawInRect:rect]; + [_image drawInRect:rect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0]; } - (CGSize)sizeThatFits:(CGSize)size { diff --git a/lib/UIKit/TUIKit.h b/lib/UIKit/TUIKit.h index 373c3824..c99d4047 100644 --- a/lib/UIKit/TUIKit.h +++ b/lib/UIKit/TUIKit.h @@ -19,6 +19,7 @@ #import "CAAnimation+TUIExtensions.h" #import "CoreText+Additions.h" #import "NSClipView+TUIExtensions.h" +#import "NSImage+TUIExtensions.h" #import "NSScrollView+TUIExtensions.h" #import "NSView+TUIExtensions.h" #import "TUIActivityIndicatorView.h" @@ -30,7 +31,6 @@ #import "TUIColor.h" #import "TUIFastIndexPath.h" #import "TUIHostView.h" -#import "TUIImage.h" #import "TUIImageView.h" #import "TUILabel.h" #import "TUILayoutConstraint.h" @@ -43,6 +43,7 @@ #import "TUIResponder.h" #import "TUIScrollView.h" #import "TUIScrollView+TUIBridgedScrollView.h" +#import "TUIStretchableImage.h" #import "TUIStringDrawing.h" #import "TUIStyledView.h" #import "TUITableView+Additions.h" diff --git a/lib/UIKit/TUIStretchableImage.h b/lib/UIKit/TUIStretchableImage.h new file mode 100644 index 00000000..6206d6c3 --- /dev/null +++ b/lib/UIKit/TUIStretchableImage.h @@ -0,0 +1,33 @@ +/* + 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 +#import "TUIGeometry.h" + +/* + * An image that supports resizing based on end caps. + */ +@interface TUIStretchableImage : NSImage + +/* + * The end cap insets for the image. + * + * Any portion of the image not covered by end caps will be tiled when the image + * is drawn. + */ +@property (nonatomic, assign) TUIEdgeInsets capInsets; + +@end diff --git a/lib/UIKit/TUIStretchableImage.m b/lib/UIKit/TUIStretchableImage.m new file mode 100644 index 00000000..0a9299b2 --- /dev/null +++ b/lib/UIKit/TUIStretchableImage.m @@ -0,0 +1,181 @@ +/* + 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 "TUIStretchableImage.h" + +@implementation TUIStretchableImage + +#pragma mark Properties + +@synthesize capInsets = _capInsets; + +#pragma mark Drawing + +- (void)drawInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)alpha { + [self drawInRect:dstRect fromRect:srcRect operation:op fraction:alpha respectFlipped:YES hints:nil]; +} + +- (void)drawInRect:(NSRect)dstRect fromRect:(NSRect)srcRect operation:(NSCompositingOperation)op fraction:(CGFloat)alpha respectFlipped:(BOOL)respectFlipped hints:(NSDictionary *)hints { + CGImageRef image = [self CGImageForProposedRect:&dstRect context:[NSGraphicsContext currentContext] hints:hints]; + if (image == NULL) { + NSLog(@"*** Could not get CGImage of %@", self); + return; + } + + CGSize size = CGSizeMake(CGImageGetWidth(image), CGImageGetHeight(image)); + TUIEdgeInsets insets = self.capInsets; + + // TODO: Cache the nine-part images for this common case of wanting to draw + // the whole source image. + if (CGRectIsEmpty(srcRect)) { + // Match the image creation that occurs in the 'else' clause. + CGImageRetain(image); + } else { + image = CGImageCreateWithImageInRect(image, srcRect); + if (!image) return; + + // Reduce insets to account for taking only part of the original image. + insets.left = fmax(0, insets.left - CGRectGetMinX(srcRect)); + insets.bottom = fmax(0, insets.bottom - CGRectGetMinY(srcRect)); + + CGFloat srcRightInset = size.width - CGRectGetMaxX(srcRect); + insets.right = fmax(0, insets.right - srcRightInset); + + CGFloat srcTopInset = size.height - CGRectGetMaxY(srcRect); + insets.top = fmax(0, insets.top - srcTopInset); + } + + NSImage *topLeft = nil, *topEdge = nil, *topRight = nil; + NSImage *leftEdge = nil, *center = nil, *rightEdge = nil; + NSImage *bottomLeft = nil, *bottomEdge = nil, *bottomRight = nil; + + // Length of sides that run vertically. + CGFloat verticalEdgeLength = fmax(0, size.height - insets.top - insets.bottom); + + // Length of sides that run horizontally. + CGFloat horizontalEdgeLength = fmax(0, size.height - insets.left - insets.right); + + NSImage *(^imageWithRect)(CGRect) = ^ id (CGRect rect){ + CGImageRef part = CGImageCreateWithImageInRect(image, rect); + if (part == NULL) return nil; + + NSImage *image = [[NSImage alloc] initWithCGImage:part size:rect.size]; + CGImageRelease(part); + + return image; + }; + + if (verticalEdgeLength > 0) { + if (insets.left > 0) { + CGRect partRect = CGRectMake(0, insets.bottom, insets.left, verticalEdgeLength); + leftEdge = imageWithRect(partRect); + } + + if (insets.right > 0) { + CGRect partRect = CGRectMake(size.width - insets.right, insets.bottom, insets.right, verticalEdgeLength); + rightEdge = imageWithRect(partRect); + } + } + + if (horizontalEdgeLength > 0) { + if (insets.bottom > 0) { + CGRect partRect = CGRectMake(insets.left, 0, horizontalEdgeLength, insets.bottom); + bottomEdge = imageWithRect(partRect); + } + + if (insets.top > 0) { + CGRect partRect = CGRectMake(insets.left, size.height - insets.top, horizontalEdgeLength, insets.top); + topEdge = imageWithRect(partRect); + } + } + + if (insets.left > 0 && insets.top > 0) { + CGRect partRect = CGRectMake(0, size.height - insets.top, insets.left, insets.top); + topLeft = imageWithRect(partRect); + } + + if (insets.left > 0 && insets.bottom > 0) { + CGRect partRect = CGRectMake(0, 0, insets.left, insets.bottom); + bottomLeft = imageWithRect(partRect); + } + + if (insets.right > 0 && insets.top > 0) { + CGRect partRect = CGRectMake(size.width - insets.right, size.height - insets.top, insets.right, insets.top); + topRight = imageWithRect(partRect); + } + + if (insets.right > 0 && insets.bottom > 0) { + CGRect partRect = CGRectMake(size.width - insets.right, 0, insets.right, insets.bottom); + bottomRight = imageWithRect(partRect); + } + + CGRect centerRect = TUIEdgeInsetsInsetRect(CGRectMake(0, 0, size.width, size.height), insets); + if (centerRect.size.width > 0 && centerRect.size.height > 0) { + center = imageWithRect(centerRect); + } + + CGImageRelease(image); + + BOOL flipped = NO; + if (respectFlipped) { + flipped = [[NSGraphicsContext currentContext] isFlipped]; + } + + if (topLeft != nil || bottomRight != nil) { + NSDrawNinePartImage(dstRect, topLeft, topEdge, topRight, leftEdge, center, rightEdge, bottomLeft, bottomEdge, bottomRight, op, alpha, flipped); + } else if (leftEdge != nil) { + // Horizontal three-part image. + NSDrawThreePartImage(dstRect, leftEdge, center, rightEdge, NO, op, alpha, flipped); + } else { + // Vertical three-part image. + NSDrawThreePartImage(dstRect, topEdge, center, bottomEdge, YES, op, alpha, flipped); + } +} + +#pragma mark NSCopying + +- (id)copyWithZone:(NSZone *)zone { + TUIStretchableImage *image = [super copyWithZone:zone]; + image.capInsets = self.capInsets; + return image; +} + +#pragma mark NSCoding + +- (id)initWithCoder:(NSCoder *)coder { + self = [super initWithCoder:coder]; + if (self == nil) return nil; + + self.capInsets = TUIEdgeInsetsMake( + [coder decodeFloatForKey:@"capInsetTop"], + [coder decodeFloatForKey:@"capInsetLeft"], + [coder decodeFloatForKey:@"capInsetBottom"], + [coder decodeFloatForKey:@"capInsetRight"] + ); + + return self; +} + +- (void)encodeWithCoder:(NSCoder *)coder { + [super encodeWithCoder:coder]; + + [coder encodeFloat:self.capInsets.top forKey:@"capInsetTop"]; + [coder encodeFloat:self.capInsets.left forKey:@"capInsetLeft"]; + [coder encodeFloat:self.capInsets.bottom forKey:@"capInsetBottom"]; + [coder encodeFloat:self.capInsets.right forKey:@"capInsetRight"]; +} + +@end diff --git a/lib/UIKit/TUITextField.m b/lib/UIKit/TUITextField.m index 8384e247..6a05e627 100644 --- a/lib/UIKit/TUITextField.m +++ b/lib/UIKit/TUITextField.m @@ -16,7 +16,6 @@ #import "TUITextField.h" #import "TUIButton.h" -#import "TUIImage.h" #import "TUITextViewEditor.h" @interface TUITextFieldEditor : TUITextViewEditor @@ -79,7 +78,7 @@ - (void)clear:(id)sender - (TUIButton *)clearButton { TUIButton *b = [TUIButton button]; - [b setImage:[TUIImage imageNamed:@"clear-button.png" cache:YES] forState:TUIControlStateNormal]; + [b setImage:[NSImage imageNamed:@"clear-button.png"] forState:TUIControlStateNormal]; [b addTarget:self action:@selector(clear:) forControlEvents:TUIControlEventTouchUpInside]; return b; } diff --git a/lib/UIKit/TUITextRenderer+Event.m b/lib/UIKit/TUITextRenderer+Event.m index 4f05b683..4a210453 100644 --- a/lib/UIKit/TUITextRenderer+Event.m +++ b/lib/UIKit/TUITextRenderer+Event.m @@ -18,7 +18,6 @@ #import "ABActiveRange.h" #import "CoreText+Additions.h" #import "TUICGAdditions.h" -#import "TUIImage.h" #import "TUINSView.h" #import "TUINSWindow.h" #import "TUIView+Private.h" @@ -87,12 +86,12 @@ - (CFIndex)stringIndexForEvent:(NSEvent *)event return nil; } -- (TUIImage *)dragImageForSelection:(NSRange)selection +- (NSImage *)dragImageForSelection:(NSRange)selection { CGRect b = self.view.frame; _flags.drawMaskDragSelection = 1; - TUIImage *image = TUIGraphicsDrawAsImage(b.size, ^{ + NSImage *image = TUIGraphicsDrawAsImage(b.size, ^{ [self draw]; }); _flags.drawMaskDragSelection = 0; @@ -117,12 +116,10 @@ - (BOOL)beginWaitForDragInRange:(NSRange)range string:(NSString *)string CFIndex saveEnd = _selectionEnd; _selectionStart = range.location; _selectionEnd = range.location + range.length; - TUIImage *dragImage = [self dragImageForSelection:range]; + NSImage *image = [self dragImageForSelection:range]; _selectionStart = saveStart; _selectionEnd = saveEnd; - NSImage *image = [[NSImage alloc] initWithCGImage:dragImage.CGImage size:NSZeroSize]; - [view.nsView dragImage:image at:f.origin offset:NSZeroSize diff --git a/lib/UIKit/TUIView+PasteboardDragging.m b/lib/UIKit/TUIView+PasteboardDragging.m index 10338ad4..8fb65ac5 100644 --- a/lib/UIKit/TUIView+PasteboardDragging.m +++ b/lib/UIKit/TUIView+PasteboardDragging.m @@ -16,7 +16,6 @@ #import "TUIView+PasteboardDragging.h" #import "TUICGAdditions.h" -#import "TUIImage.h" #import "TUINSView.h" @implementation TUIView (PasteboardDragging) @@ -64,12 +63,10 @@ - (void)pasteboardDragMouseDragged:(NSEvent *)event TUIView *dragView = [self handleForPasteboardDragView]; id pasteboardObject = [dragView representedPasteboardObject]; - TUIImage *dragImage = TUIGraphicsDrawAsImage(dragView.frame.size, ^{ - [TUIGraphicsGetImageForView(dragView) drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:0.75]; + NSImage *dragNSImage = TUIGraphicsDrawAsImage(dragView.frame.size, ^{ + [TUIGraphicsGetImageForView(dragView) drawAtPoint:CGPointZero fromRect:CGRectZero operation:NSCompositeSourceOver fraction:0.75]; }); - NSImage *dragNSImage = [[NSImage alloc] initWithCGImage:dragImage.CGImage size:NSZeroSize]; - NSPasteboard *pasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; [pasteboard clearContents]; [pasteboard writeObjects:[NSArray arrayWithObject:pasteboardObject]]; diff --git a/lib/UIKit/TUIView.m b/lib/UIKit/TUIView.m index bc62a36c..183c768d 100644 --- a/lib/UIKit/TUIView.m +++ b/lib/UIKit/TUIView.m @@ -18,7 +18,6 @@ #import #import "TUICGAdditions.h" #import "TUIColor.h" -#import "TUIImage.h" #import "TUILayoutManager.h" #import "TUINSView.h" #import "TUINSWindow.h" @@ -393,8 +392,7 @@ - (void)displayLayer:(CALayer *)layer } #endif - TUIImage *image = TUIGraphicsGetImageFromCurrentImageContext(); - layer.contents = (id)image.CGImage; + layer.contents = TUIGraphicsGetImageFromCurrentImageContext(); CGContextScaleCTM(context, 1.0f / scale, 1.0f / scale); TUIGraphicsPopContext();