diff --git a/README.md b/README.md index ca67f6a..a65a968 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,11 @@ Category for trimming transparent pixels of an UIImage object. How to use ---------- -Add the `UIImage+Trim` files to your project. Include `UIImage+Trim.h` in the files where you want to trim your images. +Add `UIImage+Trim` to your project with Cocoapods: + +`pod 'UIImage-Trim', :git => 'https://github.com/gizmosachin/UIImage-Trim'` + +Include `UIImage+Trim.h` in the files where you want to trim your images. Trimming is pretty straightforward: diff --git a/UIImage+Trim.h b/Source/UIImage+Trim.h similarity index 87% rename from UIImage+Trim.h rename to Source/UIImage+Trim.h index 006c4a2..6d12100 100644 --- a/UIImage+Trim.h +++ b/Source/UIImage+Trim.h @@ -14,5 +14,6 @@ - (UIEdgeInsets)transparencyInsetsRequiringFullOpacity:(BOOL)fullyOpaque; - (UIImage *)imageByTrimmingTransparentPixels; - (UIImage *)imageByTrimmingTransparentPixelsRequiringFullOpacity:(BOOL)fullyOpaque; +- (UIImage *)imageByTrimmingWhitePixelsWithOpacity:(UInt8)tolerance; @end diff --git a/UIImage+Trim.m b/Source/UIImage+Trim.m old mode 100644 new mode 100755 similarity index 54% rename from UIImage+Trim.m rename to Source/UIImage+Trim.m index 7251246..a41e7b7 --- a/UIImage+Trim.m +++ b/Source/UIImage+Trim.m @@ -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); @@ -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 diff --git a/UIImage-Trim.podspec b/UIImage-Trim.podspec new file mode 100644 index 0000000..0bd73a1 --- /dev/null +++ b/UIImage-Trim.podspec @@ -0,0 +1,12 @@ +Pod::Spec.new do |s| + s.name = 'UIImage-Trim' + s.authors = { "Sachin Patel" => "me@gizmosachin.com" } + s.license = 'MIT' + s.summary = 'Category for trimming transparent pixels of an UIImage object.' + s.version = '1.0' + s.homepage = 'https://github.com/gizmosachin/UIImage-Trim' + s.source = { :git => "https://github.com/gizmosachin/UIImage-Trim.git", :tag => '1.0' } + s.frameworks = 'Foundation', 'UIKit' + s.platform = :ios, '7.0' + s.source_files = 'Source' +end