I\'m trying to write a UIImage out as a tiff using libtiff. The problem is that even though I\'m writing it as 1 bit per pixel, the files are still coming out in the 2-5MB rang
I ended up going with GPUImage and libpng. If anyone wants to know how to write a png in iOS outside of the UIPNGRepresentation, here goes:
- (void) writeUIImage:(UIImage *)uiImage toPNG:(NSString *)file {
FILE *fp = fopen([file UTF8String], "wb");
if (!fp) return [self reportError:[NSString stringWithFormat:@"Unable to open file %@", file]];
CGImageRef image = [uiImage CGImage];
CGDataProviderRef provider = CGImageGetDataProvider(image);
CFDataRef pixelData = CGDataProviderCopyData(provider);
unsigned char *buffer = (unsigned char *)CFDataGetBytePtr(pixelData);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(image);
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(image);
size_t compBits = CGImageGetBitsPerComponent(image);
size_t pixelBits = CGImageGetBitsPerPixel(image);
size_t width = CGImageGetWidth(image);
size_t height = CGImageGetHeight(image);
NSLog(@"bitmapInfo=%d, alphaInfo=%d, pixelBits=%lu, compBits=%lu, width=%lu, height=%lu", bitmapInfo, alphaInfo, pixelBits, compBits, width, height);
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) [self reportError:@"Unable to create write struct."];
png_infop info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) {
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
return [self reportError:@"Unable to create info struct."];
}
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
return [self reportError:@"Got error callback."];
}
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr, info_ptr, (png_uint_32)width, (png_uint_32)height, 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_write_info(png_ptr, info_ptr);
png_set_packing(png_ptr);
png_bytep line = (png_bytep)png_malloc(png_ptr, width);
unsigned long pos;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
pos = y * width * 4 + x * 4; // multiplying by four because each pixel is represented by four bytes
line[x] = buffer[ pos ]; // just use the first byte (red) since r=g=b in grayscale
}
png_write_row(png_ptr, line);
}
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
if (pixelData) CFRelease(pixelData);
fclose(fp);
}
Why would you want to do this? UIPNGRepresentation is RGBA with 8 bits per component. That's 32 bits per pixel. Since I wanted a monochrome 1728x2304 image, I only need 1 bit per pixel and I end up with images as small as 40k. The same image with UIPNGRepresentation is 130k. Thankfully compression helps that 32 bit version a lot, but changing the bit depth to 1 really gets it down to very small file sizes.