I have made a static library Win32 (it doesn't actually contain Win32 code) library in MSVC 2008 and I am currently trying to link to it in QT Creator. But whenever I compile, I get the error:
C:\Users\Snowball\Documents\QT Creator\Libraries\FontSystem\main.cpp:-1: error: undefined reference to `NGUI::CFont::CFont()'
The library is a font system that loads a PNG using FreeImage and then "cuts" it up into individual symbols then passes the image data to gluBuild2DMipMaps() which then creates an OpenGL texture out of it to be used later on when drawing strings. I have ALL my class methods defined and the whole class is part of a namespace called NGUI. This way the font system won't be confused with another if for some reason two are in use. To link this library, I simply added the following code to my .pro file: LIBS += FontSystem.lib
and the only file in the application right now is this:
#include "fontsystem.h"
using namespace NGUI;
int main(int argc, char *argv[])
{
cout<< "Starting the FontSystem..."<< endl;
CFont *cFont = new CFont();
cout<< "FontSystem Started!"<< endl;
system("sleep 1");
return 0;
}
The file fontsystem.h looks like this:
#ifndef FONTSYSTEM_H
#define FONTSYSTEM_H
// Include the Basic C++ headers.
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <limits>
using namespace std;
// Include the OpenGL and Image Headers.
#include <GL/gl.h>
#include <GL/glu.h>
#include "utilities.h"
// Macros.
#define DIM(x) (sizeof(x)/sizeof(*(x)))
/*
The Font System works by loading all the font images (max image size 32px^2) into memory and storing
the OpenGL texture ID's into an array that can be access at all times. The DrawString() functions will
search through the string for the specified character requested to draw and then it will draw a quad
and paint the texture on it. Since all the images are pre-loaded, no loading of the images at load time
is necessary, this is memory consuming but efficiant for the CPU. ALL functions WILL return a character
string specifing errors or success. A function will work as long as it can and when an error happens,
unless the error is fatal, the functions will NOT rollback changes! This ensures that during testing, a
very certain bug can be spotted.
*/
namespace NGUI // This namespace is used to ensure no confusion happens. This font system paints 2D fonts for GUIs.
{
class CFont
{
public:
CFont();
~CFont();
template<typename tChar> char* DrawString(tChar *apString, int aiSize, int aiX, int aiY);
template<typename tNum> char* DrawString(tNum anNumber, int aiSize, int aiX, int aiY);
private:
char* SetupFont(); // This function will load as many images as possible into memory.
GLuint miTextIDs[36];
int miDrawIDs[1024];
};
}
#endif // FONTSYSTEM_H
EDIT:
Here is the implementation file for fontsystem.h
#include "fontsystem.h"
#include "fontsystem.h"
namespace NGUI
{
CFont::CFont()
{
SetupFont();
}
CFont::~CFont() {}
template<typename tChar>
char* CFont::DrawString(tChar *apString, int aiSize, int aiX, int aiY)
{
// Search the string from most significant character to least significant.
int iSelectIndex = 0;
for(size_t i = 0; apString[i] != NULL; ++i)
{
iSelectIndex = apString[i] >= '0' && apString[i] <= '9' ? (apString[i] - '0') :
apString[i] >= 'A' && apString[i] <= 'Z' ? (apString[i] - 'A' + 10) :
apString[i] >= 'a' && apString[i] <= 'z' ? (apString[i] - 'a' + 10) :
apString[i] == ' ' ? 36 : // This is a special case, This see's if the current character is a space or not.
-1;
if(iSelectIndex == -1)
{
return "The String Is Corrupt! Aborting!";
}
// Add the current selected character to the drawing array.
miDrawIDs[i] = iSelectIndex;
}
// Go through and draw each and every character.
for(size_t i = 0; i < DIM(miDrawIDs); ++i)
{
// Paint each qaud with the X,Y coordinates. After each quad has been successfully drawn,
// Add the size to the X coordinate. NOTE: Each character is square!!!
if(miDrawIDs[i] != 36)
{
glBindTexture(GL_TEXTURE_2D, miDrawIDs[i]);
}
// The font color is always white.
glColor4f(1.0, 1.0, 1.0, 0.0); // The alpha argument in the function call is set to 0 to allow color only where image data is present.
glBegin(GL_QUADS);
glTexCoord2i(0, 0);
glVertex2i(aiX, aiY);
glTexCoord2i(1, 0);
glVertex2i(aiX + aiSize, aiY);
glTexCoord2i(1, 1);
glVertex2i(aiX + aiSize, aiY + aiSize);
glTexCoord2i(0, 1);
glVertex2i(aiX, aiY + aiSize);
glEnd();
// Now, increase the X position by the size.
aiX += aiSize;
}
return "Successful Drawing of String!";
}
template<typename tNum>
char* CFont::DrawString(tNum anNumber, int aiSize, int aiX, int aiY)
{
// Convert the supplied number to a character string via snprintf().
char *vTempString = new char[1024];
snprintf(vTempString, 1024, "%f", anNumber);
// Next, run DrawString().
return DrawString<char>(vTempString, aiSize, aiX, aiY);
}
char* CFont::SetupFont()
{
// First Load The PNG file holding the font.
FreeImage_Initialise(false);
FIBITMAP *spBitmap = FreeImage_Load(FIF_PNG, "Font.png", BMP_DEFAULT);
if(!spBitmap)
{
return "Was Unable To Open/Decode Bitmap!";
}
// Do an image sanity check.
if(!FreeImage_HasPixels(spBitmap))
{
return "The Image doesn't contain any pixel data! Aborting!";
}
// The Image will have the red and blue channel reversed, so we need to correct them.
SwapRedBlue32(spBitmap);
// Retrieve all the image data from FreeImage.
unsigned char *pData = FreeImage_GetBits(spBitmap);
int iWidth = FreeImage_GetWidth(spBitmap);
// Cutup the PNG.
int iFontElementSize = (32*32)*4; // The first two numbers, are the dimensions fo the element, the last number (4) is the number of color channels (Red Green Blue and Alpha)
bool bDone = false; // This bit is only set when the entire image has been loaded.
unsigned char *pElemBuff = new unsigned char[iFontElementSize]; // The temporary element buffer.
int iDataSeek = 4; // Start with an offset of 4 because the first byte of image data starts there.
int iTexIdx = 0; // This is an offset specifing which texture to create/bind to.
// Create all 36 OpenGL texures. 0-9 and A-Z and finally space (' ')
glGenTextures(37, miTextIDs);
while(!bDone)
{
// Now load the an element into the buffer.
for(int i = 0, iXCount = 0, iYCount = 0;
i < iFontElementSize; ++i, ++iXCount)
{
if(iXCount >= (32*4))
{
iXCount = 0; // Reset the column offset.
++iYCount; // Move down 1 row.
iDataSeek += ((iWidth * 4) - (32*4)); // Set the data seek to the next corrosponding piece of image data.
}
if(pData[iDataSeek] == NULL)
{
break;
}
pElemBuff[i] = pData[iDataSeek];
}
// Check to see if we are done loading to prevent memory corruption and leakage.
if(bDone || iTexIdx >= 37)
{
break;
}
// Create The OpenGL Texture with the current Element.
glBindTexture(GL_TEXTURE_2D, miTextIDs[iTexIdx]);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, 32, 32, GL_RGBA, GL_UNSIGNED_BYTE, pElemBuff);
// Create the correct texture envirnment to the current texture.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
}
// Do a little house cleaning!
delete[] pElemBuff;
delete pData;
FreeImage_Unload(spBitmap);
FreeImage_DeInitialise();
}
}
PLEASE NOTE: this code hasn't been tested yet but compile fine (according to MSVC 2008)
I forgot to change the system("sleep 1"); to system("PAUSE"); and the reason I had it invoking that command is because I originally was building this in linux.
EDIT 2: I have updated the implementation code to reflect the file.
Related
I have to load a 24 bit BMP file at a certain (x,y) index of glut window from a file using OpenGL. I have found a function that uses glaux library to do so. Here the color mentioned in ignoreColor is ignored during rendering.
void iShowBMP(int x, int y, char filename[], int ignoreColor)
{
AUX_RGBImageRec *TextureImage;
TextureImage = auxDIBImageLoad(filename);
int i,j,k;
int width = TextureImage->sizeX;
int height = TextureImage->sizeY;
int nPixels = width * height;
int *rgPixels = new int[nPixels];
for (i = 0, j=0; i < nPixels; i++, j += 3)
{
int rgb = 0;
for(int k = 2; k >= 0; k--)
{
rgb = ((rgb << 8) | TextureImage->data[j+k]);
}
rgPixels[i] = (rgb == ignoreColor) ? 0 : 255;
rgPixels[i] = ((rgPixels[i] << 24) | rgb);
}
glRasterPos2f(x, y);
glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, rgPixels);
delete []rgPixels;
free(TextureImage->data);
free(TextureImage);
}
But the problem is that glaux is now obsolete. If I call this function, the image is rendered and shown for a minute, then an error pops up (without any error message) and the glut window disappears. From the returned value shown in the console, it seems like a runtime error.
Is there any alternative to this function that doesn't use glaux? I have seen cimg, devil etc but none of them seems to work like this iShowBMP function. I am doing my project in Codeblocks.
I have to load every frame to keep the implementation consistent with other parts of the program. Also, the bmp file whose name has been passed as a parameter to the function has both width and height in powers of 2.
The last two free() statements were not getting executed for some unknown reasons, so the memory consumption was increasing. That's why the program was crashing after a moment. Later I solved it using stb_image.h.
I want to draw string or char (offscreen) and use it as Fl_Image or Fl_RGB_Image.
Based on this link I can do that easily with Fl__Image__Surface. The problem with Fl__Image__Surface is that it does not support transparency when I convert its output to image (Fl_RGB_Image) using image() method. So is there any way I can achieve this? I can do that on Java Swing with BufferedImage, also in Android with Canvas by creating Bitmap with Bitmap.Config.ARGB_8888.
If you prefer to do it manually, you can try the following:
#include <FL/Enumerations.H>
#include <FL/Fl.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Device.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Image.H>
#include <FL/Fl_Image_Surface.H>
#include <FL/fl_draw.H>
#include <cassert>
#include <vector>
Fl_RGB_Image *get_image(int w, int h) {
// draw image on surface
auto img_surf = new Fl_Image_Surface(w, h);
Fl_Surface_Device::push_current(img_surf);
// We'll use white to mask 255, 255, 255, see the loop
fl_color(FL_WHITE);
fl_rectf(0, 0, w, h);
fl_color(FL_BLACK);
fl_font(FL_HELVETICA_BOLD, 20);
fl_draw("Hello", 100, 100);
auto image = img_surf->image();
delete img_surf;
Fl_Surface_Device::pop_current();
return image;
}
Fl_RGB_Image *get_transparent_image(const Fl_RGB_Image *image) {
assert(image);
// make image transparent
auto data = (const unsigned char*)(*image->data());
auto len = image->w() * image->h() * image->d(); // the depth is by default 3
std::vector<unsigned char> temp;
for (size_t i = 0; i < len; i++) {
if (i > 0 && i % 3 == 0) {
// check if the last 3 vals are the rgb values of white, add a 0 alpha
if (data[i] == 255 && data[i - 1] == 255 && data[i - 2] == 255)
temp.push_back(0);
else
// add a 255 alpha, making the black opaque
temp.push_back(255);
temp.push_back(data[i]);
} else {
temp.push_back(data[i]);
}
}
temp.push_back(0);
assert(temp.size() == image->w() * image->h() * 4);
auto new_image_data = new unsigned char[image->w() * image->h() * 4];
memcpy(new_image_data, temp.data(), image->w() * image->h() * 4);
auto new_image = new Fl_RGB_Image(new_image_data, image->w(), image->h(), 4); // account for alpha
return new_image;
}
int main() {
auto win = new Fl_Double_Window(400, 300);
auto box = new Fl_Box(0, 0, 400, 300);
win->end();
win->show();
auto image = get_image(box->w(), box->h());
auto transparent_image = get_transparent_image(image);
delete image;
box->image(transparent_image);
box->redraw();
return Fl::run();
}
The idea is that an Fl_Image_Surface gives an Fl_RGB_Image with 3 channels (r, g, b), no alpha. We manually add the alpha by creating a temporary vector, querying the data (can be optimized if you know the colors you're using by only checking data[i] == 255. The vector is an RAII type whose life ends at the end of the scope, so we just mempcy the data from the vector to a long-lived unsigned char array that we pass to an Fl_RGB_Image specifying the depth to be 4, accounting for alpha.
The other option is to use an external library like CImg (single header lib) to draw text into an image buffer and then pass that buffer to Fl_RGB_Image.
I am currently trying to render textured objects in Opengl. Everything worked fine until I wanted to render a texture with transparency. Instead of showing the the object transparent it just rendered in total black.
The method fo loading the texture file is this:
// structures for reading and information variables
char magic[4];
unsigned char header[124];
unsigned int width, height, linearSize, mipMapCount, fourCC;
unsigned char* dataBuffer;
unsigned int bufferSize;
fstream file(path, ios::in|ios::binary);
// read magic and header
if (!file.read((char*)magic, sizeof(magic))){
cerr<< "File " << path << " not found!"<<endl;
return false;
}
if (magic[0]!='D' || magic[1]!='D' || magic[2]!='S' || magic[3]!=' '){
cerr<< "File does not comply with dds file format!"<<endl;
return false;
}
if (!file.read((char*)header, sizeof(header))){
cerr<< "Not able to read file information!"<<endl;
return false;
}
// derive information from header
height = *(int*)&(header[8]);
width = *(int*)&(header[12]);
linearSize = *(int*)&(header[16]);
mipMapCount = *(int*)&(header[24]);
fourCC = *(int*)&(header[80]);
// determine dataBuffer size
bufferSize = mipMapCount > 1 ? linearSize * 2 : linearSize;
dataBuffer = new unsigned char [bufferSize*2];
// read data and close file
if (file.read((char*)dataBuffer, bufferSize/1.5))
cout<<"Loading texture "<<path<<" successful"<<endl;
else{
cerr<<"Data of file "<<path<<" corrupted"<<endl;
return false;
}
file.close();
// check pixel format
unsigned int format;
switch(fourCC){
case FOURCC_DXT1:
format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
break;
case FOURCC_DXT3:
format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
break;
case FOURCC_DXT5:
format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
break;
default:
cerr << "Compression type not supported or corrupted!" << endl;
return false;
}
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_2D, ID);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
/* load the mipmaps */
for (unsigned int level = 0; level < mipMapCount && (width || height); ++level) {
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
0, size, dataBuffer + offset);
offset += size;
width /= 2;
height /= 2;
}
textureType = DDS_TEXTURE;
return true;
In the fragment shader I just set the gl_FragColor = texture2D( myTextureSampler, UVcoords )
I hope that there is an easy explanation such as some code missing.
In the openGL initialization i glEnabled GL_Blend and set a blend function.
Does anyone have an idea of what I did wrong?
Make sure the blend function is the correct function for what you are trying to accomplish. For what you've described that should be glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
You probably shouldn't set the blend function in your openGL initialization function but should wrap it around your draw calls like:
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
//gl draw functions (glDrawArrays,glDrawElements,etc..)
glDisable(GL_BLEND)
Are you clearing the 2D texture binding before you swap buffers? i.e ...
glBindTexture(GL_TEXTURE_2D, 0);
I have an error with my source code which basically causes bitmap images to appear too wide. for example it will print the width and the height and the height is perfect (256) and the width should also be 256 but the programs says it is billions of pixels wide and it is different everytime. here is the source code.
#include "glob.h"
/* Image type - contains height, width, and data */
struct Image {
unsigned long sizeX;
unsigned long sizeY;
char *data;
};
typedef struct Image Image;
int ImageLoad(char *filename, Image *image) {
FILE *file;
unsigned long size; // size of the image in bytes.
unsigned long i; // standard counter.
unsigned short int planes; // number of planes in image (must be 1)
unsigned short int bpp; // number of bits per pixel (must be 24)
char temp; // temporary color storage for bgr-rgb conversion.
// make sure the file is there.
if ((file = fopen(filename, "rb"))==NULL){
printf("bitmap Not Found : %s\n",filename);
return 0;
}
// seek through the bmp header, up to the width/height:
fseek(file, 18, SEEK_CUR);
// read the width
if ((i = fread(&image->sizeX, 4, 1, file)) != 1) {
printf("Error reading width from %s.\n", filename);
return 0;
}
printf("Width of %s: %lu\n", filename, image->sizeX);
// read the height
if ((i = fread(&image->sizeY, 4, 1, file)) != 1) {
printf("Error reading height from %s.\n", filename);
return 0;
}
printf("Height of %s: %lu\n", filename, image->sizeY);
// calculate the size (assuming 24 bits or 3 bytes per pixel).
size = image->sizeX * image->sizeY * 3;
// read the planes
if ((fread(&planes, 2, 1, file)) != 1) {
printf("Error reading planes from %s.\n", filename);
return 0;
}
if (planes != 1) {
printf("Planes from %s is not 1: %u\n", filename, planes);
return 0;
}
// read the bpp
if ((i = fread(&bpp, 2, 1, file)) != 1) {
printf("Error reading bpp from %s.\n", filename);
return 0;
}
if (bpp != 24) {
printf("Bpp from %s is not 24: %u\n", filename, bpp);
return 0;
}
// seek past the rest of the bitmap header.
fseek(file, 24, SEEK_CUR);
// read the data.
image->data = (char *) malloc(size);
if (image->data == NULL) {
printf("Error allocating memory for color-corrected image data\n");
return 0;
}
if ((i = fread(image->data, size, 1, file)) != 1) {
printf("Error reading image data from %s.\n", filename);
return 0;
}
for (i=0; i<size; i+=3) { // reverse all of the colors. (bgr -> rgb)
temp = image->data[i];
image->data[i] = image->data[i+2];
image->data[i+2] = temp;
}
// we're done.
return 0;
}
// Load Bitmaps And Convert To Textures
void glob::LoadGLTextures() {
// Load Texture
Image *image1;
// allocate space for texture
image1 = (Image *) malloc(sizeof(Image));
if (image1 == NULL) {
printf("(image1 == NULL)\n");
exit(0);
}
ImageLoad("data/textures/NeHe.bmp", image1);
// Create Texture
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture); // 2d texture (x and y size)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // scale linearly when image bigger than texture
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // scale linearly when image smalled than texture
// 2d texture, level of detail 0 (normal), 3 components (red, green, blue), x size from image, y size from image,
// border 0 (normal), rgb color data, unsigned byte data, and finally the data itself.
glTexImage2D(GL_TEXTURE_2D, 0, 3, image1->sizeX, image1->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
};
glob.h is this:
#ifndef GLOB_H_INCLUDED
#define GLOB_H_INCLUDED
#include <iostream>
#include <stdlib.h>
#include <stdio.h> // Header file for standard file i/o.
#include <GL/glx.h> /* this includes the necessary X headers */
#include <GL/gl.h>
//#include <GL/glut.h> // Header File For The GLUT Library
//#include <GL/glu.h> // Header File For The GLu32 Library
#include <X11/X.h> /* X11 constant (e.g. TrueColor) */
#include <X11/keysym.h>
class glob {
bool Running;
GLuint texture; //make an array when we start using more then 1
Display *dpy;
Window win;
XEvent event;
GLboolean doubleBuffer;
GLboolean needRedraw;
GLfloat xAngle, yAngle, zAngle;
float camera_x, camera_y, camera_z;
public:
glob();
int OnExecute();
public:
int init(int argc, char **argv);
void LoadGLTextures();
void OnEvent();
void redraw(void);
};
#endif // GLOB_H_INCLUDED
can any body help me fix this problem?
Lots of things could be going wrong.
If it's a very old file, it could have a BITMAPCOREHEADER which has size fields that are only 2 bytes each.
Is your machine little endian? BMP files are stored little endian.
Note that height may be negative, (which implies it's a top-down bitmap instead of a bottom up one). If you interpret a small negative number as an unsigned 32-bit int, you'll see values in the billions.
Also, your seek to the actual pixel data assumes that it starts right after the bitmap header. This is common, but not required. The file header contains the offset of the actual pixel data. (Microsoft documentation calls this the "bitmap bits" or the "color data".)
I recommend doing a hex dump of the beginning of your file and step through it by hand to make sure all your offsets and assumptions are correct. Feel free to paste the beginning of a hex dump into your question.
Are you on Windows? Can you just call LoadImage?
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)
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.
It sounds like your file isn't importing the UIKit header.
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;