WinQuake: Loading 24-bit media as 8-bit
Moderator: InsideQC Admins
7 posts
• Page 1 of 1
WinQuake: Loading 24-bit media as 8-bit
I'm just posting this because I can't work on it at the moment, but want to document the thought.
I'm going to end up using Sajt's palette reduction code so "WinQuake" can load the same 24-bit skyboxes that a single player map would be using. I'm expecting the results to be "ok" in most cases, a bit rough in other cases, but it is a nice middleground.
Another possible use of this is to be able to load, say, a Half-Life map and palette reduce the color to the Quake palette for the sake of being able to somewhat load it properly in a WinQuakey engine.
Currently, I have a "WinQuake" build that loads Half-Life BSP crappily using the same method that FuhQuake used to permit that ... it turns all the textures into a single color. Which is functional, but highly ugly.
(Of course the true solution would be the massive FTEQW 24-bit color modification, but I can't justify that kind of time expenditure at this time when the same amount of time can do so much more with, say, an OpenGL build.)
Anyway, Sajt's Qwalk palette reduction, which I have posted in another thread but I might as well repost.
In a few weeks, I'm sure I'll have a working implementation of the above.
I'm going to end up using Sajt's palette reduction code so "WinQuake" can load the same 24-bit skyboxes that a single player map would be using. I'm expecting the results to be "ok" in most cases, a bit rough in other cases, but it is a nice middleground.
Another possible use of this is to be able to load, say, a Half-Life map and palette reduce the color to the Quake palette for the sake of being able to somewhat load it properly in a WinQuakey engine.
Currently, I have a "WinQuake" build that loads Half-Life BSP crappily using the same method that FuhQuake used to permit that ... it turns all the textures into a single color. Which is functional, but highly ugly.
(Of course the true solution would be the massive FTEQW 24-bit color modification, but I can't justify that kind of time expenditure at this time when the same amount of time can do so much more with, say, an OpenGL build.)
Anyway, Sajt's Qwalk palette reduction, which I have posted in another thread but I might as well repost.
- Code: Select all
image_paletted_t *image_palettize(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*)qmalloc(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;
}
In a few weeks, I'm sure I'll have a working implementation of the above.
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
A warning: palette reduction is by far the slowest step in Qwalk model conversion when you are converting something to MDL or MD2. Because of the "palettize_colour" function itself. I decided I wanted to do it exactly, and I'm not really sure how to optimize it, other than to, for each pixel, compare with all 256 colours. (Now that was a badly split infinitive...)
The other option would be something like to reduce 24-bit to 16-bit first, then you can use a offline generated lookup table (in fact GLQuake mentions some '15to8' conversion table which is probably this exactly, though I don't know what it was used for). It would be infinitely faster, but possibly lead to some minor posterization. I would probably recommend it if you were doing this in the game on load.
If you used a 16to8 or 15to8 table, and didn't support fullbrights (which aren't used in 24-bit colour images anyway), you could inline the lookup and unroll the loop a bit and it would be basically instantaneous.
The other option would be something like to reduce 24-bit to 16-bit first, then you can use a offline generated lookup table (in fact GLQuake mentions some '15to8' conversion table which is probably this exactly, though I don't know what it was used for). It would be infinitely faster, but possibly lead to some minor posterization. I would probably recommend it if you were doing this in the game on load.
If you used a 16to8 or 15to8 table, and didn't support fullbrights (which aren't used in 24-bit colour images anyway), you could inline the lookup and unroll the loop a bit and it would be basically instantaneous.
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
- Sajt
- Posts: 1215
- Joined: Sat Oct 16, 2004 3:39 am
Ah well this is sort of an intermediate bandage anyway so if there is a bit of a delay during load, so be it. It is far more convenient than the alternatives (open .tga skybox in gfx editor and reduce palette and save as .pcx).
I love FTE's 24-bit color but it presents a couple of implementation challenges in an attempt to port it (for instance, you can't really port just part of it, most of the changes have to be ported for it to function).
But at some point I will .... different color tables, pixel formats, etc. Sounds like 3 days of fun for the middle of next summer.
I love FTE's 24-bit color but it presents a couple of implementation challenges in an attempt to port it (for instance, you can't really port just part of it, most of the changes have to be ported for it to function).
But at some point I will .... different color tables, pixel formats, etc. Sounds like 3 days of fun for the middle of next summer.
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
Sajt wrote:reduce 24-bit to 16-bit first, then you can use a offline generated lookup table (in fact GLQuake mentions some '15to8' conversion table which is probably this exactly, though I don't know what it was used for). It would be infinitely faster, but possibly lead to some minor posterization.
Yes, the loss of detail would be huge, bigger than the loss from a direct conversion of 24-bit color to 8-bit color.
If you really want to convert everything as fast as possible, using a lookup table from 24 to 8 bit color could be feasible since such a table would only take 16MB of RAM.
-

mankrip - Posts: 915
- Joined: Fri Jul 04, 2008 3:02 am
Well, the loss of detail with 15->8 isn't so much worse than straight 24->8, but the absolute worst case for this sort of thing is skyboxes, which tend to have large areas of subtle gradients. Any kind of extra posterization would be very destructive to the visual quality.
A 16MB lookup table is kind of crazy. You could do that. Or you could convert the skybox images to 8-bit the slow way but cache the results somewhere in AppData, and regenerate them if the original file's modified time changes. If you were to use the 16MB lookup table, this would have to be generated and cached anyway, and it would be REALLY slow to generate, equivalent to a 4096x4096 image.
Anyway, if you did cache stuff in AppData, it would probably be nice to have a menu that lists all cached stuff and lets the user delete them (e.g. skyboxes from mods that were uninstalled ages ago), if the user is concerned about hard-drive space or cruft.
A 16MB lookup table is kind of crazy. You could do that. Or you could convert the skybox images to 8-bit the slow way but cache the results somewhere in AppData, and regenerate them if the original file's modified time changes. If you were to use the 16MB lookup table, this would have to be generated and cached anyway, and it would be REALLY slow to generate, equivalent to a 4096x4096 image.
Anyway, if you did cache stuff in AppData, it would probably be nice to have a menu that lists all cached stuff and lets the user delete them (e.g. skyboxes from mods that were uninstalled ages ago), if the user is concerned about hard-drive space or cruft.
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
- Sajt
- Posts: 1215
- Joined: Sat Oct 16, 2004 3:39 am
24-to-8 lookup table would work fairly well, but ideally you would implement some sort of error diffusion or dithering to help replicate colors better.
The simplest error diffusion i know of is to store a running "error" value and subtract if from the current RGB value before doing the lookup. Then you calculate the difference between the indexed RGB value and the original input, and store that as your new error (to be subtracted from the next pixel input.) When you get to the end of the row you throw away the remaining error.
The simplest error diffusion i know of is to store a running "error" value and subtract if from the current RGB value before doing the lookup. Then you calculate the difference between the indexed RGB value and the original input, and store that as your new error (to be subtracted from the next pixel input.) When you get to the end of the row you throw away the remaining error.
- metlslime
- Posts: 316
- Joined: Tue Feb 05, 2008 11:03 pm
Almost as easy is a simplified Floyd Steinberg, where 50% of the error goes sideways and 50% goes down to the next row.
I once tried doing that in the surface cache functions of Quake2, but it was much slower and not much of a visual improvement .
I once tried doing that in the surface cache functions of Quake2, but it was much slower and not much of a visual improvement .
- andrewj
- Posts: 133
- Joined: Mon Aug 30, 2010 3:29 pm
- Location: Australia
7 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest