How do I write a 1bpp tiff with libtiff on iOS?

前端 未结 2 583
傲寒
傲寒 2021-02-11 02:25

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

2条回答
  •  执笔经年
    2021-02-11 03:14

    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.

提交回复
热议问题