Image Manipulation
Moderator: InsideQC Admins
39 posts
• Page 1 of 3 • 1, 2, 3
Image Manipulation
I have somewhat unusual ideas going on in my head about a Quake engine, not a Quake engine, map editor, image editor, model viewer and text editor all being the same thing.
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.

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.
[This is why I am disappointed with Gimp as free software ... it can be a pain to figure out how to do something, even with Google.]
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.]
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.]
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Image Manipulation
And this was easier than Layer->Transparency->Add Alpha Channel ?!
I like PIXOP_ALPHA_FROM_COLOR. It represents many alpha conditions. It could replace large alpha tgas with smaller jpegs. Could add attenuation factor, where alpha is proportional to average squared for example. Also, PIXOP_ALPHA_FROM_RGB where a specific color becomes alpha.
I like PIXOP_ALPHA_FROM_COLOR. It represents many alpha conditions. It could replace large alpha tgas with smaller jpegs. Could add attenuation factor, where alpha is proportional to average squared for example. Also, PIXOP_ALPHA_FROM_RGB where a specific color becomes alpha.
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: Image Manipulation
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
then you don't need an alpha channel.
then you don't need an alpha channel.
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
Re: Image Manipulation
qbism wrote:And this was easier than Layer->Transparency->Add Alpha Channel ?!![]()
Easier than spending 30 minutes each and every time I try to do something unobvious with it, yes.
Spike wrote:glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
then you don't need an alpha channel.
Well, sure for that particular image
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Image Manipulation
qbism wrote:And this was easier than Layer->Transparency->Add Alpha Channel ?!
That merely adds an alpha channel to the image, it does not convert black pixels into transparent pixels while keeping white pixels solid.
The easiest way might be to use Colors -> Components -> Decompose, which creates a new image and on that image do Colors -> Components -> Compose and select RGBA as the color model.
- andrewj
- Posts: 133
- Joined: Mon Aug 30, 2010 3:29 pm
- Location: Australia
Re: Image Manipulation
Most of the time I'd just do this kind of thing in a shader. GPUs are better at this work and the image won't need preprocessing.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
We knew the words, we knew the score, we knew what we were fighting for
-

mh - Posts: 2292
- Joined: Sat Jan 12, 2008 1:38 am
Re: Image Manipulation
I think Gimp has a color to alpha filter as well.
it does
Productivity is a state of mind.
-

revelator - Posts: 2567
- Joined: Thu Jan 24, 2008 12:04 pm
- Location: inside tha debugger
Re: Image Manipulation
Yes, under colors menu.goldenboy wrote:I think Gimp has a color to alpha filter as well.
Even so, could be beneficial to winquake engines.mh wrote:Most of the time I'd just do this kind of thing in a shader. GPUs are better at this work and the image won't need preprocessing.
-
qbism - Posts: 1236
- Joined: Thu Nov 04, 2004 5:51 am
Re: Image Manipulation
qbism wrote:Even so, could be beneficial to winquake engines.
The current incarnation of this code is for RGBA and not for paletted images. So only an engine like FTEQW's past-life software renderer would benefit.
The way things are at the moment. Needless to say I care about 256 color paletted images for reasons too obvious to explain further ...
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Image Manipulation
- Code: Select all
byte quakePalette [] =
{
0, 0, 0,
15, 15, 15,
31, 31, 31,
47, 47, 47,
63, 63, 63,
75, 75, 75,
91, 91, 91,
107, 107, 107,
123, 123, 123,
139, 139, 139,
155, 155, 155,
171, 171, 171,
187, 187, 187,
203, 203, 203,
219, 219, 219,
235, 235, 235,
15, 11, 7,
23, 15, 11,
31, 23, 11,
39, 27, 15,
47, 35, 19,
55, 43, 23,
63, 47, 23,
75, 55, 27,
83, 59, 27,
91, 67, 31,
99, 75, 31,
107, 83, 31,
115, 87, 31,
123, 95, 35,
131, 103, 35,
143, 111, 35,
11, 11, 15,
19, 19, 27,
27, 27, 39,
39, 39, 51,
47, 47, 63,
55, 55, 75,
63, 63, 87,
71, 71, 103,
79, 79, 115,
91, 91, 127,
99, 99, 139,
107, 107, 151,
115, 115, 163,
123, 123, 175,
131, 131, 187,
139, 139, 203,
0, 0, 0,
7, 7, 0,
11, 11, 0,
19, 19, 0,
27, 27, 0,
35, 35, 0,
43, 43, 7,
47, 47, 7,
55, 55, 7,
63, 63, 7,
71, 71, 7,
75, 75, 11,
83, 83, 11,
91, 91, 11,
99, 99, 11,
107, 107, 15,
7, 0, 0,
15, 0, 0,
23, 0, 0,
31, 0, 0,
39, 0, 0,
47, 0, 0,
55, 0, 0,
63, 0, 0,
71, 0, 0,
79, 0, 0,
87, 0, 0,
95, 0, 0,
103, 0, 0,
111, 0, 0,
119, 0, 0,
127, 0, 0,
19, 19, 0,
27, 27, 0,
35, 35, 0,
47, 43, 0,
55, 47, 0,
67, 55, 0,
75, 59, 7,
87, 67, 7,
95, 71, 7,
107, 75, 11,
119, 83, 15,
131, 87, 19,
139, 91, 19,
151, 95, 27,
163, 99, 31,
175, 103, 35,
35, 19, 7,
47, 23, 11,
59, 31, 15,
75, 35, 19,
87, 43, 23,
99, 47, 31,
115, 55, 35,
127, 59, 43,
143, 67, 51,
159, 79, 51,
175, 99, 47,
191, 119, 47,
207, 143, 43,
223, 171, 39,
239, 203, 31,
255, 243, 27,
11, 7, 0,
27, 19, 0,
43, 35, 15,
55, 43, 19,
71, 51, 27,
83, 55, 35,
99, 63, 43,
111, 71, 51,
127, 83, 63,
139, 95, 71,
155, 107, 83,
167, 123, 95,
183, 135, 107,
195, 147, 123,
211, 163, 139,
227, 179, 151,
171, 139, 163,
159, 127, 151,
147, 115, 135,
139, 103, 123,
127, 91, 111,
119, 83, 99,
107, 75, 87,
95, 63, 75,
87, 55, 67,
75, 47, 55,
67, 39, 47,
55, 31, 35,
43, 23, 27,
35, 19, 19,
23, 11, 11,
15, 7, 7,
187, 115, 159,
175, 107, 143,
163, 95, 131,
151, 87, 119,
139, 79, 107,
127, 75, 95,
115, 67, 83,
107, 59, 75,
95, 51, 63,
83, 43, 55,
71, 35, 43,
59, 31, 35,
47, 23, 27,
35, 19, 19,
23, 11, 11,
15, 7, 7,
219, 195, 187,
203, 179, 167,
191, 163, 155,
175, 151, 139,
163, 135, 123,
151, 123, 111,
135, 111, 95,
123, 99, 83,
107, 87, 71,
95, 75, 59,
83, 63, 51,
67, 51, 39,
55, 43, 31,
39, 31, 23,
27, 19, 15,
15, 11, 7,
111, 131, 123,
103, 123, 111,
95, 115, 103,
87, 107, 95,
79, 99, 87,
71, 91, 79,
63, 83, 71,
55, 75, 63,
47, 67, 55,
43, 59, 47,
35, 51, 39,
31, 43, 31,
23, 35, 23,
15, 27, 19,
11, 19, 11,
7, 11, 7,
255, 243, 27,
239, 223, 23,
219, 203, 19,
203, 183, 15,
187, 167, 15,
171, 151, 11,
155, 131, 7,
139, 115, 7,
123, 99, 7,
107, 83, 0,
91, 71, 0,
75, 55, 0,
59, 43, 0,
43, 31, 0,
27, 15, 0,
11, 7, 0,
0, 0, 255,
11, 11, 239,
19, 19, 223,
27, 27, 207,
35, 35, 191,
43, 43, 175,
47, 47, 159,
47, 47, 143,
47, 47, 127,
47, 47, 111,
47, 47, 95,
43, 43, 79,
35, 35, 63,
27, 27, 47,
19, 19, 31,
11, 11, 15,
43, 0, 0,
59, 0, 0,
75, 7, 0,
95, 7, 0,
111, 15, 0,
127, 23, 7,
147, 31, 7,
163, 39, 11,
183, 51, 15,
195, 75, 27,
207, 99, 43,
219, 127, 59,
227, 151, 79,
231, 171, 95,
239, 191, 119,
247, 211, 139,
167, 123, 59,
183, 155, 55,
199, 195, 55,
231, 227, 87,
127, 191, 255,
171, 231, 255,
215, 255, 255,
103, 0, 0,
139, 0, 0,
179, 0, 0,
215, 0, 0,
255, 0, 0,
255, 243, 147,
255, 247, 199,
255, 255, 255,
159, 91, 83
}
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Image Manipulation
Raw materials for planning: After looking over a few different ways of palette reduction, adapting Qwalk's (Sajt's model converter) way seems easiest:
Edit ... uh ... doesn't Super8 have something like this built-in??? So much engine stuff happening so fast ... if so, do not reinvent wheel ...
Edit ... uh ... doesn't Super8 have something like this built-in??? So much engine stuff happening so fast ... if so, do not reinvent wheel ...
- Code: Select all
/*
QShed <http://www.icculus.org/qshed>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include "global.h"
#include "image.h"
typedef enum image_format_e
{
IMGFMT_MISSING,
IMGFMT_UNRECOGNIZED,
IMGFMT_PCX,
IMGFMT_TGA,
IMGFMT_JPG
} image_format_t;
static image_format_t get_image_format(const char *filename)
{
const char *ext = strrchr(filename, '.');
if (!ext)
return IMGFMT_MISSING;
if (!strcasecmp(ext, ".pcx"))
return IMGFMT_PCX;
if (!strcasecmp(ext, ".tga"))
return IMGFMT_TGA;
if (!strcasecmp(ext, ".jpg") || !strcasecmp(ext, ".jpeg"))
return IMGFMT_JPG;
return IMGFMT_UNRECOGNIZED;
}
image_rgba_t *image_load(mem_pool_t *pool, const char *filename, void *filedata, size_t filesize, char **out_error)
{
switch (get_image_format(filename))
{
default:
case IMGFMT_MISSING:
return (void)(out_error && (*out_error = msprintf("missing file extension"))), NULL;
case IMGFMT_UNRECOGNIZED:
return (void)(out_error && (*out_error = msprintf("unrecognized file extension"))), NULL;
case IMGFMT_PCX: return image_pcx_load(pool, filedata, filesize, out_error);
case IMGFMT_TGA: return image_tga_load(pool, filedata, filesize, out_error);
case IMGFMT_JPG: return image_jpg_load(pool, filedata, filesize, out_error);
}
}
image_rgba_t *image_load_from_file(mem_pool_t *pool, const char *filename, char **out_error)
{
void *filedata;
size_t filesize;
image_rgba_t *image;
if (!loadfile(filename, &filedata, &filesize, out_error))
return NULL;
image = image_load(pool, filename, filedata, filesize, out_error);
qfree(filedata);
return image;
}
bool_t image_save(const char *filename, const image_rgba_t *image, char **out_error)
{
bool_t (*savefunc)(const image_rgba_t *image, xbuf_t *xbuf, char **out_error) = NULL;
xbuf_t *xbuf;
switch (get_image_format(filename))
{
default:
case IMGFMT_MISSING:
return (void)(out_error && (*out_error = msprintf("missing file extension"))), false;
case IMGFMT_UNRECOGNIZED:
return (void)(out_error && (*out_error = msprintf("unrecognized file extension"))), false;
case IMGFMT_PCX:
return (void)(out_error && (*out_error = msprintf("cannot save 32-bit image to PCX"))), false;
case IMGFMT_TGA:
savefunc = image_tga_save;
break;
case IMGFMT_JPG:
return (void)(out_error && (*out_error = msprintf("jpeg saving not implemented"))), false; /* FIXME - so implement it! */
}
/* allocate write buffer and set it up to flush directly to the file */
xbuf = xbuf_create_file(262144, filename, out_error);
if (!xbuf)
return false;
/* write image data */
if (!(*savefunc)(image, xbuf, out_error))
{
xbuf_free(xbuf, NULL);
return false;
}
/* flush any remaining data to file and free the buffer */
return xbuf_finish_file(xbuf, out_error);
}
bool_t image_paletted_save(const char *filename, const image_paletted_t *image, char **out_error)
{
bool_t (*savefunc)(const image_paletted_t *image, xbuf_t *xbuf, char **out_error) = NULL;
xbuf_t *xbuf;
switch (get_image_format(filename))
{
default:
case IMGFMT_MISSING:
return (void)(out_error && (*out_error = msprintf("missing file extension"))), false;
case IMGFMT_UNRECOGNIZED:
return (void)(out_error && (*out_error = msprintf("unrecognized file extension"))), false;
case IMGFMT_PCX:
savefunc = image_pcx_save;
break;
case IMGFMT_TGA:
return (void)(out_error && (*out_error = msprintf("cannot save 8-bit image to TGA"))), false;
case IMGFMT_JPG:
return (void)(out_error && (*out_error = msprintf("cannot save 8-bit image to JPEG"))), false;
}
/* allocate write buffer and set it up to flush directly to the file */
xbuf = xbuf_create_file(262144, filename, out_error);
if (!xbuf)
return false;
/* write image data */
if (!(*savefunc)(image, xbuf, out_error))
{
xbuf_free(xbuf, NULL);
return false;
}
/* flush any remaining data to file and free the buffer */
return xbuf_finish_file(xbuf, out_error);
}
image_rgba_t *image_alloc(mem_pool_t *pool, int width, int height)
{
image_rgba_t *image;
if (width < 1 || height < 1)
return NULL;
image = (image_rgba_t*)mem_alloc(pool, sizeof(image_rgba_t) + width * height * 4);
if (!image)
return NULL;
image->width = width;
image->height = height;
image->pixels = (unsigned char*)(image + 1);
return image;
}
void image_free(image_rgba_t **image)
{
mem_free(*image);
*image = NULL;
}
image_paletted_t *image_paletted_alloc(mem_pool_t *pool, int width, int height)
{
image_paletted_t *image;
if (width < 1 || height < 1)
return NULL;
image = (image_paletted_t*)mem_alloc(pool, sizeof(image_paletted_t) + width * height);
if (!image)
return NULL;
image->width = width;
image->height = height;
image->pixels = (unsigned char*)(image + 1);
return image;
}
void image_paletted_free(image_paletted_t **image)
{
mem_free(*image);
*image = NULL;
}
image_rgba_t *image_createfill(mem_pool_t *pool, int width, int height, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
{
image_rgba_t *image;
int i;
image = image_alloc(pool, width, height);
if (!image)
return NULL;
for (i = 0; i < width * height; i++)
{
image->pixels[i*4+0] = r;
image->pixels[i*4+1] = g;
image->pixels[i*4+2] = b;
image->pixels[i*4+3] = a;
}
return image;
}
image_rgba_t *image_clone(mem_pool_t *pool, const image_rgba_t *source)
{
image_rgba_t *image;
if (!source)
return NULL;
image = image_alloc(pool, source->width, source->height);
if (!image)
return NULL;
memcpy(image->pixels, source->pixels, source->width * source->height * 4);
return image;
}
static unsigned char palettize_colour(const palette_t *palette, bool_t fullbright, const unsigned char rgb[3])
{
int i, dist;
int besti = -1, bestdist = 0;
for (i = 0; i < 256; i++)
{
if (fullbright != !!(palette->fullbright_flags[i >> 5] & (1U << (i & 31))))
continue;
dist = 299 * abs(palette->rgb[i*3+0] - rgb[0]) + 587 * abs(palette->rgb[i*3+1] - rgb[1]) + 114 * abs(palette->rgb[i*3+2] - rgb[2]);
if (besti == -1 || dist < bestdist)
{
besti = i;
bestdist = dist;
}
}
return (besti != -1) ? besti : 0;
}
image_paletted_t *image_palettize(mem_pool_t *pool, const palette_t *palette, const image_rgba_t *source_diffuse, const image_rgba_t *source_fullbright)
{
bool_t palette_has_fullbrights;
image_paletted_t *pimage;
int i;
if (!source_diffuse && !source_fullbright)
return NULL;
pimage = (image_paletted_t*)mem_alloc(pool, sizeof(image_paletted_t) + source_diffuse->width * source_diffuse->height);
if (!pimage)
return NULL;
pimage->width = source_diffuse->width;
pimage->height = source_diffuse->height;
pimage->pixels = (unsigned char*)(pimage + 1);
pimage->palette = *palette;
palette_has_fullbrights = false;
for (i = 0; i < 8; i++)
if (palette->fullbright_flags[i])
palette_has_fullbrights = true;
if (source_diffuse && source_fullbright)
{
const unsigned char *in_diffuse = source_diffuse->pixels;
const unsigned char *in_fullbright = source_fullbright->pixels;
unsigned char *out = pimage->pixels;
for (i = 0; i < pimage->width * pimage->height; i++, in_diffuse += 4, in_fullbright += 4, out++)
{
if (in_fullbright[0] || in_fullbright[1] || in_fullbright[2])
*out = palettize_colour(palette, palette_has_fullbrights, in_fullbright);
else
*out = palettize_colour(palette, false, in_diffuse);
}
}
else if (source_diffuse)
{
const unsigned char *in_diffuse = source_diffuse->pixels;
unsigned char *out = pimage->pixels;
for (i = 0; i < pimage->width * pimage->height; i++, in_diffuse += 4, out++)
*out = palettize_colour(palette, false, in_diffuse);
}
else if (source_fullbright)
{
const unsigned char *in_fullbright = source_fullbright->pixels;
unsigned char *out = pimage->pixels;
for (i = 0; i < pimage->width * pimage->height; i++, in_fullbright += 4, out++)
*out = palettize_colour(palette, palette_has_fullbrights, in_fullbright);
}
return pimage;
}
/* FIXME - rewrite the minification code... it's ugly, probably slow, and maybe also has some weighting issues */
image_rgba_t *image_resize(mem_pool_t *pool, const image_rgba_t *source, int newwidth, int newheight)
{
image_rgba_t *intermediate;
image_rgba_t *image;
int x, y, i;
intermediate = image_alloc(pool, newwidth, source->height);
if (!intermediate)
return NULL;
image = image_alloc(pool, newwidth, newheight);
if (!image)
{
image_free(&intermediate);
return NULL;
}
/* horizontal resize */
if (newwidth > source->width)
{
for (x = 0; x < newwidth; x++)
{
float fx = (float)x * (float)source->width / (float)newwidth;
int x1 = (int)fx;
int x2 = min(x1 + 1, source->width - 1);
float fx2 = fx - (float)x1;
float fx1 = 1.0f - fx2;
int ifx1 = (int)(fx1 * 256.0f);
int ifx2 = (int)(fx2 * 256.0f);
unsigned char *out = intermediate->pixels + x * 4;
const unsigned char *in1 = source->pixels + x1 * 4;
const unsigned char *in2 = source->pixels + x2 * 4;
for (y = 0; y < source->height; y++, out += newwidth * 4, in1 += source->width * 4, in2 += source->width * 4)
for (i = 0; i < 4; i++)
out[i] = (in1[i] * ifx1 + in2[i] * ifx2 + 127) >> 8;
}
}
else if (newwidth < source->width)
{
for (y = 0; y < source->height; y++)
for (x = 0; x < newwidth; x++)
{
const unsigned char *in = source->pixels + y * source->width * 4;
float colour[4], count;
float fxx = (x * source->width) / (float)newwidth;
int x1 = ((x - 1) * source->width + newwidth / 2) / newwidth;
int x2 = ((x + 1) * source->width + newwidth / 2) / newwidth;
x1 = max(x1, 0);
x2 = min(x2, source->width - 1);
colour[0] = colour[1] = colour[2] = colour[3] = 0;
count = 0;
for (i = x1; i <= x2; i++)
{
float dist;
if (i < fxx)
dist = (fxx - x1) - (fxx - i);
else if (i > fxx)
dist = (x2 - fxx) - (i - fxx);
else
dist = max(x2 - fxx, fxx - x1);
dist *= dist; /* square it to increase sharpness a little */
colour[0] += dist * in[i*4+0];
colour[1] += dist * in[i*4+1];
colour[2] += dist * in[i*4+2];
colour[3] += dist * in[i*4+3];
count += dist;
}
if (!count)
count = 1; /* i don't THINK this should happen... */
for (i = 0; i < 4; i++)
intermediate->pixels[(y*newwidth+x)*4+i] = (unsigned char)(colour[i] / count);
}
}
else
{
memcpy(intermediate->pixels, source->pixels, source->width * source->height * 4);
}
/* vertical resize */
if (newheight > source->height)
{
for (y = 0; y < newheight; y++)
{
float fy = (float)y * (float)source->height / (float)newheight;
int y1 = (int)fy;
int y2 = min(y1 + 1, source->height - 1);
float fy2 = fy - (float)y1;
float fy1 = 1.0f - fy2;
int ify1 = (int)(fy1 * 256.0f);
int ify2 = (int)(fy2 * 256.0f);
unsigned char *out = image->pixels + y * newwidth * 4;
const unsigned char *in1 = intermediate->pixels + y1 * newwidth * 4;
const unsigned char *in2 = intermediate->pixels + y2 * newwidth * 4;
for (x = 0; x < newwidth; x++, out += 4, in1 += 4, in2 += 4)
for (i = 0; i < 4; i++)
out[i] = (in1[i] * ify1 + in2[i] * ify2 + 127) >> 8;
}
}
else if (newheight < source->height)
{
for (y = 0; y < newheight; y++)
for (x = 0; x < newwidth; x++)
{
float colour[4], count;
float fyy = (y * source->height) / (float)newheight;
int y1 = ((y - 1) * source->height + newheight / 2) / newheight;
int y2 = ((y + 1) * source->height + newheight / 2) / newheight;
y1 = max(y1, 0);
y2 = min(y2, source->height - 1);
colour[0] = colour[1] = colour[2] = colour[3] = 0;
count = 0;
for (i = y1; i <= y2; i++)
{
const unsigned char *in = intermediate->pixels + (i * newwidth + x) * 4;
float dist;
if (i < fyy)
dist = (fyy - y1) - (fyy - i);
else if (i > fyy)
dist = (y2 - fyy) - (i - fyy);
else
dist = max(y2 - fyy, fyy - y1);
dist *= dist; /* square it to increase sharpness a little */
colour[0] += dist * in[0];
colour[1] += dist * in[1];
colour[2] += dist * in[2];
colour[3] += dist * in[3];
count += dist;
}
if (!count)
count = 1; /* i don't THINK this should happen... */
for (i = 0; i < 4; i++)
image->pixels[(y*newwidth+x)*4+i] = (unsigned char)(colour[i] / count);
}
}
else
{
memcpy(image->pixels, intermediate->pixels, newwidth * newheight * 4);
}
image_free(&intermediate);
return image;
}
/* pad an image to a larger size. the edge pixels will be repeated instead of filled with black, to avoid any unwanted
* bleeding if mipmapped and/or rendered with texture filtering. */
image_rgba_t *image_pad(mem_pool_t *pool, const image_rgba_t *source, int width, int height)
{
image_rgba_t *image;
const unsigned char *inpixels;
unsigned char *outp;
int x, y;
if (width < source->width || height < source->height)
return NULL;
image = image_alloc(pool, width, height);
if (!image)
return NULL;
inpixels = source->pixels;
outp = image->pixels;
for (y = 0; y < source->height; y++)
{
memcpy(outp, inpixels, source->width * 4);
outp += source->width * 4;
inpixels += source->width * 4;
for (x = source->width; x < width; x++, outp += 4)
{
outp[0] = outp[-4];
outp[1] = outp[-3];
outp[2] = outp[-2];
outp[3] = outp[-1];
}
}
for (; y < height; y++)
{
memcpy(outp, outp - width * 4, width * 4);
outp += width * 4;
}
return image;
}
void image_drawpixel(image_rgba_t *image, int x, int y, unsigned char r, unsigned char g, unsigned char b)
{
unsigned char *ptr;
if (x < 0 || x >= image->width)
return;
if (y < 0 || y >= image->height)
return;
ptr = image->pixels + (y * image->width + x) * 4;
ptr[0] = r;
ptr[1] = g;
ptr[2] = b;
ptr[3] = 255;
}
void image_drawline(image_rgba_t *image, int x1, int y1, int x2, int y2, unsigned char r, unsigned char g, unsigned char b)
{
int i, dx, dy, dxabs, dyabs, sdx, sdy, px, py;
if (x1 < 0 && x2 < 0) return;
if (y1 < 0 && y2 < 0) return;
if (x1 >= image->width && x2 >= image->width) return;
if (y1 >= image->height && y2 >= image->height) return;
dx = x2 - x1;
dy = y2 - y1;
dxabs = abs(dx);
dyabs = abs(dy);
sdx = (dx < 0) ? -1 : ((dx > 0) ? 1 : 0);
sdy = (dy < 0) ? -1 : ((dy > 0) ? 1 : 0);
px = x1;
py = y1;
image_drawpixel(image, px, py, r, g, b);
if (dxabs >= dyabs) /* line is more horizontal than vertical */
{
int y = dxabs >> 1;
for (i = 0; i < dxabs; i++)
{
y += dyabs;
if (y >= dxabs)
{
y -= dxabs;
py += sdy;
}
px += sdx;
image_drawpixel(image, px, py, r, g, b);
}
}
else /* line is more vertical than horizontal */
{
int x = dyabs >> 1;
for (i = 0; i < dyabs; i++)
{
x += dxabs;
if (x >= dyabs)
{
x -= dyabs;
px += sdx;
}
py += sdy;
image_drawpixel(image, px, py, r, g, b);
}
}
}
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Image Manipulation
- Code: Select all
PIXOP_QUAKE_COLORS_NO_FULLBRIGHT,
Converts RGBA to RGBA with colors to Quake palette (the non-fullbright ones).

- Code: Select all
#include "quakepalette.h"
static void Pixel_Apply_Best_Palette_Index (byte* red, byte* green, byte *blue, byte* myPalette, int* myPaletteNumColors)
{
int bestColorIndex = -1;
int bestColorDistance = -1;
const byte* currentPaletteIndex = myPalette;
for (int i = 0; i < *myPaletteNumColors; i++, currentPaletteIndex += 3 )
{
const int redDistance = *red - currentPaletteIndex[0];
const int greenDistance = *green - currentPaletteIndex[1];
const int blueDistance = *blue - currentPaletteIndex[2];
const unsigned ColorDistance = SQUARED(redDistance) + SQUARED(greenDistance) + SQUARED(blueDistance); // Could sqrt this but no reason to do so
if (ColorDistance < bestColorDistance)
{
bestColorDistance = ColorDistance;
bestColorIndex = i;
}
}
*red = myPalette[bestColorIndex * 3 + 0];
*green = myPalette[bestColorIndex * 3 + 1];
*blue = myPalette[bestColorIndex * 3 + 2];
}
static void Pixel_Apply_Best_Quake_NoFullbright (byte* red, byte* green, byte *blue)
{
int numNoFullBrightColors = 224;
Pixel_Apply_Best_Palette_Index (red, green, blue, quakePalette, &numNoFullBrightColors);
}
quakepalette.h
- Code: Select all
/***********************************************************************************************************\
*****
**
** quakepalette.h: Quake palette
**
*****
\***********************************************************************************************************/
#ifndef _QUAKEPALETTE_H
#define _QUAKEPALETTE_H
byte quakePalette [] =
{
0, 0, 0,
15, 15, 15,
31, 31, 31,
47, 47, 47,
63, 63, 63,
75, 75, 75,
91, 91, 91,
107, 107, 107,
123, 123, 123,
139, 139, 139,
155, 155, 155,
171, 171, 171,
187, 187, 187,
203, 203, 203,
219, 219, 219,
235, 235, 235,
15, 11, 7,
23, 15, 11,
31, 23, 11,
39, 27, 15,
47, 35, 19,
55, 43, 23,
63, 47, 23,
75, 55, 27,
83, 59, 27,
91, 67, 31,
99, 75, 31,
107, 83, 31,
115, 87, 31,
123, 95, 35,
131, 103, 35,
143, 111, 35,
11, 11, 15,
19, 19, 27,
27, 27, 39,
39, 39, 51,
47, 47, 63,
55, 55, 75,
63, 63, 87,
71, 71, 103,
79, 79, 115,
91, 91, 127,
99, 99, 139,
107, 107, 151,
115, 115, 163,
123, 123, 175,
131, 131, 187,
139, 139, 203,
0, 0, 0,
7, 7, 0,
11, 11, 0,
19, 19, 0,
27, 27, 0,
35, 35, 0,
43, 43, 7,
47, 47, 7,
55, 55, 7,
63, 63, 7,
71, 71, 7,
75, 75, 11,
83, 83, 11,
91, 91, 11,
99, 99, 11,
107, 107, 15,
7, 0, 0,
15, 0, 0,
23, 0, 0,
31, 0, 0,
39, 0, 0,
47, 0, 0,
55, 0, 0,
63, 0, 0,
71, 0, 0,
79, 0, 0,
87, 0, 0,
95, 0, 0,
103, 0, 0,
111, 0, 0,
119, 0, 0,
127, 0, 0,
19, 19, 0,
27, 27, 0,
35, 35, 0,
47, 43, 0,
55, 47, 0,
67, 55, 0,
75, 59, 7,
87, 67, 7,
95, 71, 7,
107, 75, 11,
119, 83, 15,
131, 87, 19,
139, 91, 19,
151, 95, 27,
163, 99, 31,
175, 103, 35,
35, 19, 7,
47, 23, 11,
59, 31, 15,
75, 35, 19,
87, 43, 23,
99, 47, 31,
115, 55, 35,
127, 59, 43,
143, 67, 51,
159, 79, 51,
175, 99, 47,
191, 119, 47,
207, 143, 43,
223, 171, 39,
239, 203, 31,
255, 243, 27,
11, 7, 0,
27, 19, 0,
43, 35, 15,
55, 43, 19,
71, 51, 27,
83, 55, 35,
99, 63, 43,
111, 71, 51,
127, 83, 63,
139, 95, 71,
155, 107, 83,
167, 123, 95,
183, 135, 107,
195, 147, 123,
211, 163, 139,
227, 179, 151,
171, 139, 163,
159, 127, 151,
147, 115, 135,
139, 103, 123,
127, 91, 111,
119, 83, 99,
107, 75, 87,
95, 63, 75,
87, 55, 67,
75, 47, 55,
67, 39, 47,
55, 31, 35,
43, 23, 27,
35, 19, 19,
23, 11, 11,
15, 7, 7,
187, 115, 159,
175, 107, 143,
163, 95, 131,
151, 87, 119,
139, 79, 107,
127, 75, 95,
115, 67, 83,
107, 59, 75,
95, 51, 63,
83, 43, 55,
71, 35, 43,
59, 31, 35,
47, 23, 27,
35, 19, 19,
23, 11, 11,
15, 7, 7,
219, 195, 187,
203, 179, 167,
191, 163, 155,
175, 151, 139,
163, 135, 123,
151, 123, 111,
135, 111, 95,
123, 99, 83,
107, 87, 71,
95, 75, 59,
83, 63, 51,
67, 51, 39,
55, 43, 31,
39, 31, 23,
27, 19, 15,
15, 11, 7,
111, 131, 123,
103, 123, 111,
95, 115, 103,
87, 107, 95,
79, 99, 87,
71, 91, 79,
63, 83, 71,
55, 75, 63,
47, 67, 55,
43, 59, 47,
35, 51, 39,
31, 43, 31,
23, 35, 23,
15, 27, 19,
11, 19, 11,
7, 11, 7,
255, 243, 27,
239, 223, 23,
219, 203, 19,
203, 183, 15,
187, 167, 15,
171, 151, 11,
155, 131, 7,
139, 115, 7,
123, 99, 7,
107, 83, 0,
91, 71, 0,
75, 55, 0,
59, 43, 0,
43, 31, 0,
27, 15, 0,
11, 7, 0,
0, 0, 255,
11, 11, 239,
19, 19, 223,
27, 27, 207,
35, 35, 191,
43, 43, 175,
47, 47, 159,
47, 47, 143,
47, 47, 127,
47, 47, 111,
47, 47, 95,
43, 43, 79,
35, 35, 63,
27, 27, 47,
19, 19, 31,
11, 11, 15,
43, 0, 0,
59, 0, 0,
75, 7, 0,
95, 7, 0,
111, 15, 0,
127, 23, 7,
147, 31, 7,
163, 39, 11,
183, 51, 15,
195, 75, 27,
207, 99, 43,
219, 127, 59,
227, 151, 79,
231, 171, 95,
239, 191, 119,
247, 211, 139,
167, 123, 59,
183, 155, 55,
199, 195, 55,
231, 227, 87,
127, 191, 255,
171, 231, 255,
215, 255, 255,
103, 0, 0,
139, 0, 0,
179, 0, 0,
215, 0, 0,
255, 0, 0,
255, 243, 147,
255, 247, 199,
255, 255, 255,
159, 91, 83,
};
#endif // _QUAKEPALETTE_H
[And I just noticed a weird oversight that actually works for a really funny reason and I think I'll just keep that in there. ColorDistance < bestColorDistance ---> but bestColorDistance starts as -1. Still works of all things probably because ColorDistance is unsigned int so -1 evaluates to a very large number. ]
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Image Manipulation
Baker wrote:[And I just noticed a weird oversight that actually works for a really funny reason and I think I'll just keep that in there. ColorDistance < bestColorDistance ---> but bestColorDistance starts as -1. Still works of all things probably because ColorDistance is unsigned int so -1 evaluates to a very large number. ]
That is probably compiler-dependent behavior, not something you want to rely on, so fix that crappy part of the code.
- andrewj
- Posts: 133
- Joined: Mon Aug 30, 2010 3:29 pm
- Location: Australia
Re: Image Manipulation
andrewj wrote:That is probably compiler-dependent behavior, not something you want to rely on, so fix that crappy part of the code.
unsigned bestColorDistance = 0xFFFFFFFF;
- Code: Select all
typedef enum
{
TRANSFORM_FLIP_VERTICAL,
TRANSFORM_FLIP_HORIZONTAL,
TRANSFORM_ROTATE_CLOCKWISE90,
TRANSFORM_ROTATE_COUNTERCLOCKWISE90,
TRANSFORM_ROTATE_180,
} transform_simple_op;
- Code: Select all
byte* Image_RGBA_Transform (const int transformOperation, byte *source, const int sourceSize, int* sourceWidth, int* sourceHeight)
{
const int pixelcount = *sourceWidth * *sourceHeight;
const int bitsPerPixel = sourceSize / pixelcount; // Choke if not RGBA?
const fbool swapWidthHeight = (transformOperation == TRANSFORM_ROTATE_CLOCKWISE90 || transformOperation == TRANSFORM_ROTATE_COUNTERCLOCKWISE90);
unsigned* oldImage = (unsigned*)source;
const int oldWidth = *sourceWidth;
const int oldHeight = *sourceHeight;
unsigned* newImage = Memory_malloc(sourceSize, "New Buffer");
int newWidth = oldWidth;
int newHeight = oldHeight;
unsigned* newPixel = newImage;
if (swapWidthHeight)
{
newWidth = oldHeight;
newHeight = oldWidth;
}
for (int row = 0; row < oldHeight; row ++)
for (int col = 0; col < oldWidth; col ++, newPixel ++)
{
int oldrow, oldcol;
switch (transformOperation)
{
case TRANSFORM_ROTATE_COUNTERCLOCKWISE90:
oldrow = col; oldcol = (oldHeight - 1) - row; break;
case TRANSFORM_ROTATE_CLOCKWISE90: oldrow = (oldWidth - 1) - col; oldcol = row; break;
case TRANSFORM_FLIP_VERTICAL: oldrow = (oldHeight - 1) - row; oldcol = col; break;
case TRANSFORM_FLIP_HORIZONTAL: oldrow = row; oldcol = (oldWidth - 1) - col; break;
case TRANSFORM_ROTATE_180: oldrow = (oldHeight - 1) - row; oldcol = (oldWidth - 1) - col; break;
default: break;
}
*newPixel = oldImage[oldrow * oldWidth + oldcol];
}
// Destroy old image
Memory_free (source);
*sourceWidth = newWidth;
*sourceHeight = newHeight;
return (byte*) newImage;
}
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
39 posts
• Page 1 of 3 • 1, 2, 3
Who is online
Users browsing this forum: No registered users and 1 guest
