Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,11 @@ Additionally, if you merely desire to know the `UIEdgeInsets` of the transparenc

`[yourImage transparencyInsetsRequiringFullOpacity:YES];`

This call works based on the same principles as the "advanced" trim method, with the boolean dictating whether non-opaque pixels should be considered transparent.
This call works based on the same principles as the "advanced" trim method, with the boolean dictating whether non-opaque pixels should be considered transparent.


This fork adds support for trimming white area around image with given tolerance

`[yourImage imageByTrimmingWhitePixelsWithOpacity:VALUE];`

where 0 - cut only white, 255 - remove everything
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
6B06373E177B30D900B55AC5 /* UIImage+Trim.m in Sources */ = {isa = PBXBuildFile; fileRef = 6B06373D177B30D900B55AC5 /* UIImage+Trim.m */; };
6B063741177B33B900B55AC5 /* demo_image.png in Resources */ = {isa = PBXBuildFile; fileRef = 6B06373F177B33B900B55AC5 /* demo_image.png */; };
6B063742177B33B900B55AC5 /* demo_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 6B063740177B33B900B55AC5 /* demo_image@2x.png */; };
9F31271D18C1239E008CD497 /* demo_white_image.png in Resources */ = {isa = PBXBuildFile; fileRef = 9F31271B18C1239E008CD497 /* demo_white_image.png */; };
9F31271E18C1239E008CD497 /* demo_white_image@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 9F31271C18C1239E008CD497 /* demo_white_image@2x.png */; };
9F31272118C123D5008CD497 /* ELCWhiteViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F31272018C123D5008CD497 /* ELCWhiteViewController.m */; };
9F31272418C123DE008CD497 /* ELCWhiteViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9F31272218C123DE008CD497 /* ELCWhiteViewController.xib */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -44,6 +48,11 @@
6B06373D177B30D900B55AC5 /* UIImage+Trim.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Trim.m"; sourceTree = "<group>"; };
6B06373F177B33B900B55AC5 /* demo_image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = demo_image.png; sourceTree = "<group>"; };
6B063740177B33B900B55AC5 /* demo_image@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "demo_image@2x.png"; sourceTree = "<group>"; };
9F31271B18C1239E008CD497 /* demo_white_image.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = demo_white_image.png; sourceTree = "<group>"; };
9F31271C18C1239E008CD497 /* demo_white_image@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "demo_white_image@2x.png"; sourceTree = "<group>"; };
9F31271F18C123D5008CD497 /* ELCWhiteViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ELCWhiteViewController.h; sourceTree = "<group>"; };
9F31272018C123D5008CD497 /* ELCWhiteViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ELCWhiteViewController.m; sourceTree = "<group>"; };
9F31272318C123DE008CD497 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ELCWhiteViewController.xib; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -97,6 +106,9 @@
6B063731177B30C400B55AC5 /* ELCViewController.h */,
6B063732177B30C400B55AC5 /* ELCViewController.m */,
6B063734177B30C400B55AC5 /* ELCViewController.xib */,
9F31271F18C123D5008CD497 /* ELCWhiteViewController.h */,
9F31272018C123D5008CD497 /* ELCWhiteViewController.m */,
9F31272218C123DE008CD497 /* ELCWhiteViewController.xib */,
6B063720177B30C400B55AC5 /* Supporting Files */,
);
path = "Transparency Trim Demo";
Expand All @@ -107,6 +119,8 @@
children = (
6B06373F177B33B900B55AC5 /* demo_image.png */,
6B063740177B33B900B55AC5 /* demo_image@2x.png */,
9F31271B18C1239E008CD497 /* demo_white_image.png */,
9F31271C18C1239E008CD497 /* demo_white_image@2x.png */,
6B063721177B30C400B55AC5 /* Transparency Trim Demo-Info.plist */,
6B063722177B30C400B55AC5 /* InfoPlist.strings */,
6B063725177B30C400B55AC5 /* main.m */,
Expand Down Expand Up @@ -171,11 +185,14 @@
buildActionMask = 2147483647;
files = (
6B063724177B30C400B55AC5 /* InfoPlist.strings in Resources */,
9F31271E18C1239E008CD497 /* demo_white_image@2x.png in Resources */,
6B06372C177B30C400B55AC5 /* Default.png in Resources */,
6B06372E177B30C400B55AC5 /* Default@2x.png in Resources */,
6B063730177B30C400B55AC5 /* Default-568h@2x.png in Resources */,
9F31272418C123DE008CD497 /* ELCWhiteViewController.xib in Resources */,
6B063736177B30C400B55AC5 /* ELCViewController.xib in Resources */,
6B063741177B33B900B55AC5 /* demo_image.png in Resources */,
9F31271D18C1239E008CD497 /* demo_white_image.png in Resources */,
6B063742177B33B900B55AC5 /* demo_image@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -190,6 +207,7 @@
6B063726177B30C400B55AC5 /* main.m in Sources */,
6B06372A177B30C400B55AC5 /* ELCAppDelegate.m in Sources */,
6B063733177B30C400B55AC5 /* ELCViewController.m in Sources */,
9F31272118C123D5008CD497 /* ELCWhiteViewController.m in Sources */,
6B06373E177B30D900B55AC5 /* UIImage+Trim.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -213,6 +231,14 @@
name = ELCViewController.xib;
sourceTree = "<group>";
};
9F31272218C123DE008CD497 /* ELCWhiteViewController.xib */ = {
isa = PBXVariantGroup;
children = (
9F31272318C123DE008CD497 /* en */,
);
name = ELCWhiteViewController.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */

/* Begin XCBuildConfiguration section */
Expand Down Expand Up @@ -345,6 +371,7 @@
6B06373B177B30C400B55AC5 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

#import <UIKit/UIKit.h>

@class ELCViewController;
@class UIViewController;

@interface ELCAppDelegate : UIResponder <UIApplicationDelegate>

@property (strong, nonatomic) UIWindow *window;

@property (strong, nonatomic) ELCViewController *viewController;
@property (strong, nonatomic) UIViewController *viewController;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@

#import "ELCAppDelegate.h"

#import "ELCViewController.h"
#import "ELCWhiteViewController.h"

@implementation ELCAppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
self.viewController = [[ELCViewController alloc] initWithNibName:@"ELCViewController" bundle:nil];
self.viewController = [[ELCWhiteViewController alloc] initWithNibName:@"ELCWhiteViewController" bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// ELCWhiteViewController.h
// Transparency Trim Demo
//
// Created by Blazej Stanek on 28.02.2014.
// Copyright (c) 2013 Chris Stroud. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ELCWhiteViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIImageView *semiTransparentImageView;
@property (weak, nonatomic) IBOutlet UIImageView *originalImageView;
@property (weak, nonatomic) IBOutlet UIImageView *fullyOpaqueImageView;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// ELCWhiteViewController.m
// Transparency Trim Demo
//
// Created by Blazej Stanek on 28.02.2014.
// Copyright (c) 2013 Chris Stroud. All rights reserved.
//

#import "ELCWhiteViewController.h"
#import "UIImage+Trim.h"

@implementation ELCWhiteViewController

- (void)viewDidLoad
{
[super viewDidLoad];

UIImage *originalImage = [UIImage imageNamed:@"demo_white_image"];
UIImage *semiTransparentCrop = [originalImage imageByTrimmingWhitePixelsWithOpacity:24];
UIImage *fullOpacityCrop = [originalImage imageByTrimmingWhitePixelsWithOpacity:64];

[self.originalImageView setImage:originalImage];
[self.semiTransparentImageView setImage:semiTransparentCrop];
[self.fullyOpaqueImageView setImage:fullOpacityCrop];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
- (UIEdgeInsets)transparencyInsetsRequiringFullOpacity:(BOOL)fullyOpaque;
- (UIImage *)imageByTrimmingTransparentPixels;
- (UIImage *)imageByTrimmingTransparentPixelsRequiringFullOpacity:(BOOL)fullyOpaque;
- (UIImage *)imageByTrimmingWhitePixelsWithOpacity:(UInt8)tolerance;

@end
137 changes: 136 additions & 1 deletion Transparency Trim Demo/Transparency Trim Demo/UIImage+Trim.m
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ - (UIEdgeInsets)transparencyInsetsRequiringFullOpacity:(BOOL)fullyOpaque
uint8_t * bitmapData = calloc((size_t)(width * height), sizeof(uint8_t));

// Create alpha-only bitmap context
CGContextRef contextRef = CGBitmapContextCreate(bitmapData, (NSUInteger)width, (NSUInteger)height, 8, (NSUInteger)bytesPerRow, NULL, kCGImageAlphaOnly);
CGContextRef contextRef = CGBitmapContextCreate(bitmapData, (NSUInteger)width, (NSUInteger)height, 8, (NSUInteger)bytesPerRow, NULL, (kCGBitmapAlphaInfoMask & kCGImageAlphaOnly));

CGImageRef cgImage = self.CGImage;
CGRect rect = CGRectMake(0, 0, width, height);
Expand Down Expand Up @@ -172,4 +172,139 @@ - (UIImage *)imageByTrimmingTransparentPixelsRequiringFullOpacity:(BOOL)fullyOpa
return img;
}

/*
* Calculates the insets of white area around all sides of the image
*
* @param tolerance
* Maximal difference from white
*/
- (UIEdgeInsets)transparencyInsetsByCuttingWhitespace:(UInt8)tolerance
{
// Draw our image on that context
NSInteger width = (NSInteger)CGImageGetWidth([self CGImage]);
NSInteger height = (NSInteger)CGImageGetHeight([self CGImage]);
NSInteger bytesPerRow = width * (NSInteger)sizeof(uint8_t);

// Allocate array to hold alpha channel
uint8_t * bitmapData = calloc((size_t)(width * height), sizeof(uint8_t));

// Create grayscale image
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGContextRef contextRef = CGBitmapContextCreate(bitmapData, (NSUInteger)width, (NSUInteger)height, 8, (NSUInteger)bytesPerRow, colorSpace, (kCGBitmapAlphaInfoMask & kCGImageAlphaNone));

CGImageRef cgImage = self.CGImage;
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(contextRef, rect, cgImage);

// Sum all non-transparent pixels in every row and every column
uint16_t * rowSum = calloc((size_t)height, sizeof(uint16_t));
uint16_t * colSum = calloc((size_t)width, sizeof(uint16_t));

// Enumerate through all pixels
for (NSInteger row = 0; row < height; row++) {

for (NSInteger col = 0; col < width; col++) {

// Found darker pixel
if (bitmapData[row*bytesPerRow + col] <= UINT8_MAX - tolerance) {

rowSum[row]++;
colSum[col]++;

}
}
}

// Initialize crop insets and enumerate cols/rows arrays until we find non-empty columns or row
UIEdgeInsets crop = UIEdgeInsetsZero;

// Top
for (NSInteger i = 0; i < height; i++) {

if (rowSum[i] > 0) {

crop.top = i;
break;

}

}

// Bottom
for (NSInteger i = height - 1; i >= 0; i--) {

if (rowSum[i] > 0) {
crop.bottom = MAX(0, height - i - 1);
break;
}

}

// Left
for (NSInteger i = 0; i < width; i++) {

if (colSum[i] > 0) {
crop.left = i;
break;
}

}

// Right
for (NSInteger i = width - 1; i >= 0; i--) {

if (colSum[i] > 0) {

crop.right = MAX(0, width - i - 1);
break;

}
}

free(bitmapData);
free(colSum);
free(rowSum);

CGContextRelease(contextRef);

return crop;
}

- (UIImage *)imageByTrimmingWhitePixelsWithOpacity:(UInt8)tolerance
{
if (self.size.height < 2 || self.size.width < 2) {

return self;

}

CGRect rect = CGRectMake(0, 0, self.size.width * self.scale, self.size.height * self.scale);
UIEdgeInsets crop = [self transparencyInsetsByCuttingWhitespace:tolerance];

UIImage *img = self;

if (crop.top == 0 && crop.bottom == 0 && crop.left == 0 && crop.right == 0) {

// No cropping needed

} else {

// Calculate new crop bounds
rect.origin.x += crop.left;
rect.origin.y += crop.top;
rect.size.width -= crop.left + crop.right;
rect.size.height -= crop.top + crop.bottom;

// Crop it
CGImageRef newImage = CGImageCreateWithImageInRect([self CGImage], rect);

// Convert back to UIImage
img = [UIImage imageWithCGImage:newImage scale:self.scale orientation:self.imageOrientation];

CGImageRelease(newImage);
}

return img;
}

@end
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading