Here are the beginnings of a very short a brief image manipulation ... err ... file. And it does not just an entire image, but also can do mere ranges.
Notice how short it is. And it uses inlining to keep things separated but clear and readable.
PIXOP_ALPHA_FROM_COLOR generates an alpha channel for an image based on the existing color.
Code: Select all
typedef enum
{
PIXOP_NEGATIVE_IMAGE,
PIXOP_GREYSCALE,
PIXOP_SWAP_RED_GREEN,
PIXOP_SWAP_GREEN_BLUE,
PIXOP_SWAP_BLUE_RED,
PIXOP_ALPHA_FROM_COLOR,
PIXOP_REMOVE_RED,
PIXOP_REMOVE_GREEN,
PIXOP_REMOVE_BLUE,
} pixelop;Code: Select all
static inline byte Pix_Average_RGB (byte* red, byte* green, byte *blue)
{
unsigned average = ((unsigned)*red + *green + *blue) / 3; // Average the colors
average = CLAMP (0, average, 255); // Clamp to range just in case. Shouldn't happen?
return average;
}
static inline unsigned Pixel_From_Components (byte* red, byte* green, byte *blue, byte* alpha)
{
return ((unsigned)*red + ((unsigned)*green << 8) + ((unsigned)*blue << 16) + ((unsigned)*alpha << 24));
}
static inline void Pixel_Component_Swap (byte* component1, byte* component2)
{
byte extra = *component1;
*component1 = *component2;
*component2 = extra;
}
static inline void Pixel_Perform_Operation (const int* pixelOperation, unsigned* myPixel)
{
byte red = (*myPixel ) & 0xFF;
byte green = (*myPixel >> 8 ) & 0xFF;
byte blue = (*myPixel >> 16) & 0xFF;
byte alpha = (*myPixel >> 24) & 0xFF;
switch (*pixelOperation)
{
case PIXOP_GREYSCALE: red = green = blue = Pix_Average_RGB (&red, &green, &blue); break;
case PIXOP_NEGATIVE_IMAGE: red = ~red; green = ~green; blue = ~ blue; /* ~ is bitwise NOT */ break;
case PIXOP_SWAP_RED_GREEN: Pixel_Component_Swap (&red, &green); break;
case PIXOP_SWAP_GREEN_BLUE: Pixel_Component_Swap (&green, &blue); break;
case PIXOP_SWAP_BLUE_RED: Pixel_Component_Swap (&blue, &red); break;
case PIXOP_ALPHA_FROM_COLOR: alpha = Pix_Average_RGB (&red, &green, &blue); break;
case PIXOP_REMOVE_RED: red = 0; break;
case PIXOP_REMOVE_GREEN: green = 0; break;
case PIXOP_REMOVE_BLUE: blue = 0; break;
default: break;
}
*myPixel = Pixel_From_Components (&red, &green, &blue, &alpha);
}
byte* Image_RGBA_Modify_Range (const int pixelOperation, const int x1, const int y1, const int x2, const int y2, byte *sourceDest, const int sourceDestSize, const int sourceDestWidth, const int sourceDestHeight)
{
const int pixelcount = sourceDestWidth * sourceDestHeight;
const int bitsPerPixel = sourceDestSize / pixelcount; // Choke if not RGBA?
unsigned* buffer = (unsigned*)sourceDest;
for (int row = y1; row <= y2; row ++)
for (int col = x1; col <= x2; col ++)
{
unsigned* thisPixel = buffer + (row * sourceDestWidth + col); // unsigned* thisPixel = &buffer[row * sourceDestWidth + col];
Pixel_Perform_Operation (&pixelOperation, thisPixel);
}
return sourceDest;
}
byte* Image_RGBA_Modify (const int pixelOperation, byte *sourceDest, const int sourceDestSize, const int sourceDestWidth, const int sourceDestHeight)
{
const int pixelcount = sourceDestWidth * sourceDestHeight;
const int bitsPerPixel = sourceDestSize / pixelcount; // Choke if not RGBA?
unsigned* thisPixel = (unsigned*) sourceDest;
for (int i = 0 ; i < pixelcount ; i++, thisPixel++)
Pixel_Perform_Operation (&pixelOperation, thisPixel);
return sourceDest;
}
The origin of fair of this kind of goes like this. I wanted to load the above charset into my "non-Quake" engine (or whatever it is) project.
But the above image does not have an alpha mask. I spent maybe 45 minutes trying to figure out how to make an alpha channel in Gimp (I did it once for sure) off the existing image. Could not get it to work.
I already had a bit of an "image library" going and found myself repeating the bulk of a body of functions over and over. I'm like "this is crazy".
Went with inlining and passing the vars as pointers, which I figured would make the inlining more natural.
Why did I want to load that pic? ---------> Next destination ---> OpenGL simple ascii text editor.
(Yes I have enlarge canvas, vertical flip and such already written. Need to determine how to do resample the "right way" .. where the right way may involve treating edges as a special case. I saw some of that in DirectQ.]
