Search code examples
imagepngcore-graphicsimage-compression

PNG Compression - ImageIO (on Mac OS using C/C++)


I am trying to compress PNG image by applying PNG Filters like(Sub, Up, Average, Paeth), I am applying filers using property kCGImagePropertyPNGCompressionFilter but there is no change seen in resultant images after trying any of the filter. What is the issue here can someone help me with this. Do I have compress image data after applying filter? If yes how to do that?

Here is my source code

    CGImageDestinationRef   outImageDestRef = NULL;
        long                    keyCounter = kzero;
        CFStringRef             dstImageFormatStrRef = NULL;
        CFMutableDataRef        destDataRef = CFDataCreateMutable(kCFAllocatorDefault,0);
        Handle srcHndl = //source image handle;
        ImageTypes srcImageType = //'JPEG', 'PNGf, etct;
        CGImageRef inImageRef = CreateCGImageFromHandle(srcHndl,srcImageType);
        
        if(inImageRef)
        {
            CFTypeRef keys[4] = {nil};
            CFTypeRef values[4] = {nil};

            dstImageFormatStrRef = CFSTR("public.png");
            long png_filter = IMAGEIO_PNG_FILTER_SUB; //IMAGEIO_PNG_FILTER_SUB, IMAGEIO_PNG_FILTER_UP, IMAGEIO_PNG_FILTER_AVG, IMAGEIO_PNG_FILTER_PAETH .. it is one of this at a time
            keys[keyCounter] = kCGImagePropertyPNGCompressionFilter;
            values[keyCounter] = CFNumberCreate(NULL,kCFNumberLongType,&png_filter);
            keyCounter++;

            outImageDestRef = CGImageDestinationCreateWithData(destDataRef, dstImageFormatStrRef, 1, NULL);
            
            if(outImageDestRef)
            {
//                keys[keyCounter] = kCGImagePropertyDPIWidth;
//                values[keyCounter] = CFNumberCreate(NULL,kCFNumberLongType,&Resolution);
//                keyCounter++;
//
//                keys[keyCounter] = kCGImagePropertyDPIHeight;
//                values[keyCounter] = CFNumberCreate(NULL,kCFNumberLongType,&Resolution);
//                keyCounter++;
                
                CFDictionaryRef options = CFDictionaryCreate(NULL,keys,values,keyCounter,&kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks);
                CGImageDestinationAddImage(outImageDestRef,inImageRef, options);
                CFRelease(options);
                status = CGImageDestinationFinalize(outImageDestRef);
                if(status == true)
                {
                    UInt8 *destImagePtr = CFDataGetMutableBytePtr(destDataRef);
                    destSize = CFDataGetLength(destDataRef);
                    //using destImagePtr after this ...
                }

                CFRelease(outImageDestRef);
            }
            
            for(long cnt = kzero; cnt < keyCounter; cnt++)
                if(values[cnt])
                    CFRelease(values[cnt]);

            if(inImageRef)
                CGImageRelease(inImageRef);
        }

Solution

  • I asked help from apple developer team and got solution for it from them. I am sharing sample codes shared by them which is working as expected.

    { // Insert code here to initialize your application

        CFStringRef dstImageFormatStrRef = NULL;
    
        /* load the source image */
        NSDictionary * myOptions = @{
            (id)kCGImageSourceShouldAllowFloat : @YES,
            (id)kCGImageSourceShouldCache : @YES,
        };
        NSURL *input_file = [[NSBundle mainBundle] URLForResource:@"dogcow2021b" withExtension:@"png"];
        CGImageSourceRef myImageSource = CGImageSourceCreateWithURL((CFURLRef)input_file, (CFDictionaryRef)myOptions);
    
        CGImageRef inImageRef = CGImageSourceCreateImageAtIndex(myImageSource, 0, NULL);
    
        if(inImageRef)
        {
            dstImageFormatStrRef = CFSTR("public.png");
            long png_filters[] = {IMAGEIO_PNG_FILTER_SUB, IMAGEIO_PNG_FILTER_UP, IMAGEIO_PNG_FILTER_AVG, IMAGEIO_PNG_FILTER_PAETH};
            NSMutableData * result_file_data[5];
            
            NSMutableDictionary * options = [NSMutableDictionary dictionary];
            NSMutableDictionary * png_options = [NSMutableDictionary dictionary];
            options[(id)kCGImagePropertyPNGDictionary] = png_options;
    
            for (int i = 0; i < sizeof(png_filters)/sizeof(long); i++ ) {
                
                result_file_data[i] = [NSMutableData data];
    
                long png_filter = 0;
                //for (int loop = 0; loop <= i; loop++)
                    png_filter = png_filters[i];
                png_options[(id)kCGImagePropertyPNGCompressionFilter] = @(png_filter);
    
                NSLog(@"#%3d trying png filter %ld", i, png_filter);
                
                CGImageDestinationRef outImageDestRef = CGImageDestinationCreateWithData((CFMutableDataRef)result_file_data[i], dstImageFormatStrRef, 1, NULL);
                            
                CGImageDestinationAddImage(outImageDestRef,inImageRef, (CFDictionaryRef)options);
    
                int status = CGImageDestinationFinalize(outImageDestRef);
                if(status == true)
                {
                    CFIndex aSize = result_file_data[i].length;
                    NSLog(@"     created an image of size '%ld'", aSize);
                    if (i > 0) {
                        CFIndex bSize = result_file_data[i-1].length;
                        if ( aSize == bSize ) {
                            // if the size is the same, do a byte by byte comparison.
                            const uint8_t *B = [result_file_data[i-1] bytes];
                            const uint8_t *A = [result_file_data[i] bytes];
                            CFIndex sameBytes = 0;
                            CFIndex differentBytes = 0;
                            
                            for (CFIndex k = 0; k < aSize; k++ ) { // memcmp but counting different bytes
                                if ( A[k] == B[k] ) {
                                    sameBytes++;
                                } else {
                                    differentBytes++;
                                }
                            }
                            NSLog(@"(%ld bytes identical to previous, %ld bytes different than previous)", sameBytes, differentBytes);
                        } else {
                            NSLog(@"(images have different sizes '%ld' vs '%ld')", aSize, bSize);
                        }
                    }
                }
                
                CFRelease(outImageDestRef);
                
            }
            
            CGImageRelease(inImageRef);
        }
    }