RE: code 4 cropping & copying subrectangle of PNG image

Started by
8 comments, last by gbadebo 17 years, 9 months ago
i have to crop and copy subrectangles of hundreds of PNG files within seconds, so i can't do it manually by using some software like photoshop, instead i have to do it using raw C++ and Opengl code by supplying the start coordinates of the subrectangles (x,y) plus height and width to the code. But during my research into doing this, i found that chapter 8 of the 'OpenGL programming guide' to be too generic and not too helpful. ie doesn't say how to covert PNG to binary or generic format, performming the operations, and then converting back to PNG format. And it also doesn't include real-hands-on-test examples that can enhance understanding. Has anyone got ideas on how to do these and good test examples (with understandable code) please? many thanks [Edited by - gbadebo on July 13, 2006 12:18:05 PM]
Advertisement
You don't need OpenGL for this. Use libpng to handle the png loading and saving and the rest is just going to be copying bytes around. For example, a crop would be just copying pixels from one buffer to a smaller buffer using for loops with some offsets. Something like:

for(x = minX; x < dstWidth + minX; x++) {     for(y = minY; y < dstHeight + minY; Y++) {         srcOffset = y * srcWidth + x;         dstOffset = (y - minY) * dstWidth + (x - minX);         // assume dword copy between 32bit buffers, use per channel for 24 bit         dst[dstOffset] = src[srcOffset];      }}


Subrectangle blts are the same as crops, but not to a smaller buffer.

Quote:
so i can't do it manually by using some software like photoshop


If your only objection is the manual part, take a look at ImageMagik.
A way you could do it through OpenGL if you already have the texture loaded is through the glCopySubImage2D() function.

Note that the filtering in the PNG format means you might have to load every row/column before the one you want - you can't guarantee that you'll be able to load rows 4 - 17, or columns 15-40.
has any one got answers to:
i have loaded the png textures loaded but how do i create ( as in the example above; dst[dstOffset] = src[srcOffset] ) these buffers that would represent png files?

and how do i use glCopySubImage2D() to read png files

[Edited by - gbadebo on July 12, 2006 10:03:39 AM]
Just to clarify, something like this is your final goal right?

load a png.
crop the image in the png.
save the result to a png.

Quote:Original post by gbadebo
has any one got answers to:
i have loaded the png textures loaded but how do i create ( as in the example above; dst[dstOffset] = src[srcOffset] ) these buffers that would represent png files?


The buffers are just regions of memory, you would allocate them with new or malloc. To load the png file you'll want to use libpng or a similar image library. I believe devil and sdl image will also handle pngs, but I just use libpng myself. For more details on loading png files, go to the libpng website. They have tutorials there and sample code showing how to use the library.

Opengl (and others) usually represent an image as a single contigious block of memory of size width * height. Rows of pixels are stored end to end in the buffer. Libpng unfortunatly wants to store image data differently. It wants to store each row as its own contiguous buffer and have an array of pointers to those rows. If you allocate the memory yourself and pass it to libpng, you can still set up the rows back to back and then create an array of pointers to the rows to pass to libpng. Or you can adapt the pseudocode I gave slightly to support seperate rows.

So the general approach is:

1. Allocate width * height * sizeof(pixel) memory for the source buffer
2. Allocate destwidth * destheight * sizeof(pixel) memory for the destination buffer
3. Use libpng (or other image lib) to load the png file data into source.
4. Copy each pixel in the cropped area from source to dest. Do this with two for loops for x and y and offset the dest coordinates so that the cropped area is copied to 0,0. You also have to account for different widths for the two arrays. If you are using 32bit images, just make the buffers arrays of dwords and copy a pixel at a time. (the pseudocode I gave above covers step 4 here.)
5. Use libpng to save the contents of dest to a new png file.

Copying subrects is the same, just adjust the target offset to position the dest rect somewhere other than 0,0 and leave the dest buffer the same size as source.


Quote:and how do i use glCopySubImage2D() to read png files

You don't. newera was suggesting this as a method for copy subrects within the image data. This function copys a portion of the frame buffer to another position in the frame buffer. You would have to already load the data from the file and have it on the screen before you could use this function. There are several ways you could force opengl to do the crop part for you, however it should be easier and faster to do it yourself. Either way, you will need to load the data form the file using a different library. OpenGL does not know anything about png files, it's only a graphics rendering library.

I hope this helps, feel free to post followup questions if you are still confused.

I'm probably going to get flamed to death for posting this in the OGL forum, but I'd use DirectX. The reason being that D3DX supports loading PNGs, and will let you load any format (No matter what format the graphics hardware supports - so you can load a 32-bit PNG even if the hardware only does 16/24 bit) if you use D3DPOOL_SCRATCH. D3DX will also let you save a surface to a file (as a PNG), and copying rectangles of a surface is pretty easy in DX too.

Having said that, I'm really not an OpenGL person, so I don't know how easy it is to do this sort of thing in OpenGL.
Quote:Original post by Evil Steve
I'm probably going to get flamed to death for posting this in the OGL forum, but I'd use DirectX. The reason being that D3DX supports loading PNGs, and will let you load any format (No matter what format the graphics hardware supports - so you can load a 32-bit PNG even if the hardware only does 16/24 bit) if you use D3DPOOL_SCRATCH. D3DX will also let you save a surface to a file (as a PNG), and copying rectangles of a surface is pretty easy in DX too.

Having said that, I'm really not an OpenGL person, so I don't know how easy it is to do this sort of thing in OpenGL.


It's really not an API question at all, it could be done entirely in libpng without even opening a window.
Quote:
baldurk
It's really not an API question at all, it could be done entirely in libpng without even opening a window.


I agree.
Quote:
post by solias
1. Allocate width * height * sizeof(pixel) memory for the source buffer
2. Allocate destwidth * destheight * sizeof(pixel) memory for the destination buffer
3. Use libpng (or other image lib) to load the png file data into source.
4. Copy each pixel in the cropped area from source to dest. Do this with two for loops for x and y and offset the dest coordinates so that the cropped area is copied to 0,0.....


the problem is the original ligpng loader doesn't allow for pixel level acess. take a look at this extract from my code. You will see that the code comented out and astericked(*) cannot be done because of lack of pixel access. But if u 've got a better method please lets know
NOTE: PLEASE BEAR WITH ME AS THE TEXT EDITOR REMOVES THE INDENTATION SO THE CODE APPEARS LESS READABLE!!!

// header file
class texture
{
public:
GLuint *ImageTexture, *TextureNumber, *croppedTest;
texture();
~texture();
void mainLoader();
GLuint* getTexure(int texid, char type);
};

// cpp file
#include "texture.h"
#include "glpng.h"
#include <GL/glut.h>
#include <stdlib.h>


texture::texture()
{
ImageTexture = new GLuint[50];
TextureNumber = new GLuint[50];


croppedTest = new GLuint[50];
}

texture::~texture()
{

}


GLuint* texture::getTexure(int texid, char type)
{

if( (texid<40) && (texid>=0) ){
if( type == 'T' ){
return &ImageTexture[texid];
}
else if( type == 'N' ){
return &TextureNumber[texid];
}
}
else {
//**************************************************************************
//** for( int i=100; i<300; i++ ){
//** for( int j=100; j<300; j++ ){
//**
//** croppedTest[texid][j*200 + i] = ImageTexture[texid][j*300 + i];
//** }
//** }
//*********************************************************************************
return &ImageTexture[7];
}
}


void texture::mainLoader() {
.
.
.
.
}
#endif

[Edited by - gbadebo on July 13, 2006 12:40:01 PM]

This topic is closed to new replies.

Advertisement