Search code examples
iosobjective-cuiimagedrawinguigraphicscontext

UIGraphicsBeginImageContext drawInRect inaccurate


I'm using this method to compress UIImage

if (actualHeight > maxHeight || actualWidth > maxWidth)
    {
        if(imgRatio < maxRatio)
        {
            //adjust width according to maxHeight
            imgRatio = maxHeight / actualHeight;
            actualWidth = imgRatio * actualWidth;
            actualHeight = maxHeight;
        }
        else if(imgRatio > maxRatio)
        {
            //adjust height according to maxWidth
            imgRatio = maxWidth / actualWidth;
            actualHeight = imgRatio * actualHeight;
            actualWidth = maxWidth;
        }
        else
        {
            actualHeight = maxHeight;
            actualWidth = maxWidth;
        }
    }
 CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
    UIGraphicsBeginImageContext(rect.size);
    [image drawInRect:rect];
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    NSData *imageData = UIImageJPEGRepresentation(img, compressionQuality);
    UIGraphicsEndImageContext();

But for some UIImage, at the bottom of the of them, there is a 1 pixel white line. Any suggestions on what could be the cause? Thank you so much!


Solution

  • The problem may be that you're working with CGFloats and you're multiplying/dividing them and this results in a non-integral coordinates.

    Line

    actualWidth = imgRatio * actualWidth;
    

    and

    actualHeight = imgRatio * actualHeight;
    

    may cause a non integral coordinate. Use ceilf or floorf or roundf to remedy this. E.g.

    actualWidth = ceilf(imgRatio * actualWidth);
    actualHeight = ceilf(imgRatio * actualHeight);
    

    What happens without it

    CGRect rect = CGRectMake(0.0, 0.0, actualWidth, actualHeight);
    

    may actually be (0,0,24.33, 24.33) You may actually be drawing a rect of this size, but image has to have a round number of pixels for height and width, so Apple probably rounds the rect up to create the image context.

    But then they probably draw it using antialiasing, so it really visually looks like non-integral pixels. And that's why you get the white line and possibly a loss of quality too.