I need to set random picture (type noise pixel) necessarily with using OpenGL ES. Without openGL (in ios) we have did it like this:
//declare variable:
UIImage* myImage;
UIImageView *imageView;
CGRect rect;
//function whitch drawing random pixel-array picture and display it on screen ios-device;
-(void)setPicture
{
if(imageView!=NULL)
{
//Addition removal imageView
[imageView removeFromSuperview];
imageView = NULL; //
}
int width = 352; //sizes
int height = 288;
size_t bufferLength = width * height * 4; //
uint8_t* pixels = (uint8_t*)malloc(bufferLength); //
for (int i = 0; i < bufferLength/2; i++) {
pixels[i] = rand() % 255; //obtain random pixel array
}
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(pixels, width, height, 8, width * 4, space, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast); //
CGImageRef toCGImage = CGBitmapContextCreateImage(ctx);
UIImage * uiimage = [[UIImage alloc] initWithCGImage:toCGImage]; //reflect pixel array to picture
CGImageRelease(toCGImage);
CGColorSpaceRelease(space);
CGContextRelease(ctx);
free(pixels);
myImage = uiimage;
//
rect = self.view.bounds; //
if(imageView == NULL)
{
imageView = [[UIImageView alloc] initWithFrame:rect];
imageView.image = myImage;
[self.view addSubview:imageView];
NSLog(#"NULL");
} else
{
[imageView setNeedsDisplay];
NSLog(#"Dewnull");
}
}
Same picture displays on screen after calling of my function.
Questions
Is it possible to set similar picture on ios screen with openGL ES? How to make it? Will it work faster then my function?
See the section 'Procedural Textures: “Random” Noise'. Is it faster? to generate, almost certainly yes, because the GPU can easily parallelize this process. Reading back from the GPU can be slow though; I imagine maybe less so on iOS because the memory is unified. Profile it and see. It depends what you are doing with the texture.
Related
I want to change the color of an opaque UIImage. My original image is as follows:
and I want to convert the image to the following format
So, basically I want to convert red color in the image into black (Any other color) color. Above two images are added for better understanding.
I couldn't see any answers on the 'duplicates' (this question shouldn't have been flagged as a duplicate) that will let you replace a given color with another color and work on an opaque image, so I decided to add one that would.
I created a UIImage category to do this, it basically works by looping through each pixel and detecting how close it is to a given colour, and blends it with your replacement colour if it is.
This will work for images with both transparency and opaque backgrounds.
#implementation UIImage (UIImageColorReplacement)
-(UIImage*) imageByReplacingColor:(UIColor*)sourceColor withMinTolerance:(CGFloat)minTolerance withMaxTolerance:(CGFloat)maxTolerance withColor:(UIColor*)destinationColor {
// components of the source color
const CGFloat* sourceComponents = CGColorGetComponents(sourceColor.CGColor);
UInt8* source255Components = malloc(sizeof(UInt8)*4);
for (int i = 0; i < 4; i++) source255Components[i] = (UInt8)round(sourceComponents[i]*255.0);
// components of the destination color
const CGFloat* destinationComponents = CGColorGetComponents(destinationColor.CGColor);
UInt8* destination255Components = malloc(sizeof(UInt8)*4);
for (int i = 0; i < 4; i++) destination255Components[i] = (UInt8)round(destinationComponents[i]*255.0);
// raw image reference
CGImageRef rawImage = self.CGImage;
// image attributes
size_t width = CGImageGetWidth(rawImage);
size_t height = CGImageGetHeight(rawImage);
CGRect rect = {CGPointZero, {width, height}};
// bitmap format
size_t bitsPerComponent = 8;
size_t bytesPerRow = width*4;
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
// data pointer
UInt8* data = calloc(bytesPerRow, height);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// create bitmap context
CGContextRef ctx = CGBitmapContextCreate(data, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
CGContextDrawImage(ctx, rect, rawImage);
// loop through each pixel's components
for (int byte = 0; byte < bytesPerRow*height; byte += 4) {
UInt8 r = data[byte];
UInt8 g = data[byte+1];
UInt8 b = data[byte+2];
// delta components
UInt8 dr = abs(r-source255Components[0]);
UInt8 dg = abs(g-source255Components[1]);
UInt8 db = abs(b-source255Components[2]);
// ratio of 'how far away' each component is from the source color
CGFloat ratio = (dr+dg+db)/(255.0*3.0);
if (ratio > maxTolerance) ratio = 1; // if ratio is too far away, set it to max.
if (ratio < minTolerance) ratio = 0; // if ratio isn't far enough away, set it to min.
// blend color components
data[byte] = (UInt8)round(ratio*r)+(UInt8)round((1.0-ratio)*destination255Components[0]);
data[byte+1] = (UInt8)round(ratio*g)+(UInt8)round((1.0-ratio)*destination255Components[1]);
data[byte+2] = (UInt8)round(ratio*b)+(UInt8)round((1.0-ratio)*destination255Components[2]);
}
// get image from context
CGImageRef img = CGBitmapContextCreateImage(ctx);
// clean up
CGContextRelease(ctx);
CGColorSpaceRelease(colorSpace);
free(data);
free(source255Components);
free(destination255Components);
UIImage* returnImage = [UIImage imageWithCGImage:img];
CGImageRelease(img);
return returnImage;
}
#end
Usage:
UIImage* colaImage = [UIImage imageNamed:#"cola1.png"];
UIImage* blackColaImage = [colaImage imageByReplacingColor:[UIColor colorWithRed:1 green:0 blue:0 alpha:1] withMinTolerance:0.5 withMaxTolerance:0.6 withColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1]];
The minTolerance is the point at which pixels will start to blend with the replacement colour (rather than being replaced). The maxTolerance is the point at which the pixels will stop being blended.
Before:
After:
The result is a little aliased, but bear in mind that your original image was fairly small. This will work much better with a higher resolution image. You can also play about with the tolerances to get even better results!
You can use this
theImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[theImageView setTintColor:[UIColor blackColor]];
I think something wrong with your image here is my image, it is in white .
here is the change in simulator
Note: First, You need to set your image's Background as transparent with Photoshop or any other tool.
Then use below code.
Easy way,
You need to use rendering mode to change this color.
cokeImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; // yourimageviewname
[cokeImageView setTintColor:[UIColor blackColor]];
I've got two methods I'm working with, and they aren't playing nicely. The first is a Perlin noise generator, which exports a black-and-white UIImage of random clouds, and it's working perfectly. The second method takes a UIImage and filters out all pixels above or below a given brightness, returning an image with transparency where the unwanted pixels were, and it's working perfectly with the black-and-white test images I've been using.
But when I try to feed an image from the first method into the second, it doesn't work. Every pixel gets removed, regardless of the input values, and I get back a blank UIImage. (To be clear, that's a non-nil UIImage with nothing but transparent pixels, as though every pixel is being counted as outside the desired brightness range, regardless of that pixel's actual brightness.)
Below are the two methods. I adapted each from tutorials and SO answers, but while I'm not 100% comfortable with Core Graphics, they seem reasonably simple to me: the first iterates through each pixel and colors it with RGB values from a Perlin formula, and the second creates a mask based on input values. (Note: both are category methods on UIImage, so the "self" references in the latter method are referring to the source image.)
+ (UIImage *)perlinMapOfSize:(CGSize)size {
CZGPerlinGenerator *generator = [[CZGPerlinGenerator alloc] init];
generator.octaves = 10;
generator.persistence = 0.5;
generator.zoom = 150;
CGContextRef ctx = [self contextSetup:size];
CGContextSetRGBFillColor(ctx, 0.000, 0.000, 0.000, 1.000);
CGContextFillRect(ctx, CGRectMake(0.0, 0.0, size.width, size.height));
for (CGFloat x = 0.0; x<size.width; x+=1.0) {
for (CGFloat y=0.0; y<size.height; y+=1.0) {
double value = [generator perlinNoiseX:x y:y z:0 t:0];
CGContextSetRGBFillColor(ctx, value, value, value, 1.0);
CGContextFillRect(ctx, CGRectMake(x, y, 1.0, 1.0));
}
}
return [self finishImageContext];
}
-(UIImage*)imageWithLumaMaskFromDark:(CGFloat)lumaFloor toLight:(CGFloat)lumaCeil {
// inputs range from 0 - 255
CGImageRef rawImageRef = self.CGImage;
const CGFloat colorMasking[6] = {lumaFloor, lumaCeil, lumaFloor, lumaCeil, lumaFloor, lumaCeil};
UIGraphicsBeginImageContext(self.size);
CGImageRef maskedImageRef = CGImageCreateWithMaskingColors(rawImageRef, colorMasking);
{
//if in iphone
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0.0, self.size.height);
CGContextScaleCTM(UIGraphicsGetCurrentContext(), 1.0, -1.0);
}
CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, self.size.width, self.size.height), maskedImageRef);
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
CGImageRelease(maskedImageRef);
UIGraphicsEndImageContext();
return result;
}
Does anyone know why an image from the former method would be incompatible with the latter? The former method is successfully returning cloud images, and the latter method is working with every image I feed into it from my computer or the internet, just not the images from the former method.
I'm assuming that the CGImageCreateWithMaskingColors() call in the second method is looking for some information that the first method isn't putting into the image, or something, I just don't know the system well enough to figure out what's wrong.
Can anyone shed some light?
EDIT: As requested, here are the two other methods referenced above. It's an odd setup, I know, to use class methods like that in a category, but it's how I found the code in a tutorial and it works so I never bothered to change it.
+ (CGContextRef) contextSetup: (CGSize) size {
UIGraphicsBeginImageContext(size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(context);
//NSLog(#"Begin drawing");
return context;
}
+ (UIImage *) finishImageContext {
//NSLog(#"End drawing");
UIGraphicsPopContext();
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}
EDIT 2: Based on some research that the CGImageCreateWithMaskingColors() function doesn't work with images that include alpha components, I've rearranged the first method like so. My gut tells me this was the problem, but I'm kind of casting about in the dark. This is my attempt at trying to create an image with kCGImageAlphaNone, but now UIGraphicsGetImageFromCurrentImageContext() at the end is returning nil.
+ (UIImage *)perlinMapOfSize:(CGSize)size {
CZGPerlinGenerator *generator = [[CZGPerlinGenerator alloc] init];
generator.octaves = 10;
generator.persistence = 0.5;
generator.zoom = 150;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(NULL, size.width, size.height, 8, size.width * 4, colorSpace, kCGImageAlphaNoneSkipLast);
UIGraphicsPushContext(ctx);
CGContextSetRGBFillColor(ctx, 0.000, 0.000, 0.000, 1.0);
CGContextFillRect(ctx, CGRectMake(0.0, 0.0, size.width, size.height));
for (int x = 0; x<size.width; x++) {
for (int y=0; y<size.height; y++) {
double value = [generator perlinNoiseX:x y:y z:0 t:0];
CGContextSetRGBFillColor(ctx, value, value, value, 1.0);
CGContextFillRect(ctx, CGRectMake(x, y, 1.0, 1.0));
}
}
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
NSLog(#"Output: %#", outputImage);
UIGraphicsEndImageContext();
CGContextRelease(ctx);
return outputImage;
}
As you've discovered, CGImageCreateWithMaskingColors requires an image without an alpha channel. However your attempted fix doesn't work as #matt points out because you're trying to mix and match image context function calls (e.g UIGraphicsGetImageFromCurrentImageContext) with a bitmap context.
The simplest fix therefore is to simply carry on using an image context, but set it to be opaque. You can do this by calling UIGraphicsBeginImageContextWithOptions and supplying YES into the opaque argument, which will output an image without an alpha channel.
Although that being said, using a bitmap context here would be a more appropriate solution as it allows you to manipulate the pixel data directly, rather than doing a ton of CGContextFillRect calls.
Something like this should achieve the desired result:
+(UIImage *)perlinMapOfSize:(CGSize)size {
// your generator setup
CZGPerlinGenerator *generator = [[CZGPerlinGenerator alloc] init];
generator.octaves = 10;
generator.persistence = 0.5;
generator.zoom = 150;
// bitmap info
size_t width = size.width;
size_t height = size.height;
size_t bytesPerPixel = 4;
size_t bitsPerComponent = 8;
size_t bytesPerRow = width * bytesPerPixel;
CGBitmapInfo bitmapInfo = kCGImageAlphaNoneSkipLast | kCGBitmapByteOrder32Big;
// allocate memory for the bitmap
UInt8* imgData = calloc(bytesPerRow, height);
// create RGB color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// create an RGBA bitmap context where the alpha component is ignored
CGContextRef ctx = CGBitmapContextCreate(imgData, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
// iterate over pixels
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
// the current pixel index
size_t byte = x * bytesPerPixel + y * bytesPerRow;
// get noise data for given x & y
int value = round([generator perlinNoiseX:x y:y z:0 t:0]*255.0);
// limit values (not too sure of the range of values that the method outputs – this may not be needed)
if (value > 255) value = 255;
else if (value < 0) value = 0;
// write values to pixel components
imgData[byte] = value; // R
imgData[byte+1] = value; // G
imgData[byte+2] = value; // B
}
}
// get image
CGImageRef imgRef = CGBitmapContextCreateImage(ctx);
UIImage* img = [UIImage imageWithCGImage:imgRef];
// clean up
CGContextRelease(ctx);
CGColorSpaceRelease(colorSpace);
CGImageRelease(imgRef);
free(imgData);
return img;
}
A few other things to note
UIGraphicsBeginImageContext(WithOptions) automatically makes the image context the current context – thus you don't need to do UIGraphicsPushContext/UIGraphicsPopContext with it.
UIGraphicsBeginImageContext uses a scale of 1.0 – meaning you're working with sizes in pixels, not points. Therefore the images you ouput may not be suitable for 2x or 3x displays. You should usually be using UIGraphicsBeginImageContextWithOptions instead, with a scale of 0.0 (the main screen scale) or image.scale if you're just manipulating a given image (appropriate for your imageWithLumaMaskFromDark method).
CGBitmapContextCreate will also create a context with a scale of 1.0. If you want to scale the image to the same scale as your screen, you'll want to simply multiply the width and height that you input by the screen scale:
CGFloat scale = [UIScreen mainScreen].scale;
size_t width = size.width*scale;
size_t height = size.height*scale;
and then supply the scale when you create the output UIImage:
UIImage* img = [UIImage imageWithCGImage:imgRef scale:scale orientation:UIImageOrientationUp];
If you want to do some CGContext drawing calls in the bitmap context, you'll also want to scale it before drawing, so you can work in a points coordinate system:
CGContextScaleCTM(ctx, scale, scale);
I want to change the color of an opaque UIImage. My original image is as follows:
and I want to convert the image to the following format
So, basically I want to convert red color in the image into black (Any other color) color. Above two images are added for better understanding.
I couldn't see any answers on the 'duplicates' (this question shouldn't have been flagged as a duplicate) that will let you replace a given color with another color and work on an opaque image, so I decided to add one that would.
I created a UIImage category to do this, it basically works by looping through each pixel and detecting how close it is to a given colour, and blends it with your replacement colour if it is.
This will work for images with both transparency and opaque backgrounds.
#implementation UIImage (UIImageColorReplacement)
-(UIImage*) imageByReplacingColor:(UIColor*)sourceColor withMinTolerance:(CGFloat)minTolerance withMaxTolerance:(CGFloat)maxTolerance withColor:(UIColor*)destinationColor {
// components of the source color
const CGFloat* sourceComponents = CGColorGetComponents(sourceColor.CGColor);
UInt8* source255Components = malloc(sizeof(UInt8)*4);
for (int i = 0; i < 4; i++) source255Components[i] = (UInt8)round(sourceComponents[i]*255.0);
// components of the destination color
const CGFloat* destinationComponents = CGColorGetComponents(destinationColor.CGColor);
UInt8* destination255Components = malloc(sizeof(UInt8)*4);
for (int i = 0; i < 4; i++) destination255Components[i] = (UInt8)round(destinationComponents[i]*255.0);
// raw image reference
CGImageRef rawImage = self.CGImage;
// image attributes
size_t width = CGImageGetWidth(rawImage);
size_t height = CGImageGetHeight(rawImage);
CGRect rect = {CGPointZero, {width, height}};
// bitmap format
size_t bitsPerComponent = 8;
size_t bytesPerRow = width*4;
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;
// data pointer
UInt8* data = calloc(bytesPerRow, height);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// create bitmap context
CGContextRef ctx = CGBitmapContextCreate(data, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
CGContextDrawImage(ctx, rect, rawImage);
// loop through each pixel's components
for (int byte = 0; byte < bytesPerRow*height; byte += 4) {
UInt8 r = data[byte];
UInt8 g = data[byte+1];
UInt8 b = data[byte+2];
// delta components
UInt8 dr = abs(r-source255Components[0]);
UInt8 dg = abs(g-source255Components[1]);
UInt8 db = abs(b-source255Components[2]);
// ratio of 'how far away' each component is from the source color
CGFloat ratio = (dr+dg+db)/(255.0*3.0);
if (ratio > maxTolerance) ratio = 1; // if ratio is too far away, set it to max.
if (ratio < minTolerance) ratio = 0; // if ratio isn't far enough away, set it to min.
// blend color components
data[byte] = (UInt8)round(ratio*r)+(UInt8)round((1.0-ratio)*destination255Components[0]);
data[byte+1] = (UInt8)round(ratio*g)+(UInt8)round((1.0-ratio)*destination255Components[1]);
data[byte+2] = (UInt8)round(ratio*b)+(UInt8)round((1.0-ratio)*destination255Components[2]);
}
// get image from context
CGImageRef img = CGBitmapContextCreateImage(ctx);
// clean up
CGContextRelease(ctx);
CGColorSpaceRelease(colorSpace);
free(data);
free(source255Components);
free(destination255Components);
UIImage* returnImage = [UIImage imageWithCGImage:img];
CGImageRelease(img);
return returnImage;
}
#end
Usage:
UIImage* colaImage = [UIImage imageNamed:#"cola1.png"];
UIImage* blackColaImage = [colaImage imageByReplacingColor:[UIColor colorWithRed:1 green:0 blue:0 alpha:1] withMinTolerance:0.5 withMaxTolerance:0.6 withColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:1]];
The minTolerance is the point at which pixels will start to blend with the replacement colour (rather than being replaced). The maxTolerance is the point at which the pixels will stop being blended.
Before:
After:
The result is a little aliased, but bear in mind that your original image was fairly small. This will work much better with a higher resolution image. You can also play about with the tolerances to get even better results!
You can use this
theImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[theImageView setTintColor:[UIColor blackColor]];
I think something wrong with your image here is my image, it is in white .
here is the change in simulator
Note: First, You need to set your image's Background as transparent with Photoshop or any other tool.
Then use below code.
Easy way,
You need to use rendering mode to change this color.
cokeImageView.image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; // yourimageviewname
[cokeImageView setTintColor:[UIColor blackColor]];
Is it possible to make CGContextClipToMask ignore the grayscale values of the mask image and work as if it was plain black and white?
I have a grayscale image, and when I use it as a mask gray color are interpreted as an alpha channel. This is fine except for a point where I need to completely mask those pixels that are not transparent.
Short example:
UIImage *mask = [self prepareMaskImage];
UIGraphicsBeginImageContextWithOptions(mask.size, NO, mask.scale); {
// Custom code
CGContextClipToMask(UIGraphicsGetCurrentContext(), mask.size, mask.CGImage);
// Custom code
}
Is it possible to adapt this code to achieve my goal?
Long story short: I need to make a transparent grayscale image become transparent where it originally was and completely black where it's solid-colored.
Interesting problem! Here's code that does what I think you want in a simple sample project. Similar to above but handles scale properly. Also has option to retain the alpha in the mask image if you want. Quick hacked together test that seems to work.
My rough idea would be the following:
You convert your input image to readable byte data in grey + alpha format. Maybe you have to do RGBA instead due to iOS limitations.
You iterate over the byte data modifying the values in place.
To simplify access, use a
typedef struct RGBA {
UInt8 red;
UInt8 green;
UInt8 blue;
UInt8 alpha;
} RGBA;
Let's assume image your input mask.
// First step, using RGBA (because I know it works and does not harm, just writes/consumes twice the amount of memory)
CGImageRef imageRef = image.CGImage;
NSInteger rawWidth = CGImageGetWidth(imageRef);
NSInteger rawHeight = CGImageGetHeight(imageRef);
NSInteger rawBitsPerComponent = 8;
NSInteger rawBytesPerPixel = 4;
NSInteger rawBytesPerRow = rawBytesPerPixel * rawWidth;
CGRect rawRect = CGRectMake(0, 0, rawWidth, rawHeight);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
UInt8 *rawImage = (UInt8 *)malloc(rawHeight * rawWidth * rawBytesPerPixel);
CGContextRef rawContext = CGBitmapContextCreate(rawImage,
rawWidth,
rawHeight,
rawBitsPerComponent,
rawBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace);
// At this point, rawContext is ready for drawing, everything drawn will be in rawImage's byte array.
CGContextDrawImage(rawContext, rawRect, imageRef);
// Second step, crawl the byte array and do the evil work:
for (NSInteger y = 0; y < rawHeight; ++y) {
for (NSInteger x = 0; x < rawWidth; ++x) {
UInt8 *address = rawImage + x * rawBytesPerPixel + y * rawBytesPerRow;
RGBA *pixel = (RGBA *)address;
// If it is a grey input image, it does not matter what RGB channel to use - they shall all be the same
if (0 != pixel->red) {
pixel->alpha = 0;
} else {
pixel->alpha = UINT8_MAX;
}
pixel->red = 0;
pixel->green = 0;
pixel->blue = 0;
// I am still not sure if this is the transformation you are searching for, but it may give you the idea.
}
}
// Third: rawContext is ready, transformation is done. Get the image out of it
CGImageRef outputImage1 = CGBitmapContextCreateImage(rawContext);
UIImage *outputImage2 = [UIImage imageWithCGImage:outputImage1];
CGImageRelease(outputImage1);
Okay... the output is RGBA, but you can create a greyscale + alpha context and just blit your image there for conversion.
This piece of code helped me applying hue on non-transparent area of an image.
- (UIImage*)imageWithImage:(UIImageView*)source colorValue:(CGFloat)hue {
CGSize imageSize = [source.image size];
CGRect imageExtent = CGRectMake(0,0,imageSize.width,imageSize.height);
// Create a context containing the image.
UIGraphicsBeginImageContext(imageSize);
CGContextRef context = UIGraphicsGetCurrentContext();
[source.image drawAtPoint:CGPointMake(0, 0)];
// Setup a clip region using the image
CGContextSaveGState(context);
CGContextClipToMask(context, source.bounds, source.image.CGImage);
self.imageColor = [UIColor colorWithHue:hue saturation:1.0 brightness:1 alpha:1.0];
[self.imageColor set];
CGContextFillRect(context, source.bounds);
// Draw the hue on top of the image.
CGContextSetBlendMode(context, kCGBlendModeHue);
[self.imageColor set];
UIBezierPath *imagePath = [UIBezierPath bezierPathWithRect:imageExtent];
[imagePath fill];
CGContextRestoreGState(context); // remove clip region
// Retrieve the new image.
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
}
I am using https://github.com/PaulSolt/UIImage-Conversion
However, it seems to have a problem on the alpha channel. Problem reconstituting UIImage from RGBA pixel byte data
I have simplified the problem so I present it as a tidier question.
I download Paul's project, run it. It displays an image in the centre of the screen -- everything so far is correct.
But his demo is not testing for Alpha.
So I attempt to composite my button image...
... on top.
The result is:
here is the code -- I have just modified AppDelegate_iPad.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
{
UIImage *image = [UIImage imageNamed:#"Icon4.png"];
int width = image.size.width;
int height = image.size.height;
// Create a bitmap
unsigned char *bitmap = [ImageHelper convertUIImageToBitmapRGBA8:image];
// Create a UIImage using the bitmap
UIImage *imageCopy = [ImageHelper convertBitmapRGBA8ToUIImage:bitmap withWidth:width withHeight:height];
// Cleanup
if(bitmap) {
free(bitmap);
bitmap = NULL;
}
// Display the image copy on the GUI
UIImageView *imageView = [[UIImageView alloc] initWithImage:imageCopy];
CGPoint center = CGPointMake([UIScreen mainScreen].bounds.size.width / 2.0,
[UIScreen mainScreen].bounds.size.height / 2.0);
[imageView setCenter:center];
[window addSubview:imageView];
[imageView release];
}
if( 1 )
{
UIImage *image = [UIImage imageNamed:#"OuterButton_Dull.png"];
int width = image.size.width;
int height = image.size.height;
// Create a bitmap
unsigned char *bitmap = [ImageHelper convertUIImageToBitmapRGBA8:image];
// Create a UIImage using the bitmap
UIImage *imageCopy = [ImageHelper convertBitmapRGBA8ToUIImage:bitmap withWidth:width withHeight:height];
// Cleanup
if(bitmap) {
free(bitmap);
bitmap = NULL;
}
// Display the image copy on the GUI
UIImageView *imageView = [[UIImageView alloc] initWithImage:imageCopy]; // <-- X
imageView.backgroundColor = [UIColor clearColor];
CGPoint center = CGPointMake([UIScreen mainScreen].bounds.size.width / 2.0,
[UIScreen mainScreen].bounds.size.height / 2.0);
[imageView setCenter:center];
[window addSubview:imageView];
[imageView release];
}
[window makeKeyAndVisible];
return YES;
}
If I switch the line marked...
// <-- X
to say...
... initWithImage:image]; // instead of imageCopy
... it displays as it should:
Is anyone up having a crack at this? It looks a really nice library to use, but I can't figure out where the problem is.
I just got a reply from the author:
At the bottom of his blog post, http://paulsolt.com/2010/09/ios-converting-uiimage-to-rgba8-bitmaps-and-back/ Scott Davies presents a fix:
There’s a simple one-line fix for your code so that alpha channels are
preserved when you go from the byte buffer to the UIImage. In
convertBitmapRGBA8ToUIImage, change this:
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
to this:
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault |
kCGImageAlphaPremultipliedLast;