ofImage: trimWhiteSpace()

Hey everyone,

positioning graphics (like buttons and so on) is sometimes quite a hassle. In order to optimize memory usage, you would make the button graphics as small as possible. But to draw them on the correct position, you’d have to manually find out what the correct coordinates are.

Over the years I have become used to do things a bit differently. I ask the designer to give me a transparent PNG image the size of the whole window (e.g. 1280x1024). Most of the image is transparent, except for the button. I would then cut away all transparent pixels and adjust the anchor point accordingly. Now I have a small image in memory, but I can still draw it on (0, 0).

OpenFrameworks did not yet have this functionality, so I decided to attempt to write it myself. Here is the code for the function. Note that it is far from optimized, might contain memory leaks and will probably not work correctly if colors are arranged as BGR. Still, I will post it here so you can shoot at it and I can learn from that.

  
  
//declaration in ofImage.h:  
void trimWhiteSpace(int r=255, int g=255, int b=255);  
  
//implementation in ofImage.cpp  
void ofImage::trimWhiteSpace(int r, int g, int b){  
    // trims (white) pixels from 24 bit images, or transparent pixels from 32 bit images  
    if(!myPixels.bAllocated) return;  
  
    //float t = ofGetElapsedTimeMillis();  
  
    bool test = false;  
    int trim[4] = {myPixels.width, myPixels.height, 0, 0};  
  
    // find trimming rectangle  
    for(int ih=0;ih<myPixels.height;ih++){  
        for(int iw=0;iw<myPixels.width;iw++){  
            int i = ((ih * myPixels.width) + iw) * myPixels.bytesPerPixel;  
            switch(myPixels.ofImageType){  
                case OF_IMAGE_COLOR:  
                    test = (myPixels.pixels[i] != r || myPixels.pixels[i+1] != g || myPixels.pixels[i+2] != b);  
                    break;  
                case OF_IMAGE_COLOR_ALPHA:  
                    test = (myPixels.pixels[i+3] > 0);  
                    break;  
                case OF_IMAGE_GRAYSCALE:  
                    test = (myPixels.pixels[i] < 255);  
                    break;  
            }  
  
            if(test){  
                trim[0] = min(trim[0], iw);  
                trim[1] = min(trim[1], ih);  
                trim[2] = max(trim[2], iw);  
                trim[3] = max(trim[3], ih);  
            }  
        }  
    }  
  
    // trim image  
    int tw = trim[2]-trim[0]+1;  
    int th = trim[3]-trim[1]+1;  
    // TODO: can this memcpy-style code be sped up somehow?  
    unsigned char * trimmed = new unsigned char[tw*th*myPixels.bytesPerPixel];  
    for(int ih=0;ih<th;ih++){  
        for(int iw=0;iw<tw;iw++){  
            int from = ((ih+trim[1])*myPixels.width + (iw+trim[0])) * myPixels.bytesPerPixel;  
            int to = (ih*tw+iw) * myPixels.bytesPerPixel;  
            for(int i=0;i<myPixels.bytesPerPixel; i++){  
                trimmed[to + i] = myPixels.pixels[from + i];  
            }  
        }  
    }  
    setFromPixels(trimmed, tw, th, myPixels.ofImageType, true);  
    delete trimmed;  
  
    // adjust anchor  
    if(bUseTexture){  
        //I added this function to retrieve the current anchor point from the texture,  
        //but you could also just ignore this and use (0, 0) instead of 'anchor'  
        ofPoint anchor = tex.getAnchorPoint();  
        setAnchorPoint(anchor.x-trim[0], anchor.y-trim[1]);  
    }  
  
    //printf("trimWhiteSpace(): trimming took %i ms\n", (int)(ofGetElapsedTimeMillis() - t));  
}  
  

I welcome any comments on this, because I know it is far from perfect. And I hope you find it useful.

Paul