Editor support for importing/exporting PCX images, as well as editor palette and colormap optimisations...

This commit is contained in:
eukos 2015-09-20 21:19:37 +02:00
parent 9c03393d10
commit 27278cdd88
6 changed files with 416 additions and 100 deletions

View file

@ -122,6 +122,7 @@ NGUNIX_OBJS = \
$(BUILDDIR)/console.o \
$(BUILDDIR)/crc.o \
$(BUILDDIR)/cvar.o \
$(BUILDDIR)/draw_image.o \
$(BUILDDIR)/draw_fallback.o \
$(BUILDDIR)/draw.o \
$(BUILDDIR)/d_edge.o \
@ -368,6 +369,9 @@ $(BUILDDIR)/crc.o : $(MOUNT_DIR)/crc.c
$(BUILDDIR)/cvar.o : $(MOUNT_DIR)/cvar.c
$(DO_CC)
$(BUILDDIR)/draw_image.o : $(MOUNT_DIR)/draw_image.c
$(DO_CC) -O2
$(BUILDDIR)/draw_fallback.o : $(MOUNT_DIR)/draw_fallback.c
$(DO_CC) -O2

223
engine/draw_image.c Normal file
View file

@ -0,0 +1,223 @@
/*
Copyright (C) 2015 Marco "eukara" Hladik
Copyright (C) 1996-1997 Id Software, Inc.
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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// Parsing image files, mainly PCX - code mainly adopted from FTEQW by Spike
#include "globaldef.h"
#include "r_local.h"
typedef struct
{
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin,ymin,xmax,ymax;
unsigned short hres,vres;
unsigned char palette[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
unsigned char data; // unbounded
} pcx_t;
/*
==============
WritePCXfile
==============
*/
int WritePCXfile (const char *filename, byte *data, int width, int height, int rowbytes, byte *palette)
{
int i, j;
size_t length;
pcx_t *pcx;
byte *pack;
pcx = (pcx_t *) Hunk_TempAlloc (width*height*2+1000);
if (pcx == NULL)
{
Con_Printf("[IMAGE] Not enough memory to write PCX!\n");
return -1;
}
pcx->manufacturer = 0x0a; // PCX id
pcx->version = 5; // 256 color
pcx->encoding = 1; // uncompressed
pcx->bits_per_pixel = 8; // 256 color
pcx->xmin = 0;
pcx->ymin = 0;
pcx->xmax = LittleShort((short)(width-1));
pcx->ymax = LittleShort((short)(height-1));
pcx->hres = LittleShort((short)width);
pcx->vres = LittleShort((short)height);
memset (pcx->palette, 0, sizeof(pcx->palette));
pcx->color_planes = 1; // chunky image
pcx->bytes_per_line = LittleShort((short)width);
pcx->palette_type = LittleShort(2); // not a grey scale
memset (pcx->filler, 0, sizeof(pcx->filler));
#if 0
int b;
printf("Exporting IMAGE: %i, %i\n", width, height);
for(b = 0; b < width * height; b++)
printf("%i,", data[b]);
printf("\n...DONE\n");
#endif
// pack the image
pack = &pcx->data;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
if ( (*data & 0xc0) != 0xc0)
*pack++ = *data++;
else
{
*pack++ = 0xc1;
*pack++ = *data++;
}
}
data += rowbytes - width;
}
*pack++ = 0x0c;
for (i = 0; i < 768; i++)
*pack++ = *palette++;
length = pack - (byte *)pcx;
COM_WriteFile(filename, pcx, length);
}
/*
==============
ReadPCXFile
==============
*/
byte *ReadPCXFile(byte *buf, int length, int *width, int *height)
{
pcx_t *pcx;
byte *palette;
byte *pix;
int x, y;
int dataByte, runLength;
int count;
byte *data;
byte *pcx_rgb;
unsigned short xmin, ymin, swidth, sheight;
pcx = (pcx_t *)buf;
xmin = LittleShort(pcx->xmin);
ymin = LittleShort(pcx->ymin);
swidth = LittleShort(pcx->xmax)-xmin+1;
sheight = LittleShort(pcx->ymax)-ymin+1;
if (pcx->manufacturer != 0x0a
|| pcx->version != 5
|| pcx->encoding != 1
|| pcx->bits_per_pixel != 8
|| swidth >= 1024
|| sheight >= 1024)
{
return NULL;
}
*width = swidth;
*height = sheight;
palette = host_basepal;
data = (char *)(pcx+1);
count = (swidth) * (sheight);
pcx_rgb = malloc(count);
for (y=0 ; y<sheight ; y++)
{
pix = pcx_rgb + y*(swidth);
for (x=0 ; x<swidth ; )
{
dataByte = *data;
data++;
if((dataByte & 0xC0) == 0xC0)
{
runLength = dataByte & 0x3F;
if (x+runLength>swidth)
{
Con_Printf("[IMAGE] Corrupt PCX File!\n");
free(pcx_rgb);
return NULL;
}
dataByte = *data;
data++;
}
else
runLength = 1;
while(runLength-- > 0)
{
int rgb[3];
rgb[0] = palette[dataByte*3];
rgb[1] = palette[dataByte*3+1];
rgb[2] = palette[dataByte*3+2];
pix[0] = Convert_24_to_8(palette, rgb);
pix++;
x++;
}
}
}
return pcx_rgb;
}
/*
==============
ReadPCXPalette
==============
*/
byte *ReadPCXPalette(byte *buf, int len, byte *out)
{
pcx_t *pcx;
pcx = (pcx_t *)buf;
if (pcx->manufacturer != 0x0a
|| pcx->version != 5
|| pcx->encoding != 1
|| pcx->bits_per_pixel != 8
|| LittleShort(pcx->xmax) >= 1024
|| LittleShort(pcx->ymax) >= 1024)
{
return NULL;
}
memcpy(out, (byte *)pcx + len - 768, 768);
return out;
}

View file

@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "globaldef.h"
#include "r_local.h"
#include "draw_image.h"
// only the refresh window will be updated unless these variables are flagged
int scr_copytop;
@ -926,88 +927,6 @@ void SCR_DrawConsole (void)
==============================================================================
*/
typedef struct
{
char manufacturer;
char version;
char encoding;
char bits_per_pixel;
unsigned short xmin,ymin,xmax,ymax;
unsigned short hres,vres;
unsigned char palette[48];
char reserved;
char color_planes;
unsigned short bytes_per_line;
unsigned short palette_type;
char filler[58];
unsigned char data; // unbounded
} pcx_t;
/*
==============
WritePCXfile
==============
*/
static int WritePCXfile (const char *filename, byte *data, int width, int height, int rowbytes, byte *palette)
{
int i, j;
size_t length;
pcx_t *pcx;
byte *pack;
pcx = (pcx_t *) Hunk_TempAlloc (width*height*2+1000);
if (pcx == NULL)
{
Con_Printf("WritePCXfile: not enough memory\n");
return -1;
}
pcx->manufacturer = 0x0a; // PCX id
pcx->version = 5; // 256 color
pcx->encoding = 1; // uncompressed
pcx->bits_per_pixel = 8; // 256 color
pcx->xmin = 0;
pcx->ymin = 0;
pcx->xmax = LittleShort((short)(width-1));
pcx->ymax = LittleShort((short)(height-1));
pcx->hres = LittleShort((short)width);
pcx->vres = LittleShort((short)height);
memset (pcx->palette, 0, sizeof(pcx->palette));
pcx->color_planes = 1; // chunky image
pcx->bytes_per_line = LittleShort((short)width);
pcx->palette_type = LittleShort(2); // not a grey scale
memset (pcx->filler, 0, sizeof(pcx->filler));
// pack the image
pack = &pcx->data;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
if ( (*data & 0xc0) != 0xc0)
*pack++ = *data++;
else
{
*pack++ = 0xc1;
*pack++ = *data++;
}
}
data += rowbytes - width;
}
// write the palette
*pack++ = 0x0c; // palette ID byte
for (i = 0; i < 768; i++)
*pack++ = *palette++;
// write output file
length = pack - (byte *)pcx;
COM_WriteFile(filename, pcx, length);
}
/*
==================
SCR_ScreenShot_f

View file

@ -69,9 +69,3 @@ int rmap_maps;
int rmap_pics;
int rmap_particles;
extern byte coltranslate[256]; // TranslateToCustomPal

27
include/draw_image.h Normal file
View file

@ -0,0 +1,27 @@
/*
Copyright (C) 1996-1997 Id Software, Inc.
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 2
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, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
// draw.h -- these are the only functions outside the refresh allowed
// to touch the vid buffer
int WritePCXfile (const char *filename, byte *data, int width, int height, int rowbytes, byte *palette);
byte *ReadPCXFile(byte *buf, int length, int *width, int *height);
byte *ReadPCXData(byte *buf, int length, int width, int height, byte *result);
byte *ReadPCXPalette(byte *buf, int len, byte *out);

View file

@ -152,7 +152,9 @@ void VID_Init(unsigned char *palette)
Sys_Error("[VIDEO] Bad window height\n");
}
ed_palette = palette;
ed_palette = malloc(16*16*3);
memcpy(&ed_palette, &palette, sizeof(16*16*3));
vid.maxwarpwidth = WARP_WIDTH;
vid.maxwarpheight = WARP_HEIGHT;
vid.maxlowwidth = LOW_WIDTH;
@ -850,7 +852,7 @@ gint VID_EditorViewColormap(gpointer data)
if(pic)
{
ed_file = malloc(16385 + (sizeof(int) * 2));
ed_file = Hunk_Alloc(16385 + (sizeof(int) * 2));
ed_file->width = 256;
ed_file->height = 64;
@ -989,6 +991,7 @@ gint VID_EditorSaveFile(gpointer data)
COM_WriteFile (filename, pic, ed_file->width * ed_file->height + 2);
g_free (filename);
free(pic);
}
gtk_widget_destroy (dialog);
@ -1059,19 +1062,15 @@ gint VID_EditorLoadFile(gpointer data)
GtkWidget *ed_newwindow;
GtkWidget *ed_widthbox;
GtkWidget *ed_heightbox;
int ed_twi, ed_twh;
gint VID_EditorCreatePic(gpointer data)
{
int i;
int mem;
if(ed_file)
free(ed_file);
mem = (size_t)((sizeof(int) * 2) + (gtk_spin_button_get_value_as_int (ed_widthbox) * gtk_spin_button_get_value_as_int (ed_heightbox)));
//mem = 262152;
printf("[EDITOR] Trying to allocate %i memory...\n", mem);
ed_file = malloc((size_t)mem);
ed_file = Hunk_Alloc((size_t)mem);
ed_file->width = gtk_spin_button_get_value_as_int (ed_widthbox);
ed_file->height = gtk_spin_button_get_value_as_int (ed_heightbox);
@ -1114,15 +1113,144 @@ gint VID_EditorNewFile(gpointer data)
g_signal_connect(btnok, "clicked", G_CALLBACK(VID_EditorCreatePic), G_OBJECT(ed_newwindow));
}
/* EDITOR: LOAD PCX
* ================
* Loads a PCX into the editor as a pic...
*/
gint VID_EditorImportFile(gpointer data)
{
GtkWidget *dialog;
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN;
gint res;
loadedfile_t *pic;
dialog = gtk_file_chooser_dialog_new ("NGUNIXEd - Picture Exchange (.pcx) File",
NULL,
action,
("_Cancel"),
GTK_RESPONSE_CANCEL,
("_Open"),
GTK_RESPONSE_ACCEPT,
NULL);
// Set the dialog and the default path (current directory)
GtkFileChooser *chooser = GTK_FILE_CHOOSER (dialog);
char path[MAX_OSPATH];
sprintf(path, "file://%s", get_current_dir_name());
gtk_file_chooser_set_current_folder_uri(chooser, path);
// Add the filter for .lmp files
GtkFileFilter *filter = gtk_file_filter_new ();
gtk_file_filter_add_pattern (filter, "*.pcx");
gtk_file_chooser_set_filter (chooser, filter);
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_ACCEPT)
{
char *filename;
byte *temp;
filename = gtk_file_chooser_get_filename (chooser);
pic = COM_LoadFile (filename, 2);
if (pic)
{
int pic_width, pic_height;
byte *pic_data = Hunk_Alloc(pic->filelen);
pic_data = ReadPCXFile(pic->data, pic->filelen, &pic_width, &pic_height);
ed_file = Hunk_Alloc((sizeof(int) * 2) + (pic_width * pic_height));
ed_file->width = pic_width;
ed_file->height = pic_height;
int i;
for(i = 0; i < pic_width * pic_height; i++)
ed_file->data[i] = pic_data[i];
}
gtk_window_resize(ed_window, ed_file->width, ed_file->height + ed_menubar->allocation.height);
g_free (filename);
}
gtk_widget_destroy (dialog);
return 0;
}
/* EDITOR: EXPORT LUMP
* ===================
* Saves the pic currently displayed as a PCX file...
*/
gint VID_EditorExportFile(gpointer data)
{
GtkWidget *dialog;
GtkFileChooser *chooser;
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SAVE;
gint res;
byte *pic;
if(!ed_file)
return;
dialog = gtk_file_chooser_dialog_new ("NGUNIXEd - Save Picture Exchange (.pcx) File",
NULL,
action,
("_Cancel"),
GTK_RESPONSE_CANCEL,
("_Save"),
GTK_RESPONSE_ACCEPT,
NULL);
chooser = GTK_FILE_CHOOSER (dialog);
gtk_file_chooser_set_do_overwrite_confirmation (chooser, TRUE);
// Set the default path (current directory)
char path[MAX_OSPATH];
sprintf(path, "file://%s", get_current_dir_name());
gtk_file_chooser_set_current_folder_uri(chooser, path);
// Add the filter for .lmp files
GtkFileFilter *filter = gtk_file_filter_new ();
gtk_file_filter_add_pattern (filter, "*.pcx");
gtk_file_chooser_set_filter (chooser, filter);
//if (user_edited_a_new_document)
gtk_file_chooser_set_current_name (chooser,
("untitled.pcx"));
//else
// gtk_file_chooser_set_filename (chooser,
// existing_filename);
res = gtk_dialog_run (GTK_DIALOG (dialog));
if (res == GTK_RESPONSE_ACCEPT)
{
char *filename;
filename = gtk_file_chooser_get_filename (chooser);
int w, h;
w = ed_file->width;
h = ed_file->height;
pic = Hunk_Alloc(ed_file->width * ed_file->height);
memcpy(pic, &ed_file->data, (ed_file->width * ed_file->height));
#if 0
int i;
printf("Exporting IMAGE: %i, %i\n", ed_file->width, ed_file->height);
for(i = 0; i < ed_file->width * ed_file->height; i++)
printf("%i,", ed_file->data[i]);
printf("\n...DONE\n");
#endif
WritePCXfile (filename, pic, w, h, ed_file->width, host_basepal);
g_free (filename);
}
gtk_widget_destroy (dialog);
return 0;
}
/* EDITOR: QUIT
* ============
* Kills the editor, unloads any pics, etc.
*/
void VID_EditorQuit(void)
{
if(ed_file)
free(ed_file);
gtk_widget_destroy(ed_window);
vid_ineditor = false;
gtk_main_quit();
@ -1153,8 +1281,23 @@ void VID_LaunchEditor(void)
GtkWidget *vbox;
GtkWidget *fileMenu;
GtkWidget *extraMenu;
unsigned int ed_colors[256];
if (!ed_cmap)
{
int i;
for (i = 0; i < 256; i++)
{
unsigned char r, g, b;
r = *ed_palette++;
g = *ed_palette++;
b = *ed_palette++;
ed_colors[i] = r << 16 | g << 8 | b;
}
ed_cmap = gdk_rgb_cmap_new(ed_colors, 256);
}
ed_cmap = x_cmap;
vid_ineditor = true;
ed_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(ed_window), GTK_WIN_POS_CENTER);
@ -1172,6 +1315,8 @@ void VID_LaunchEditor(void)
GtkWidget *newMi = gtk_menu_item_new_with_label("New");
GtkWidget *loadMi = gtk_menu_item_new_with_label("Load...");
GtkWidget *saveMi = gtk_menu_item_new_with_label("Save...");
GtkWidget *importMi = gtk_menu_item_new_with_label("Import...");
GtkWidget *exportMi = gtk_menu_item_new_with_label("Export...");
GtkWidget *quitMi = gtk_menu_item_new_with_label("Quit");
GtkWidget *extrMi = gtk_menu_item_new_with_label("Extra");
@ -1183,6 +1328,8 @@ void VID_LaunchEditor(void)
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), newMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), loadMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), saveMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), importMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), exportMi);
gtk_menu_shell_append(GTK_MENU_SHELL(fileMenu), quitMi);
gtk_menu_shell_append(GTK_MENU_SHELL(ed_menubar), fileMi);
@ -1198,7 +1345,9 @@ void VID_LaunchEditor(void)
g_signal_connect(G_OBJECT(newMi), "activate", G_CALLBACK(VID_EditorNewFile), NULL);
g_signal_connect(G_OBJECT(loadMi), "activate", G_CALLBACK(VID_EditorLoadFile), NULL);
g_signal_connect(G_OBJECT(saveMi), "activate", G_CALLBACK(VID_EditorSaveFile), NULL);
g_signal_connect(G_OBJECT(quitMi), "activate", G_CALLBACK(VID_EditorQuit), NULL);
g_signal_connect(G_OBJECT(importMi), "activate", G_CALLBACK(VID_EditorImportFile), NULL);
g_signal_connect(G_OBJECT(exportMi), "activate", G_CALLBACK(VID_EditorExportFile), NULL);
g_signal_connect(G_OBJECT(quitMi), "activate", G_CALLBACK(VID_EditorQuit), NULL);
g_signal_connect(G_OBJECT(swipalMi), "activate", G_CALLBACK(VID_EditorLoadPalette), NULL);
g_signal_connect(G_OBJECT(viewcolmapMi), "activate", G_CALLBACK(VID_EditorViewColormap), NULL);
g_signal_connect(G_OBJECT(gencolmapMi), "activate", G_CALLBACK(VID_EditorGenerateColormap), NULL);