UIImage categories (part 4 of 4)

And the final step of the process is to take the scaled image and center it in a rectangle. Here is the code for that:

- (UIImage *)imageCenteredInSize:(CGSize)newSize
{
    CGSize imageSize = self.size;
    CGFloat x = (newSize.width - imageSize.width) / 2.0;
    CGFloat y = (newSize.height - imageSize.height) / 2.0;
    CGFloat w = imageSize.width;
    CGFloat h = imageSize.height;
    UIGraphicsBeginImageContext(newSize);
    [self drawInRect:CGRectMake(x, y, w, h)];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}

I believe that I found this code on Stack Overflow.

BTW, Happy Doughnut Day to everyone.

UIImage categories (part 3 of 4)

Once I have the smartly cropped image, I then need to scale it to fit inside a particular rectangle. Here is the code to do that, courtesy of the NYXImagesKit by Nyx0uf:

- (UIImage*)scaleToFitSize:(CGSize)newSize
{
	/// Keep aspect ratio
	size_t destWidth, destHeight;
	if (self.size.width > self.size.height)
	{
		destWidth = (size_t)newSize.width;
		destHeight = (size_t)(self.size.height * newSize.width / self.size.width);
	}
	else
	{
		destHeight = (size_t)newSize.height;
		destWidth = (size_t)(self.size.width * newSize.height / self.size.height);
	}
	if (destWidth > newSize.width)
	{
		destWidth = (size_t)newSize.width;
		destHeight = (size_t)(self.size.height * newSize.width / self.size.width);
	}
	if (destHeight > newSize.height)
	{
		destHeight = (size_t)newSize.height;
		destWidth = (size_t)(self.size.width * newSize.height / self.size.height);
	}
	return [self scaleToFillSize:CGSizeMake(destWidth, destHeight)];
}

BTW, I hope everyone is having a good Memorial Day weekend. Please pause and remember well those that made the ultimate sacrifice for us.

UIImage categories (part 2 of 4)

Here is the second category, where I take the UIImage with the clear background and crop it down. The algorithm is a bit brute force, but it seems to work fine.

- (UIImage *)imageByTrimmingTransparentPixels
{
	int rows = self.size.height;
	int cols = self.size.width;
	int bytesPerRow = cols*sizeof(uint8_t);
 
	if ( rows < 2 || cols < 2 ) {
		return self;
	}
 
	//allocate array to hold alpha channel
	uint8_t *bitmapData = calloc(rows*cols, sizeof(uint8_t));
 
	//create alpha-only bitmap context
	CGContextRef contextRef = CGBitmapContextCreate(bitmapData, cols, rows, 8, bytesPerRow, NULL, kCGImageAlphaOnly);
 
	//draw our image on that context
	CGImageRef cgImage = self.CGImage;
	CGRect rect = CGRectMake(0, 0, cols, rows);
	CGContextDrawImage(contextRef, rect, cgImage);
 
	//summ all non-transparent pixels in every row and every column
	uint16_t *rowSum = calloc(rows, sizeof(uint16_t));
	uint16_t *colSum = calloc(cols, sizeof(uint16_t));
 
	//enumerate through all pixels
	for ( int row = 0; row < rows; row++) {
		for ( int col = 0; col < cols; col++)
		{
			if ( bitmapData[row*bytesPerRow + col] ) { //found non-transparent pixel
				rowSum[row]++;
				colSum[col]++;
			}
		}
	}
 
	//initialize crop insets and enumerate cols/rows arrays until we find non-empty columns or row
	UIEdgeInsets crop = UIEdgeInsetsMake(0, 0, 0, 0);
 
	for ( int i = 0; i<rows; i++ ) { 		//top
		if ( rowSum[i] > 0 ) {
			crop.top = i; break;
		}
	}
 
	for ( int i = rows; i >= 0; i-- ) {		//bottom
		if ( rowSum[i] > 0 ) {
			crop.bottom = MAX(0, rows-i-1); break;
		}
	}
 
	for ( int i = 0; i<cols; i++ ) {		//left
		if ( colSum[i] > 0 ) {
			crop.left = i; break;
		}
	}
 
	for ( int i = cols; i >= 0; i-- ) {		//right
		if ( colSum[i] > 0 ) {
			crop.right = MAX(0, cols-i-1); break;
		}
	}
 
	free(bitmapData);
	free(colSum);
	free(rowSum);
 
	if ( crop.top == 0 && crop.bottom == 0 && crop.left == 0 && crop.right == 0 ) {
		//no cropping needed
		return self;
	}
	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(cgImage, rect);
 
		//convert back to UIImage
		return [UIImage imageWithCGImage:newImage];
	}
}

Again, I would attribute this code if I could, but one of my former co-workers found this for me on some pastebin. If anyone knows where this code originally came from, please let me know and I will make the necessary attribution.

BTW, Happy Birthday today to Mr. T. (Yes, May 21 is slim pickings for birthdays, deaths, events, and holidays.)

UIImage categories (part 1 of 4)

My current iOS app gets images that I need to process. Basically, in my app, I get a UIImage with a white background and a CGRect, and I need to make the background of the image clear, automatically crop the image, scale image into the given CGRect preserving the aspect ratio, and then pad the scaled CGRect so that it appears centered inside of the original CGRect.

So in order to do this, I went through and found some UIImage categories to perform these tasks. Here is the first category that makes the white background into a clear background:

- (UIImage *)makeWhiteBackgroundTransparent
{
    return [UIImage replaceColor:[UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0] inImage:self withTolerance:0.0];
}
 
+ (UIImage *)replaceColor:(UIColor *)color inImage:(UIImage *)image withTolerance:(float)tolerance
{
    CGImageRef imageRef = [image CGImage];
 
    NSUInteger width = CGImageGetWidth(imageRef);
    NSUInteger height = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
 
    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * width;
    NSUInteger bitsPerComponent = 8;
    NSUInteger bitmapByteCount = bytesPerRow * height;
 
    unsigned char *rawData = (unsigned char*) calloc(bitmapByteCount, sizeof(unsigned char));
 
    CGContextRef context = CGBitmapContextCreate(rawData, width, height,
                                                 bitsPerComponent, bytesPerRow, colorSpace,
                                                 kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);
    CGColorSpaceRelease(colorSpace);
 
    CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
 
    CGColorRef cgColor = [color CGColor];
    const CGFloat *components = CGColorGetComponents(cgColor);
    float r = components[0];
    float g = components[1];
    float b = components[2];
    //float a = components[3]; // not needed
 
    r = r * 255.0;
    g = g * 255.0;
    b = b * 255.0;
 
    const float redRange[2] = {
        MAX(r - (tolerance / 2.0), 0.0),
        MIN(r + (tolerance / 2.0), 255.0)
    };
 
    const float greenRange[2] = {
        MAX(g - (tolerance / 2.0), 0.0),
        MIN(g + (tolerance / 2.0), 255.0)
    };
 
    const float blueRange[2] = {
        MAX(b - (tolerance / 2.0), 0.0),
        MIN(b + (tolerance / 2.0), 255.0)
    };
 
    int byteIndex = 0;
 
    while (byteIndex < bitmapByteCount)
    {
        unsigned char red   = rawData[byteIndex];
        unsigned char green = rawData[byteIndex + 1];
        unsigned char blue  = rawData[byteIndex + 2];
 
        if (((red >= redRange[0]) && (red <= redRange[1])) &&
            ((green >= greenRange[0]) && (green <= greenRange[1])) &&
            ((blue >= blueRange[0]) && (blue <= blueRange[1])))
        {
            // make the pixel transparent
            //
            rawData[byteIndex] = 0;
            rawData[byteIndex + 1] = 0;
            rawData[byteIndex + 2] = 0;
            rawData[byteIndex + 3] = 0;
        }
 
        byteIndex += 4;
    }
 
    CGImageRef imgRef = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:imgRef];
    CGImageRelease(imgRef);
 
    CGContextRelease(context);
    free(rawData);
 
    return result;
}

I would attribute this code, but I can’t find where I found this particular replaceColor method. If anyone knows where this code originally came from, please let me know and I will make the necessary attribution.

BTW, Happy Anniversary to McDonalds, who opened their first restaurant in San Bernardino, California on this date back in 1940.

Dell Windows XP reinstall cannot find new SATA hard drive

I recently have been trying to resurrect a Dell desktop computer with a bad hard drive, but after putting a new SATA hard drive in it, the Windows XP installer has not been able to find the hard drive. As it turns out, I had to go into the BIOS settings on the computer, and change the hard drive setting from “Autodetect RAID / ACHI” to “Autodetect RAID / ATA”, after which I was able to make the installer happy. Huzzah!

BTW, Happy Birthday to Joe Bonamassa, one of my favorite all-time artists. I can’t wait to see Joe later this year when he comes back and plays Ohio. (Hey Joe, Columbus is a better crowd than Cleveland. Just sayin’.)

And on a somber note, I learned this afternoon of the passing of Jeanne Cooper, the long time Mrs. Chancellor character on The Young and The Restless. I hope they do not try to put another actress in that character, Michael Learned did an OK job but it just won’t be the same without the original Mrs. C.

What UIViewController class am I looking at right now?

When you are looking at unfamiliar iOS Objective-C code (either someone else’s or your own), it can be tricky to figure out where code can be hiding out within a project.

Well no more. I found this interesting bit of method swizzling from Michael Armstrong that will show you which UIViewController subclass in on the screen at any time.

MADebugTools

I went into this code and added a #define that I am setting in my .pch file, so that I can turn off the view controller labels if I do not need to see them.

As an iOS aside, if you are an iOS developer, I would recommend following Romain Briche either on Twitter or on his web site (broken link removed). I have found quite a few useful controls, code samples, and other hints and tutorials as a result of Romain’s postings.

BTW, Happy Birthday to Melody Thomas Scott, who I thought was fantastic in The Car, one of my all time favorite movies. (Oh, and she is on The Young and The Restless, too.)

Logging an iOS class instance

I found this interesting category of NSObject from Simon Strandgaard that walks through your instance’s class variables and builds up a description string, which you can then output to the console. Here is the link to the category on Github:

https://github.com/neoneye/autodescribe

BTW, Happy Birthday to Lois Chiles, the who played Dr. Goodhead in the James Bond movie Moonraker.

Xcode 4.6 Organizer crash on update

Well I had a nice little issue with Xcode. Whenever I would try to do something in Organizer that had to sign into my Apple developer account (refresh provisioning profiles, add a device to a provisioning profile, etc.), it would ask for my credentials, chug for a moment, and then slip out the back door like it was late for a date with a supermodel.

Luckily, today I got frustrated with this issue and did some digging. I found a post on Apple’s forums about getting rid of some Library files created and used by Xcode, and after I removed the two files in question, lo and behold it started to be non-crashy again. Bonus.

The files were in the folder “~/Library/Developer/Xcode/” and begin with “connect1.apple.com”. Apple’s post says to move the files, but I just blew them away and it seemed to work fine. Here is the post on the Apple web site: (you may need to log in with your Apple developer account to see this posting)

Xcode 4.6.1 crashing while interacting with the Developer Portal

BTW, Happy Birthday to Routzy! We had our one year birthday party for Routzy tonight at Vito’s Wine Bar in Delaware, Ohio. Thanks to all who turned out, and wait until you see what we have in store for Routzy in year 2.

Post script: Happy Belated 40th Birthday to The Young and The Restless.

iOS NSRegularExpression to detect UUID

In keeping with my attempt to use more regular expressions in .NET, I figured it would be a good exercise to try and use NSRegularExpression to do some checking in my iOS app.

I have a situation where an array of strings come in, and I need to know which of them are UUIDs and which are not. Here is the code that I wrote to accomplish the UUID checking, it is implemented as a category of NSString:

#define UUID_PATTERN    @"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
 
- (BOOL)isUUID
{
    NSRegularExpression *regex;
    regex = [NSRegularExpression regularExpressionWithPattern:UUID_PATTERN
                                                      options:NSRegularExpressionCaseInsensitive
                                                        error:nil];
    int matches = [regex numberOfMatchesInString:self options:0
                                           range:NSMakeRange(0, [self length])];
    return (matches == 1);
}

To use this NSString category, you would just do something like this:

NSLog(@"Is %@ a UUID? %@", theTestString, ([theTestString isUUID] ? @"YES" : @"NO"));

BTW, as far as I am concerned, Google laid a couple of eggs the last two days. Firstly, yesterday (Wednesday, March 13) I tried for almost an hour to get Google I/O tickets, to no avail. I was hoping to attend as my trip to I/O last year was interrupted by my father passing away. Then, this morning, news breaks that Google is killing off Google Reader. My opinion of Google has gone down a couple of notches.

Double Bonus BTW: Happy Pi Day everyone! See you at Stir Trek!!! (Yes, I got tickets for that one. Whew…)

Mamas, don’t let your babies grow up to put important code in the dealloc method

Here is something you may not have noticed (yet). If you use the ZipArchive code to create zip files in your iOS app, and you convert your app code to ARC, your zip files might not be created correctly. The problem exists here in the ZipArchive.mm file:

-(void) dealloc
{
	[self CloseZipFile2];
	[super dealloc];
}

If you use the CreateZipFile2 method to create your zip file, previously you would call the release method on your ZipArchive object, and the memory would be flushed away. Unbeknownst to you (but beknownst to the ZipArchive devs), your zip file was A-OK because of the CloseZipFile2 method call in the dealloc method.

But ARC conversion removes the release messages! Bugger!!! Just manually put in a call to CloseZipFile2 everywhere that you use the CreateZipFile2 method.

BTW, Happy Birthday to Kent Tekulve, former Pirates great and commissioner of the Pittsburgh Pirates Fantasy Camp. Unfortunately Teke could not make it to camp due to illness this year, we missed you Teke and hope you are feeling better.