views:

811

answers:

6

I am trying to do some Image processing on a UIImage using some EAGLView code from the GLImageProcessing sample from Apple. The sample code is configured to perform processing to a pre-installed image (Image.png). I am trying to modify the code so that it will accept a UIImage (or at least CGImage data) of my choice and process that instead. Problem is, the texture-loader method loadTexture() (below) seems to accept only C structures as parameters, and I have not been able to get it to accept a UIImage* or a CGImage as a parameter. Can someone give me a clue as how to bridge the gap so that I can pass my UIImage into the C-method?

------------ from Texture.h ---------------

#ifndef TEXTURE_H
#define TEXTURE_H

#include "Imaging.h"


void loadTexture(const char *name, Image *img, RendererInfo *renderer);

#endif /* TEXTURE_H */

----------------from Texture.m---------------------

#import <UIKit/UIKit.h>
#import "Texture.h"


static unsigned int nextPOT(unsigned int x)
{
    x = x - 1;
    x = x | (x >> 1);
    x = x | (x >> 2);
    x = x | (x >> 4);
    x = x | (x >> 8);
    x = x | (x >>16);
    return x + 1;
}


// This is not a fully generalized image loader. It is an example of how to use
// CGImage to directly access decompressed image data. Only the most commonly
// used image formats are supported. It will be necessary to expand this code
// to account for other uses, for example cubemaps or compressed textures.
//
// If the image format is supported, this loader will Gen a OpenGL 2D texture object
// and upload texels from it, padding to POT if needed. For image processing purposes,
// border pixels are also replicated here to ensure proper filtering during e.g. blur.
//
// The caller of this function is responsible for deleting the GL texture object.
void loadTexture(const char *name, Image *img, RendererInfo *renderer)
{
    GLuint texID = 0, components, x, y;
    GLuint imgWide, imgHigh;      // Real image size
    GLuint rowBytes, rowPixels;   // Image size padded by CGImage
    GLuint POTWide, POTHigh;      // Image size padded to next power of two
    CGBitmapInfo info;            // CGImage component layout info
    CGColorSpaceModel colormodel; // CGImage colormodel (RGB, CMYK, paletted, etc)
    GLenum internal, format;
    GLubyte *pixels, *temp = NULL;

    CGImageRef CGImage = [UIImage imageNamed:[NSString stringWithUTF8String:name]].CGImage;
    rt_assert(CGImage);
    if (!CGImage)
     return;

    // Parse CGImage info
    info       = CGImageGetBitmapInfo(CGImage);  // CGImage may return pixels in RGBA, BGRA, or ARGB order
    colormodel = CGColorSpaceGetModel(CGImageGetColorSpace(CGImage));
    size_t bpp = CGImageGetBitsPerPixel(CGImage);
    if (bpp < 8 || bpp > 32 || (colormodel != kCGColorSpaceModelMonochrome && colormodel != kCGColorSpaceModelRGB))
    {
     // This loader does not support all possible CGImage types, such as paletted images
     CGImageRelease(CGImage);
     return;
    }
    components = bpp>>3;
    rowBytes   = CGImageGetBytesPerRow(CGImage); // CGImage may pad rows
    rowPixels  = rowBytes / components;
    imgWide    = CGImageGetWidth(CGImage);
    imgHigh    = CGImageGetHeight(CGImage);
    img->wide  = rowPixels;
    img->high  = imgHigh;
    img->s     = (float)imgWide / rowPixels;
    img->t     = 1.0;

    // Choose OpenGL format
    switch(bpp)
    {
     default:
      rt_assert(0 && "Unknown CGImage bpp");
     case 32:
     {
      internal = GL_RGBA;
      switch(info & kCGBitmapAlphaInfoMask)
      {
       case kCGImageAlphaPremultipliedFirst:
       case kCGImageAlphaFirst:
       case kCGImageAlphaNoneSkipFirst:
        format = GL_BGRA;
        break;
       default:
        format = GL_RGBA;
      }
      break;
     }
     case 24:
      internal = format = GL_RGB;
      break;
     case 16:
      internal = format = GL_LUMINANCE_ALPHA;
      break;
     case 8:
      internal = format = GL_LUMINANCE;
      break;
    }

    // Get a pointer to the uncompressed image data.
    //
    // This allows access to the original (possibly unpremultiplied) data, but any manipulation
    // (such as scaling) has to be done manually. Contrast this with drawing the image
    // into a CGBitmapContext, which allows scaling, but always forces premultiplication.
    CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(CGImage));
    rt_assert(data);
    pixels = (GLubyte *)CFDataGetBytePtr(data);
    rt_assert(pixels);

    // If the CGImage component layout isn't compatible with OpenGL, fix it.
    // On the device, CGImage will generally return BGRA or RGBA.
    // On the simulator, CGImage may return ARGB, depending on the file format.
    if (format == GL_BGRA)
    {
     uint32_t *p = (uint32_t *)pixels;
     int i, num = img->wide * img->high;

     if ((info & kCGBitmapByteOrderMask) != kCGBitmapByteOrder32Host)
     {
      // Convert from ARGB to BGRA
      for (i = 0; i < num; i++)
       p[i] = (p[i] << 24) | ((p[i] & 0xFF00) << 8) | ((p[i] >> 8) & 0xFF00) | (p[i] >> 24);
     }

     // All current iPhoneOS devices support BGRA via an extension.
     if (!renderer->extension[IMG_texture_format_BGRA8888])
     {
      format = GL_RGBA;

      // Convert from BGRA to RGBA
      for (i = 0; i < num; i++)
#if __LITTLE_ENDIAN__
       p[i] = ((p[i] >> 16) & 0xFF) | (p[i] & 0xFF00FF00) | ((p[i] & 0xFF) << 16);
#else
      p[i] = ((p[i] & 0xFF00) << 16) | (p[i] & 0xFF00FF) | ((p[i] >> 16) & 0xFF00);
#endif
     }
    }

    // Determine if we need to pad this image to a power of two.
    // There are multiple ways to deal with NPOT images on renderers that only support POT:
    // 1) scale down the image to POT size. Loses quality.
    // 2) pad up the image to POT size. Wastes memory.
    // 3) slice the image into multiple POT textures. Requires more rendering logic.
    //
    // We are only dealing with a single image here, and pick 2) for simplicity.
    //
    // If you prefer 1), you can use CoreGraphics to scale the image into a CGBitmapContext.
    POTWide = nextPOT(img->wide);
    POTHigh = nextPOT(img->high);

    if (!renderer->extension[APPLE_texture_2D_limited_npot] && (img->wide != POTWide || img->high != POTHigh))
    {
     GLuint dstBytes = POTWide * components;
     GLubyte *temp = (GLubyte *)malloc(dstBytes * POTHigh);

     for (y = 0; y < img->high; y++)
      memcpy(&temp[y*dstBytes], &pixels[y*rowBytes], rowBytes);

     img->s *= (float)img->wide/POTWide;
     img->t *= (float)img->high/POTHigh;
     img->wide = POTWide;
     img->high = POTHigh;
     pixels = temp;
     rowBytes = dstBytes;
    }

    // For filters that sample texel neighborhoods (like blur), we must replicate
    // the edge texels of the original input, to simulate CLAMP_TO_EDGE.
    {
     GLuint replicatew = MIN(MAX_FILTER_RADIUS, img->wide-imgWide);
     GLuint replicateh = MIN(MAX_FILTER_RADIUS, img->high-imgHigh);
     GLuint imgRow = imgWide * components;

     for (y = 0; y < imgHigh; y++)
      for (x = 0; x < replicatew; x++)
       memcpy(&pixels[y*rowBytes+imgRow+x*components], &pixels[y*rowBytes+imgRow-components], components);
     for (y = imgHigh; y < imgHigh+replicateh; y++)
      memcpy(&pixels[y*rowBytes], &pixels[(imgHigh-1)*rowBytes], imgRow+replicatew*components);
    }

    if (img->wide <= renderer->maxTextureSize && img->high <= renderer->maxTextureSize)
    {
     glGenTextures(1, &texID);
     glBindTexture(GL_TEXTURE_2D, texID);
     // Set filtering parameters appropriate for this application (image processing on screen-aligned quads.)
     // Depending on your needs, you may prefer linear filtering, or mipmap generation.
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glTexImage2D(GL_TEXTURE_2D, 0, internal, img->wide, img->high, 0, format, GL_UNSIGNED_BYTE, pixels);
    }

    if (temp) free(temp);
    CFRelease(data);
    CGImageRelease(CGImage);
    img->texID = texID;
}

Side Note: The above code is the original and unmodified sample code from Apple and does not generate any errors when compiled. However, when I try to modify the .h and .m to accept a UIImage* parameter (as below) the compiler generates the following error:"Error: expected declaration specifiers or "..." before UIImage"

----------Modified .h Code that generates the Compiler Error:-------------

void loadTexture(const char name, Image *img, RendererInfo *renderer, UIImage* newImage)
+1  A: 

It sounds like your file isn't importing the UIKit header.

Chuck
The .m file imports the UIKit header file (as seen above). Should I be doing this in the .h as well?
RexOnRoids
Just tried importing the UIKit header file in the .h as well. When I did that my error count suddenly jumped from 1 error to 1721 errors! The compiler didn't like THAT very much lol. 99% of the new errors are: "Error: expected identifier or '(' before '-' token"
RexOnRoids
+4  A: 

You are probably importing this .h into a .c somewhere. That tells the compiler to use C rather than Objective-C. UIKit.h (and it's many children) are in Objective-C and cannot be compiled by a C compiler.

You can rename all you .c files to .m, but what you really probably want is just to use CGImageRef and import CGImage.h. CoreGraphics is C-based. UIKit is Objective-C. There is no problem, if you want, for Texture.m to be in Objective-C. Just make sure that Texture.h is pure C. Alternatively (and I do this a lot with C++ code), you can make a Texture+C.h header that provides just the C-safe functions you want to expose. Import Texture.h in Objective-C code, and Texture+C.h in C code. Or name them the other way around if more convenient, with a Texture+ObjC.h.

Rob Napier
Thanks, you're awesome! I ended up importing <CoreGraphics/CGImage.h> and passing a CGImageRef as a parameter. I hope this works!
RexOnRoids
+1  A: 

WHy are you passing new image to loadTexture, instead of using loadTexture's own UImage loading to open the new image you want?

loadTexture:

void loadTexture(const char *name, Image *img, RendererInfo *renderer)
{
    GLuint texID = 0, components, x, y;
    GLuint imgWide, imgHigh;      // Real image size
    GLuint rowBytes, rowPixels;   // Image size padded by CGImage
    GLuint POTWide, POTHigh;      // Image size padded to next power of two
    CGBitmapInfo info;            // CGImage component layout info
    CGColorSpaceModel colormodel; // CGImage colormodel (RGB, CMYK, paletted, etc)
    GLenum internal, format;
    GLubyte *pixels, *temp = NULL;


[Why not have the following fetch your UIImage?]


    CGImageRef CGImage = [UIImage imageNamed:[NSString stringWithUTF8String:name]].CGImage;
    rt_assert(CGImage);
    if (!CGImage)
     return;
mahboudz
Great question, thanks. I am doing this because the image I want to pass would have JUST been pulled from the iPhone camera and thus not in my bundle.
RexOnRoids
A: 

Did you ever get this to work? I'm trying to do the same thing using the GLImageProcessing as my sample code.

I'd love to see some code that works.

I try passing a CGImageRef but it says I'm passing argument 1 of xxxx from incompatible pointer type.

chickenhouse
No. I just gave up on OpenGL ES and tried something else. I just got a CGImageRef and a buffer of the pixel data and looped through each pixel changing the RGB values accordingly. Specifically what kind of code do you need? I might be able to help.
RexOnRoids
I'm pretty much doing the same thing as you. Trying to brighten the image taken by the camera.
chickenhouse
Actually the problem I'm having is trying to covert the Image to the CGImageRef. Maybe if I can do that it will work. Currently I'm trying,- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { UIImage *tempImage = (UIImage *)[info objectForKey:@"UIImagePickerControllerOriginalImage"]; [((EAGLView*)self.view) initView]; snapShot = tempImage.CGImage; }Where snapShot is a CGImageRef but it keeps giving the error.warning: Passing argument 1 of 'setSnapShot:' from incompatible pointer type.I think I'm being stupid.
chickenhouse
A: 

If you pass image from imagepicker, it is not shown properly. Anyone else had issue?

Saqib Saud
A: 

I also tried this sample, and try to use image picker , but seems like no solution now.

Just wonder if there is some option to reset texture of eaglview ? but need unerstand opengl workflow, to make sure if this makes sense or not.

Forrest