views:

32

answers:

3

I'd LOVE to know what I'm doing wrong here. I'm a bit of a newbie with CGImageRefs so any advice would help.

I'm trying to create a bitmap image that has as it's pixel values a weighted sum of the pixels from another bitmap, and both bitmaps are 16bits per channel. For some reason I had no trouble getting this to work with 8bit images but it fails miserably with 16bit. My guess is that I'm just not setting things up correctly. I've tried using CGFloats, floats and UInt16s as the data types but nothing has worked. The input image has no alpha channel. The output image I get looks liked colored snow.

relevant stuff from the header:

UInt16 *inBaseAddress;
UInt16 *outBaseAddress;
CGFloat inAlpha[5];
CGFloat inRed[5];
CGFloat inGreen[5];
CGFloat inBlue[5];
CGFloat alphaSum, redSum, greenSum, blueSum;
int shifts[5];
CGFloat weight[5];
CGFloat weightSum;

I create the context for the input bitmap (a CGImageRef created with CGImageSourceCreateImageAtIndex(source, 0, NULL)) using:

size_t width = CGImageGetWidth(inBitmap);
size_t height = CGImageGetHeight(inBitmap);
size_t bitmapBitsPerComponent = CGImageGetBitsPerComponent(inBitmap);
size_t bitmapBytesPerRow = (pixelsWide * 4 * bitmapBitsPerComponent / 8);
CGColorSpaceRef colorSpace = CGImageGetColorSpace(inImage);

CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast;


CGContextRef inContext = CGBitmapContextCreate (NULL,width,height,bitmapBitsPerComponent,bitmapBytesPerRow,colorSpace,bitmapInfo);

The context for the output bitmap is created in the same way. I draw the inBitmap into the inContext using:

CGRect rect = {{0,0},{width,height}}; 
CGContextDrawImage(inContext, rect, inBitmap);

Then I initialize the inBaseAddress and outBaseAddress like so:

inBaseAddress = CGBitmapContextGetData(inContext);
outBaseAddress = CGBitmapContextGetData(outContext);

Then I fill the outBaseAddress with values from the inBaseAddress:

for (n = 0; n < 5; n++)
{
    inRed[n] = inBaseAddress[inSpot + 0 + shifts[n]];
    inGreen[n] = inBaseAddress[inSpot + 1 + shifts[n]];
    inBlue[n] = inBaseAddress[inSpot + 2 + shifts[n]];
    inAlpha[n] = inBaseAddress[inSpot + 3 + shifts[n]];

}

alphaSum = 0.0;
redSum = 0.0;
greenSum = 0.0;
blueSum = 0.0;

for (n = 0; n < 5; n++)
{
    redSum  += inRed[n] * weight[n];
    greenSum += inGreen[n] * weight[n];
    blueSum += inBlue[n] * weight[n];
    alphaSum += inAlpha[n] * weight[n];

}

outBaseAddress[outSpot + 0] = (UInt16)roundf(redSum);
outBaseAddress[outSpot + 1] = (UInt16)roundf(greenSum);
outBaseAddress[outSpot + 2] = (UInt16)roundf(blueSum);
outBaseAddress[outSpot + 3] = (UInt16)roundf(alphaSum);

As a simple check I've tried:

outBaseAddress[outSpot + 0] = inBaseAddress[inSpot + 0];
outBaseAddress[outSpot + 1] = inBaseAddress[inSpot + 1];
outBaseAddress[outSpot + 2] = inBaseAddress[inSpot + 2];
outBaseAddress[outSpot + 3] = inBaseAddress[inSpot + 3];

which works and at least means that the contexts and pointers to the bitmap data are working.

Thanks for any input. This has been pretty frustrating since it worked just fine with 8bit images.

A: 

It sounds like a byte ordering problem

fhj
Like big or little endian? I'm not too familiar with the byte ordering options with these CGBitmapContexts which is why I used "kCGBitmapByteOrderDefault" for the bitmap info.
Kevin Gross
A: 

Have you checked that CGImageGetBitsPerComponent is returning 16? As a matter of style, if you're assuming you're creating a bitmap context with 16 bits per pixel (since you treat the data as UInt16*), you should set explicitly set size_t bitmapBitsPerComponent = 16.

What is your shifts array for? It seems like the most likely place for error, since it's affecting the address you're reading from, but you don't explain it at all. Are the values in shifts multiples of 16?

Tony
CGImageGetBitsPerComponent returns 16. The shifts array is for selecting nearest neighbor pixels (the program is an anti-aliasing type of filter). shifts[0] = 0; shifts[1] = -width * 4; shifts[2] = width * 4; shifts[3] = -4; shifts[4] = 4;
Kevin Gross
A: 

OK, I've got it figured out. I needed to set the bitmapInfo to kCGBitmapByteOrder16Little for the 16bit images and to kCGBitmapByteOrder32Little for the 8bit images. I'm a bit surprised by this actually as would have expected it to be the other way around (32Little for 16 bit and 16Little for 8bit).

I also needed to type def the pointers to the bitmaps as UInt8* and UInt16*. It also appears that I have to include an alpha channel in the bitmapContext. I'm not sure why but the context returned was always nil without it.

Kevin Gross