From 27278cdd88e5e358a24791a615064b6af28a5d5e Mon Sep 17 00:00:00 2001 From: eukos Date: Sun, 20 Sep 2015 21:19:37 +0200 Subject: [PATCH] Editor support for importing/exporting PCX images, as well as editor palette and colormap optimisations... --- Makefile | 4 + engine/draw_image.c | 223 +++++++++++++++++++++++++++++++++++++++++++ engine/screen.c | 83 +--------------- include/draw.h | 6 -- include/draw_image.h | 27 ++++++ video/vid_gdk.c | 173 ++++++++++++++++++++++++++++++--- 6 files changed, 416 insertions(+), 100 deletions(-) create mode 100644 engine/draw_image.c create mode 100644 include/draw_image.h diff --git a/Makefile b/Makefile index bad2486..548ed20 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/engine/draw_image.c b/engine/draw_image.c new file mode 100644 index 0000000..e5a1da8 --- /dev/null +++ b/engine/draw_image.c @@ -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 ; yswidth) + { + 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; +} diff --git a/engine/screen.c b/engine/screen.c index 6a441f3..ddc84bd 100644 --- a/engine/screen.c +++ b/engine/screen.c @@ -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 diff --git a/include/draw.h b/include/draw.h index 21116d1..1b63b62 100644 --- a/include/draw.h +++ b/include/draw.h @@ -69,9 +69,3 @@ int rmap_maps; int rmap_pics; int rmap_particles; extern byte coltranslate[256]; // TranslateToCustomPal - - - - - - diff --git a/include/draw_image.h b/include/draw_image.h new file mode 100644 index 0000000..74ebb25 --- /dev/null +++ b/include/draw_image.h @@ -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); diff --git a/video/vid_gdk.c b/video/vid_gdk.c index 61fc7a0..5e923c7 100644 --- a/video/vid_gdk.c +++ b/video/vid_gdk.c @@ -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);