0

So I've got a method that successfully gets the color of a pixel.

//arrColT is an NSMutableArray<NSColor *>
        NSInteger width = [bitIn pixelsWide];
        NSInteger height = [bitIn pixelsHigh];
        NSInteger rowBytes = [bitIn bytesPerRow];
        unsigned char* pixels = [bitIn bitmapData];
        int row, col;
        //For every row,
        for (row = 0; row < height; row++)
        {
            unsigned char* rowStart = (unsigned char*)(pixels + (row * rowBytes));
            unsigned char* nextChannel = rowStart;
            //For every pixel in a row,
            for (col = 0; col < width; col++)
            {
                //Get its color.
                unsigned char red, green, blue, alpha;
                red = *nextChannel;
                int intRed = (int)red;
                nextChannel++;
                green = *nextChannel;
                int intGreen = (int)green;
                nextChannel++;
                blue = *nextChannel;
                int intBlue = (int)blue;
                nextChannel++;
                alpha = *nextChannel;
                int intAlpha = (int)alpha;
                nextChannel++;
                NSColor *colHas = [NSColor colorWithCalibratedRed:(float)intRed/255 green: (float)intGreen/255 blue: (float)intBlue/255 alpha:(float)intAlpha/255];
                for (int i = 0; i<[arrColT count]; i++) {
                    //If the target color is equal to the current color, replace it with the parallel replace color...somehow.
                    if([colHas isEqualTo:arrColT[i]]){
                    }
                }
            }
        }

The question is, how do I get color data back into the bitmapData?

With hope,
radzo73

2 Answers2

0

See Technical Q&A QA1509, which basically advises

  1. create pixel buffer (by creating CGContextRef of predefined format and drawing your image to that);
  2. manipulate the bytes within that pixel buffer as you want; and
  3. create resulting image with CGBitmapContextCreateImage.

E.g.

- (UIImage *)convertImage:(UIImage *)image
{
    // get image

    CGImageRef imageRef = image.CGImage;

    // prepare context

    CGBitmapInfo    bitmapInfo       = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big; // bytes in RGBA order
    NSInteger       width            = CGImageGetWidth(imageRef);
    NSInteger       height           = CGImageGetHeight(imageRef);
    CGColorSpaceRef colorspace       = CGColorSpaceCreateDeviceRGB();

    CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 4 * width, colorspace, bitmapInfo);

    // draw image to that context

    CGRect rect = CGRectMake(0, 0, width, height);
    CGContextDrawImage(context, rect, imageRef);
    uint8_t *buffer = CGBitmapContextGetData(context);

    // ... manipulate pixel buffer bytes here ...

    // get image from buffer

    CGImageRef outputImage = CGBitmapContextCreateImage(context);
    UIImage *result = [UIImage imageWithCGImage:outputImage scale:image.scale orientation:image.imageOrientation];

    // clean up

    CGImageRelease(outputImage);
    CGContextRelease(context);
    CGColorSpaceRelease(colorspace);

    return result;
}

For example, here is a B&W conversion routine:

/** Convert the image to B&W as a single (non-parallel) task.
 *
 * This assumes the pixel buffer is in RGBA, 8 bits per pixel format.
 *
 * @param buffer The  pixel buffer.
 * @param width        The image width in pixels.
 * @param height       The image height in pixels.
 */
- (void)convertToBWSimpleInBuffer:(uint8_t *)buffer width:(NSInteger)width height:(NSInteger)height
{
    for (NSInteger row = 0; row < height; row++) {
        for (NSInteger col = 0; col < width; col++) {
            NSUInteger offset = (col + row * width) * 4;
            uint8_t *pixel = buffer + offset;

            // get the channels

            uint8_t red   = pixel[0];
            uint8_t green = pixel[1];
            uint8_t blue  = pixel[2];
            uint8_t alpha = pixel[3];

            // update the channels with result

            uint8_t gray = 0.2126 * red + 0.7152 * green + 0.0722 * blue;

            pixel[0] = gray;
            pixel[1] = gray;
            pixel[2] = gray;
            pixel[3] = alpha;
        }
    }
}

If you want to improve performance, you can use dispatch_apply and stride through your image buffer in parallel:

/** Convert the image to B&W, using GCD to split the conversion into several concurrent GCD tasks.
 *
 * This assumes the pixel buffer is in RGBA, 8 bits per pixel format.
 *
 * @param buffer The  pixel buffer.
 * @param width        The image width in pixels.
 * @param height       The image height in pixels.
 * @param count        How many GCD tasks should the conversion be split into.
 */
- (void)convertToBWConcurrentInBuffer:(uint8_t *)buffer width:(NSInteger)width height:(NSInteger)height count:(NSInteger)count
{
    dispatch_queue_t queue = dispatch_queue_create("com.domain.app", DISPATCH_QUEUE_CONCURRENT);
    NSInteger stride = height / count;

    dispatch_apply(height / stride, queue, ^(size_t idx) {
        size_t j = idx * stride;
        size_t j_stop = MIN(j + stride, height);

        for (NSInteger row = j; row < j_stop; row++) {
            for (NSInteger col = 0; col < width; col++) {
                NSUInteger offset = (col + row * width) * 4;
                uint8_t *pixel = buffer + offset;

                // get the channels

                uint8_t red   = pixel[0];
                uint8_t green = pixel[1];
                uint8_t blue  = pixel[2];
                uint8_t alpha = pixel[3];

                // update the channels with result

                uint8_t gray = 0.2126 * red + 0.7152 * green + 0.0722 * blue;

                pixel[0] = gray;
                pixel[1] = gray;
                pixel[2] = gray;
                pixel[3] = alpha;
            }
        }
    });
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

Well it depends on what bitIn is, but something like this should do the trick.

First, create a context. You can do this once and keep the context if you are going to make a lot of changes to the data and need a lot of pictures.

self.ctx = CGBitmapContextCreate ( 
    [bitIn bitmapData],
    [bitIn pixelsWide],
    [bitIn pixelsHigh],
    [bitIn bitsPerPixel],
    [bitIn bytesPerRow],
    [bitIn colorSpace],
    [bitIn bitmapInfo] );

Here I freely used bitIn hoping it will provide all the missing pieces. Then you can assemble it all later, even after further changes to the data, using something like

CGImageRef cgImg = CGBitmapContextCreateImage ( self.ctx );
UIImage * img = [UIImage imageWithCGImage:cgImg];
CGImageRelease ( cgImg );

Now you can make more changes to the data and do the same to get a new image.

skaak
  • 2,988
  • 1
  • 8
  • 16