Archive for May 2013

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.