Sean Clark Hess
Sean Clark Hess

Reputation: 16069

Convert image manipulation to 64-bit creates black lines

I inherited an image filter app, and I'm trying to update it. Apple required me to change the architecture to support 64-bit. On 64-bit phones, the images have vertical black bars (see below). 32 bit phones work as expected.

It seems like this is an issue with the old code assuming a 32-bit system, but how can I fix it?

Imgur

I've narrowed it down to the following code that applies an image curve:

NSUInteger*  currentPixel = _rawBytes;
NSUInteger*  lastPixel = (NSUInteger*)((unsigned char*)_rawBytes + _bufferSize);

while(currentPixel < lastPixel)
{
    SET_RED_COMPONENT_RGBA(currentPixel, _reds[RED_COMPONENT_RGBA(currentPixel)]);
    SET_GREEN_COMPONENT_RGBA(currentPixel, _greens[GREEN_COMPONENT_RGBA(currentPixel)]);
    SET_BLUE_COMPONENT_RGBA(currentPixel, _blues[BLUE_COMPONENT_RGBA(currentPixel)]);
    ++currentPixel;
}

Here are the macro definitions:

#define ALPHA_COMPONENT_RGBA(pixel)      (unsigned char)(*pixel >> 24)
#define BLUE_COMPONENT_RGBA(pixel)       (unsigned char)(*pixel >> 16)
#define GREEN_COMPONENT_RGBA(pixel)      (unsigned char)(*pixel >> 8)
#define RED_COMPONENT_RGBA(pixel)        (unsigned char)(*pixel >> 0)

#define SET_ALPHA_COMPONENT_RGBA(pixel, value)      *pixel = (*pixel & 0x00FFFFFF) | ((unsigned long)value << 24)
#define SET_BLUE_COMPONENT_RGBA(pixel, value)       *pixel = (*pixel & 0xFF00FFFF) | ((unsigned long)value << 16)
#define SET_GREEN_COMPONENT_RGBA(pixel, value)      *pixel = (*pixel & 0xFFFF00FF) | ((unsigned long)value << 8)
#define SET_RED_COMPONENT_RGBA(pixel, value)        *pixel = (*pixel & 0xFFFFFF00) | ((unsigned long)value << 0)

#define BLUE_COMPONENT_ARGB(pixel)      (unsigned char)(*pixel >> 24)
#define GREEN_COMPONENT_ARGB(pixel)      (unsigned char)(*pixel >> 16)
#define RED_COMPONENT_ARGB(pixel)       (unsigned char)(*pixel >> 8)
#define ALPHA_COMPONENT_ARGB(pixel)      (unsigned char)(*pixel >> 0)

#define SET_BLUE_COMPONENT_ARGB(pixel, value)       *pixel = (*pixel & 0x00FFFFFF) | ((unsigned long)value << 24)
#define SET_GREEN_COMPONENT_ARGB(pixel, value)      *pixel = (*pixel & 0xFF00FFFF) | ((unsigned long)value << 16)
#define SET_RED_COMPONENT_ARGB(pixel, value)        *pixel = (*pixel & 0xFFFF00FF) | ((unsigned long)value << 8)
#define SET_ALPHA_COMPONENT_ARGB(pixel, value)      *pixel = (*pixel & 0xFFFFFF00) | ((unsigned long)value << 0)

How should I change the above to work on either a 32 or 64 bit device? Do I need to include more code?

Upvotes: 1

Views: 48

Answers (1)

jscs
jscs

Reputation: 64002

NSUInteger changes size between 32- and 64-bit devices. It used to be 4 bytes; now it's 8. The code assumes it's working with RGBA data, with a byte for each channel, so the increment of an 8-byte pointer is skipping over half the data.

Just be explicit about the size:

uint32_t * currentPixel = _rawBytes;
uint32_t * lastPixel = (uint32_t *)((unsigned char *)_rawBytes + _bufferSize);

and the calculation should work correctly on both types of device.

Upvotes: 2

Related Questions