mirror of https://github.com/GNOME/gimp.git
520 lines
15 KiB
C
520 lines
15 KiB
C
/* pcx.c GIMP plug-in for loading & saving PCX files */
|
|
|
|
/* This code is based in parts on code by Francisco Bustamante, but the
|
|
largest portion of the code has been rewritten and is now maintained
|
|
occasionally by Nick Lamb njl195@zepler.org.uk */
|
|
|
|
/* New for 1998 -- Load 1, 4, 8 & 24 bit PCX files */
|
|
/* -- Save 8 & 24 bit PCX files */
|
|
/* 1998-01-19 - fixed some endianness problems (Raphael Quinet) */
|
|
/* 1998-02-05 - merged patch with "official" tree, some tidying up (njl) */
|
|
/* 1998-05-17 - changed email address, more tidying up (njl) */
|
|
/* 1998-05-31 - g_message (njl) */
|
|
|
|
/* Please contact me if you can't use your PCXs with this tool, I want
|
|
The GIMP to have the best file filters on the planet */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "libgimp/gimp.h"
|
|
|
|
/* Declare plug-in functions. */
|
|
|
|
static void query (void);
|
|
static void run (char *name, int nparams, GParam *param, int *nreturn_vals,
|
|
GParam **return_vals);
|
|
|
|
#if defined(_BIG_ENDIAN) || defined(sparc) || defined (__sgi)
|
|
#define qtohl(x) \
|
|
((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
|
|
(((unsigned long int)(x) & 0x0000ff00U) << 8) | \
|
|
(((unsigned long int)(x) & 0x00ff0000U) >> 8) | \
|
|
(((unsigned long int)(x) & 0xff000000U) >> 24)))
|
|
#define qtohs(x) \
|
|
((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
|
|
(((unsigned short int)(x) & 0xff00) >> 8)))
|
|
#else
|
|
#define qtohl(x) (x)
|
|
#define qtohs(x) (x)
|
|
#endif
|
|
#define htoql(x) qtohl(x)
|
|
#define htoqs(x) qtohs(x)
|
|
|
|
GPlugInInfo PLUG_IN_INFO = { NULL, NULL, query, run, };
|
|
|
|
MAIN ()
|
|
|
|
static void query () {
|
|
static GParamDef load_args[] =
|
|
{
|
|
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
|
{ PARAM_STRING, "filename", "The name of the file to load" },
|
|
{ PARAM_STRING, "raw_filename", "The name entered" },
|
|
};
|
|
static GParamDef load_return_vals[] =
|
|
{
|
|
{ PARAM_IMAGE, "image", "Output image" },
|
|
};
|
|
static int nload_args = sizeof (load_args) / sizeof (load_args[0]);
|
|
static int nload_return_vals = sizeof (load_return_vals) / sizeof (load_return_vals[0]);
|
|
|
|
static GParamDef save_args[] =
|
|
{
|
|
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
|
{ PARAM_IMAGE, "image", "Input image" },
|
|
{ PARAM_DRAWABLE, "drawable", "Drawable to save" },
|
|
{ PARAM_STRING, "filename", "The name of the file to save the image in" },
|
|
{ PARAM_STRING, "raw_filename", "The name entered" },
|
|
};
|
|
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
|
|
|
gimp_install_procedure ("file_pcx_load",
|
|
"Loads files in Zsoft PCX file format",
|
|
"FIXME: write help for pcx_load",
|
|
"Francisco Bustamante & Nick Lamb",
|
|
"Nick Lamb <njl195@zepler.org.uk>",
|
|
"January 1997",
|
|
"<Load>/PCX",
|
|
NULL,
|
|
PROC_PLUG_IN,
|
|
nload_args, nload_return_vals,
|
|
load_args, load_return_vals);
|
|
|
|
gimp_install_procedure ("file_pcx_save",
|
|
"Saves files in ZSoft PCX file format",
|
|
"FIXME: write help for pcx_save",
|
|
"Francisco Bustamante & Nick Lamb",
|
|
"Nick Lamb <njl195@zepler.org.uk>",
|
|
"January 1997",
|
|
"<Save>/PCX",
|
|
"INDEXED, RGB, GRAY",
|
|
PROC_PLUG_IN,
|
|
nsave_args, 0,
|
|
save_args, NULL);
|
|
|
|
gimp_register_magic_load_handler ("file_pcx_load", "pcx", "",
|
|
"0&,byte,10,2&,byte,1,3&,byte,>0,3,byte,<9");
|
|
gimp_register_save_handler ("file_pcx_save", "pcx", "");
|
|
}
|
|
|
|
/* Declare internal functions. */
|
|
|
|
static gint32 load_image (char *filename);
|
|
static void load_1(FILE *fp, int width, int height, char *buffer, int bytes);
|
|
static void load_4(FILE *fp, int width, int height, char *buffer, int bytes);
|
|
static void load_8(FILE *fp, int width, int height, char *buffer, int bytes);
|
|
static void load_24(FILE *fp, int width, int height, char *buffer, int bytes);
|
|
static void readline(FILE *fp, guchar* buffer, int bytes);
|
|
|
|
static gint save_image (char *filename, gint32 image, gint32 layer);
|
|
static void save_8(FILE *fp, int width, int height, guchar *buffer);
|
|
static void save_24(FILE *fp, int width, int height, guchar *buffer);
|
|
static void writeline(FILE *fp, guchar *buffer, int bytes);
|
|
|
|
/* Plug-in implementation */
|
|
|
|
static void run (char *name, int nparams, GParam *param, int *nreturn_vals,
|
|
GParam **return_vals) {
|
|
static GParam values[2];
|
|
GStatusType status = STATUS_SUCCESS;
|
|
GRunModeType run_mode;
|
|
gint32 image_ID;
|
|
|
|
run_mode = param[0].data.d_int32;
|
|
|
|
*nreturn_vals = 1;
|
|
*return_vals = values;
|
|
values[0].type = PARAM_STATUS;
|
|
values[0].data.d_status = STATUS_CALLING_ERROR;
|
|
|
|
if (strcmp (name, "file_pcx_load") == 0) {
|
|
image_ID = load_image (param[1].data.d_string);
|
|
|
|
if (image_ID != -1) {
|
|
*nreturn_vals = 2;
|
|
values[0].data.d_status = STATUS_SUCCESS;
|
|
values[1].type = PARAM_IMAGE;
|
|
values[1].data.d_image = image_ID;
|
|
} else {
|
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
|
}
|
|
}
|
|
else if (strcmp (name, "file_pcx_save") == 0)
|
|
{
|
|
switch (run_mode)
|
|
{
|
|
case RUN_INTERACTIVE:
|
|
break;
|
|
|
|
case RUN_NONINTERACTIVE:
|
|
if (nparams != 5)
|
|
status = STATUS_CALLING_ERROR;
|
|
break;
|
|
|
|
case RUN_WITH_LAST_VALS:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
*nreturn_vals = 1;
|
|
if (save_image (param[3].data.d_string, param[1].data.d_int32,
|
|
param[2].data.d_int32)) {
|
|
values[0].data.d_status = STATUS_SUCCESS;
|
|
} else {
|
|
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
guchar mono[6]= {0, 0, 0, 255, 255, 255};
|
|
|
|
static struct {
|
|
guint8 manufacturer;
|
|
guint8 version;
|
|
guint8 compression;
|
|
guint8 bpp;
|
|
gint16 x1, y1;
|
|
gint16 x2, y2;
|
|
gint16 hdpi;
|
|
gint16 vdpi;
|
|
guint8 colormap[48];
|
|
guint8 reserved;
|
|
guint8 planes;
|
|
gint16 bytesperline;
|
|
gint16 color;
|
|
guint8 filler[58];
|
|
} pcx_header;
|
|
|
|
static gint32 load_image (char *filename) {
|
|
FILE *fd;
|
|
GDrawable *drawable;
|
|
GPixelRgn pixel_rgn;
|
|
gchar *message;
|
|
gint offset_x, offset_y, height, width;
|
|
gint32 image, layer;
|
|
guchar *dest, cmap[768];
|
|
|
|
message = g_malloc (strlen (filename) + 11);
|
|
sprintf (message, "Loading %s:", filename);
|
|
gimp_progress_init (message);
|
|
g_free (message);
|
|
|
|
fd = fopen (filename, "rb");
|
|
if (!fd) {
|
|
g_message("PCX Can't open\n%s", filename);
|
|
return -1;
|
|
}
|
|
|
|
if (fread(&pcx_header, 128, 1, fd) == 0) {
|
|
g_message("PCX Can't read header from\n%s", filename);
|
|
return -1;
|
|
}
|
|
|
|
if(pcx_header.manufacturer != 10) {
|
|
g_message("%s\nis not a PCX file", filename);
|
|
return -1;
|
|
}
|
|
|
|
offset_x = qtohs (pcx_header.x1);
|
|
offset_y = qtohs (pcx_header.y1);
|
|
width = qtohs (pcx_header.x2) - offset_x + 1;
|
|
height = qtohs (pcx_header.y2) - offset_y + 1;
|
|
|
|
if (pcx_header.planes == 3 && pcx_header.bpp == 8) {
|
|
image= gimp_image_new (width, height, RGB);
|
|
layer= gimp_layer_new (image, "Background", width, height,
|
|
RGB_IMAGE, 100, NORMAL_MODE);
|
|
} else {
|
|
image= gimp_image_new (width, height, INDEXED);
|
|
layer= gimp_layer_new (image, "Background", width, height,
|
|
INDEXED_IMAGE, 100, NORMAL_MODE);
|
|
}
|
|
gimp_image_set_filename (image, filename);
|
|
gimp_image_add_layer (image, layer, 0);
|
|
gimp_layer_set_offsets (layer, offset_x, offset_y);
|
|
drawable = gimp_drawable_get (layer);
|
|
|
|
if (pcx_header.planes == 1 && pcx_header.bpp == 1) {
|
|
dest = (guchar *) g_malloc (width * height);
|
|
load_1(fd, width, height, dest, qtohs (pcx_header.bytesperline));
|
|
gimp_image_set_cmap (image, mono, 2);
|
|
} else if (pcx_header.planes == 4 && pcx_header.bpp == 1) {
|
|
dest = (guchar *) g_malloc (width * height);
|
|
load_4(fd, width, height, dest, qtohs (pcx_header.bytesperline));
|
|
gimp_image_set_cmap (image, pcx_header.colormap, 16);
|
|
} else if (pcx_header.planes == 1 && pcx_header.bpp == 8) {
|
|
dest = (guchar *) g_malloc (width * height);
|
|
load_8(fd, width, height, dest, qtohs (pcx_header.bytesperline));
|
|
fseek(fd, -768L, SEEK_END);
|
|
fread(cmap, 768, 1, fd);
|
|
gimp_image_set_cmap (image, cmap, 256);
|
|
} else if (pcx_header.planes == 3 && pcx_header.bpp == 8) {
|
|
dest = (guchar *) g_malloc (width * height * 3);
|
|
load_24(fd, width, height, dest, qtohs (pcx_header.bytesperline));
|
|
} else {
|
|
g_message("Unusual PCX flavour, giving up");
|
|
return -1;
|
|
}
|
|
|
|
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, TRUE, FALSE);
|
|
gimp_pixel_rgn_set_rect (&pixel_rgn, dest, 0, 0, width, height);
|
|
|
|
g_free (dest);
|
|
|
|
gimp_drawable_flush (drawable);
|
|
gimp_drawable_detach (drawable);
|
|
|
|
return image;
|
|
}
|
|
|
|
static void load_8(FILE *fp, int width, int height, char *buffer, int bytes) {
|
|
int row;
|
|
guchar *line;
|
|
line= (guchar *) g_malloc(bytes);
|
|
|
|
for (row= 0; row < height; buffer+= width, ++row) {
|
|
readline(fp, line, bytes);
|
|
memcpy(buffer, line, width);
|
|
gimp_progress_update ((double) row / (double) height);
|
|
}
|
|
|
|
g_free(line);
|
|
}
|
|
|
|
static void load_24(FILE *fp, int width, int height, char *buffer, int bytes) {
|
|
int x, y, c;
|
|
guchar *line;
|
|
line= (guchar *) g_malloc(bytes * 3);
|
|
|
|
for (y= 0; y < height; buffer+= width * 3, ++y) {
|
|
for (c= 0; c < 3; ++c) {
|
|
readline(fp, line, bytes);
|
|
for (x= 0; x < width; ++x) {
|
|
buffer[x * 3 + c]= line[x];
|
|
}
|
|
}
|
|
gimp_progress_update ((double) y / (double) height);
|
|
}
|
|
|
|
g_free(line);
|
|
}
|
|
|
|
static void load_1(FILE *fp, int width, int height, char *buffer, int bytes) {
|
|
int x, y;
|
|
guchar *line;
|
|
line= (guchar *) g_malloc(bytes);
|
|
|
|
for (y= 0; y < height; buffer+= width, ++y) {
|
|
readline(fp, line, bytes);
|
|
for (x= 0; x < width; ++x) {
|
|
if (line[x / 8] & (128 >> (x % 8)))
|
|
buffer[x]= 1;
|
|
else
|
|
buffer[x]= 0;
|
|
}
|
|
gimp_progress_update ((double) y / (double) height);
|
|
}
|
|
|
|
g_free(line);
|
|
}
|
|
|
|
static void load_4(FILE *fp, int width, int height, char *buffer, int bytes) {
|
|
int x, y, c;
|
|
guchar *line;
|
|
line= (guchar *) g_malloc(bytes);
|
|
|
|
for (y= 0; y < height; buffer+= width, ++y) {
|
|
for (x= 0; x < width; ++x) buffer[x]= 0;
|
|
for (c= 0; c < 4; ++c) {
|
|
readline(fp, line, bytes);
|
|
for (x= 0; x < width; ++x) {
|
|
if (line[x / 8] & (128 >> (x % 8)))
|
|
buffer[x]+= (1 << c);
|
|
}
|
|
}
|
|
gimp_progress_update ((double) y / (double) height);
|
|
}
|
|
|
|
g_free(line);
|
|
}
|
|
|
|
static void readline(FILE *fp, guchar *buffer, int bytes) {
|
|
static guchar count= 0, value= 0;
|
|
|
|
if (pcx_header.compression) {
|
|
while (bytes--) {
|
|
if (count == 0) {
|
|
value= fgetc(fp);
|
|
if (value < 0xc0) {
|
|
count= 1;
|
|
} else {
|
|
count= value - 0xc0;
|
|
value= fgetc(fp);
|
|
}
|
|
}
|
|
count--;
|
|
*(buffer++)= value;
|
|
}
|
|
} else {
|
|
fread(buffer, bytes, 1, fp);
|
|
}
|
|
}
|
|
|
|
gint save_image (char *filename, gint32 image, gint32 layer) {
|
|
FILE *fp;
|
|
GPixelRgn pixel_rgn;
|
|
GDrawable *drawable;
|
|
GDrawableType drawable_type;
|
|
guchar *cmap= 0, *pixels;
|
|
gint offset_x, offset_y, width, height;
|
|
gchar *message;
|
|
int colors, i;
|
|
|
|
drawable= gimp_drawable_get(layer);
|
|
drawable_type= gimp_drawable_type(layer);
|
|
gimp_drawable_offsets (layer, &offset_x, &offset_y);
|
|
width= drawable->width;
|
|
height= drawable->height;
|
|
gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);
|
|
|
|
message = g_malloc (strlen (filename) + 11);
|
|
sprintf (message, "Saving %s:", filename);
|
|
gimp_progress_init (message);
|
|
g_free (message);
|
|
|
|
pcx_header.manufacturer = 0x0a;
|
|
pcx_header.version = 5;
|
|
pcx_header.compression = 1;
|
|
|
|
switch(drawable_type) {
|
|
case INDEXED_IMAGE:
|
|
cmap= gimp_image_get_cmap(image, &colors);
|
|
pcx_header.bpp = 8;
|
|
pcx_header.bytesperline = htoqs (width);
|
|
pcx_header.planes = 1;
|
|
pcx_header.color = htoqs (1);
|
|
break;
|
|
|
|
case RGB_IMAGE:
|
|
pcx_header.bpp = 8;
|
|
pcx_header.planes = 3;
|
|
pcx_header.color = htoqs (1);
|
|
pcx_header.bytesperline = htoqs (width);
|
|
break;
|
|
|
|
case GRAY_IMAGE:
|
|
pcx_header.bpp = 8;
|
|
pcx_header.planes = 1;
|
|
pcx_header.color = htoqs (2);
|
|
pcx_header.bytesperline = htoqs (width);
|
|
break;
|
|
|
|
default:
|
|
g_message("PCX Can't save this image type\nFlatten your image");
|
|
return -1;
|
|
break;
|
|
}
|
|
|
|
if ((fp = fopen(filename, "wb")) == NULL) {
|
|
g_message("PCX Can't open \n%s", filename);
|
|
return -1;
|
|
}
|
|
|
|
pixels= (guchar *) g_malloc(width * height * pcx_header.planes);
|
|
gimp_pixel_rgn_get_rect(&pixel_rgn, pixels, 0, 0, width, height);
|
|
|
|
pcx_header.x1 = htoqs (offset_x);
|
|
pcx_header.y1 = htoqs (offset_y);
|
|
pcx_header.x2 = htoqs (offset_x + width - 1);
|
|
pcx_header.y2 = htoqs (offset_y + height - 1);
|
|
|
|
pcx_header.hdpi = htoqs (300);
|
|
pcx_header.vdpi = htoqs (300);
|
|
pcx_header.reserved = 0;
|
|
|
|
fwrite(&pcx_header, 128, 1, fp);
|
|
|
|
switch(drawable_type) {
|
|
case INDEXED_IMAGE:
|
|
save_8(fp, width, height, pixels);
|
|
fputc(0x0c, fp);
|
|
fwrite(cmap, colors, 3, fp);
|
|
for (i= colors; i < 256; i++) {
|
|
fputc(0, fp); fputc(0, fp); fputc(0, fp);
|
|
}
|
|
break;
|
|
case RGB_IMAGE:
|
|
save_24(fp, width, height, pixels);
|
|
break;
|
|
case GRAY_IMAGE:
|
|
save_8(fp, width, height, pixels);
|
|
fputc(0x0c, fp);
|
|
for (i= 0; i < 256; i++) {
|
|
fputc((guchar) i, fp); fputc((guchar) i, fp); fputc((guchar) i, fp);
|
|
}
|
|
break;
|
|
default:
|
|
g_message("Can't save this image as PCX\nFlatten your image");
|
|
return -1;
|
|
break;
|
|
}
|
|
|
|
gimp_drawable_detach(drawable);
|
|
g_free(pixels);
|
|
|
|
fclose(fp);
|
|
return TRUE;
|
|
}
|
|
|
|
static void save_8(FILE *fp, int width, int height, guchar *buffer) {
|
|
int row;
|
|
|
|
for (row= 0; row < height; ++row) {
|
|
writeline(fp, buffer, width);
|
|
buffer+= width;
|
|
gimp_progress_update((double) row / (double) height);
|
|
}
|
|
}
|
|
|
|
static void save_24(FILE *fp, int width, int height, guchar *buffer) {
|
|
int x, y, c;
|
|
guchar *line;
|
|
line= (guchar *) g_malloc(width);
|
|
|
|
for (y= 0; y < height; ++y) {
|
|
for (c= 0; c < 3; ++c) {
|
|
for (x= 0; x < width; ++x) {
|
|
line[x]= buffer[(3*x) + c];
|
|
}
|
|
writeline(fp, line, width);
|
|
}
|
|
buffer+= width * 3;
|
|
gimp_progress_update((double) y / (double) height);
|
|
}
|
|
g_free (line);
|
|
}
|
|
|
|
static void writeline(FILE *fp, guchar *buffer, int bytes) {
|
|
guchar value, count;
|
|
guchar *finish= buffer+ bytes;
|
|
|
|
while (buffer < finish) {
|
|
value= *(buffer++);
|
|
count= 1;
|
|
|
|
while (buffer < finish && count < 63 && *buffer == value) {
|
|
count++; buffer++;
|
|
}
|
|
|
|
if (value < 0xc0 && count == 1) {
|
|
fputc(value, fp);
|
|
} else {
|
|
fputc(0xc0 + count, fp);
|
|
fputc(value, fp);
|
|
}
|
|
}
|
|
}
|