Search code examples
cocoansimagecgimagecgimagerefnsbitmapimagerep

When creating a CGImage with a decode array, the output has all of it's pixels offset by a small amount


Very weird behavior, but I have narrowed the problem down as far as I can go I think

I have a NSImage, let's call it inputImage. It is represented by a NSBitmapImageRep in a CGColorSpaceCreateDeviceGray, if that matters

I want to create a CGImageRef from it, but with inverted colors.

  NSData *data = [inputImage TIFFRepresentation];
  CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);

  NSBitmapImageRep *maskedRep = (NSBitmapImageRep*)[inputImage representations][0];
  CGFloat decode[] = {0.0, 1.0};

  maskRef = CGImageMaskCreate(maskedRep.pixelsWide,
                              maskedRep.pixelsHigh,
                              8,
                              maskedRep.bitsPerPixel,
                              maskedRep.bytesPerRow,
                              provider,
                              decode,
                              false);

  NSImage *testImage = [[NSImage alloc] initWithCGImage:maskRef size:NSMakeSize(1280,1185)]; //the size of the image, hard-coded for testing
  NSLog(@"testimage: %@", testImage);

The problem is when I look at testImage, all of the pixels are slightly offset to the right from the original image.

inputImage:
enter image description here

testImage:
enter image description here

It's much easier to see if you save the pictures off, but you'll notice that everything in testImage is offset to the right by about 5 pixels or so. You'll see a white gap to the left of the black content in testImage

Somewhere in my 5 lines of code I am somehow moving my image over. Does anybody have any idea how this could be happening? I'm currently suspecting TIFFRepresentation


Solution

  • The data provider you pass to CGImageMaskCreate() is supposed to be raw pixels in the form specified by the other parameters, not an image file format. You shouldn't be passing TIFF data. Frankly, I'm surprised you got anything remotely resembling your original image, rather than noise/garbage. I'm guessing the TIFF data wasn't compressed, by sheer chance. You can actually see a bit of noise, which is the TIFF header interpreted as pixels, at the upper-left of your mask.

    Probably, your best bet is to create a CGImage from your inputImage (or, depending on how inputImage was created, skip NSImage and create the CGImage directly from a file using the CGImageSource API or CGImageCreateWith{JPEG,PNG}DataProvider()). To get a CGImage from an NSImage, use -CGImageForProposedRect:context:hints:.

    Then, get the data provider from that CGImage and create the mask CGImage from that, using the various properties (width, height, bpp, etc.) queried from the first CGImage using the various CGImageGet... functions.