~ubuntu-branches/ubuntu/jaunty/gimp/jaunty-security

« back to all changes in this revision

Viewing changes to plug-ins/common/file-pcx.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2008-10-06 13:30:41 UTC
  • mto: This revision was merged to the branch mainline in revision 35.
  • Revision ID: james.westby@ubuntu.com-20081006133041-3panbkcanaymfsmp
Tags: upstream-2.6.0
ImportĀ upstreamĀ versionĀ 2.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * pcx.c GIMP plug-in for loading & saving PCX files
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
17
 */
 
18
 
 
19
/* This code is based in parts on code by Francisco Bustamante, but the
 
20
   largest portion of the code has been rewritten and is now maintained
 
21
   occasionally by Nick Lamb njl195@zepler.org.uk */
 
22
 
 
23
#include "config.h"
 
24
 
 
25
#include <errno.h>
 
26
#include <string.h>
 
27
 
 
28
#include <glib/gstdio.h>
 
29
 
 
30
#include <libgimp/gimp.h>
 
31
#include <libgimp/gimpui.h>
 
32
 
 
33
#include "libgimp/stdplugins-intl.h"
 
34
 
 
35
 
 
36
#define LOAD_PROC      "file-pcx-load"
 
37
#define SAVE_PROC      "file-pcx-save"
 
38
#define PLUG_IN_BINARY "file-pcx"
 
39
 
 
40
/* Declare local functions.  */
 
41
 
 
42
static void   query      (void);
 
43
static void   run        (const gchar      *name,
 
44
                          gint              nparams,
 
45
                          const GimpParam  *param,
 
46
                          gint             *nreturn_vals,
 
47
                          GimpParam       **return_vals);
 
48
 
 
49
static gint32 load_image (const gchar      *filename,
 
50
                          GError          **error);
 
51
 
 
52
static void   load_1     (FILE             *fp,
 
53
                          gint              width,
 
54
                          gint              height,
 
55
                          guchar           *buffer,
 
56
                          guint16           bytes);
 
57
static void   load_4     (FILE             *fp,
 
58
                          gint              width,
 
59
                          gint              height,
 
60
                          guchar           *buffer,
 
61
                          guint16           bytes);
 
62
static void   load_8     (FILE             *fp,
 
63
                          gint              width,
 
64
                          gint              height,
 
65
                          guchar           *buffer,
 
66
                          guint16           bytes);
 
67
static void   load_24    (FILE             *fp,
 
68
                          gint              width,
 
69
                          gint              height,
 
70
                          guchar           *buffer,
 
71
                          guint16           bytes);
 
72
static void   readline   (FILE             *fp,
 
73
                          guchar           *buffer,
 
74
                          gint              bytes);
 
75
 
 
76
static gint   save_image (const gchar      *filename,
 
77
                          gint32            image,
 
78
                          gint32            layer,
 
79
                          GError          **error);
 
80
 
 
81
static void   save_8     (FILE             *fp,
 
82
                          gint              width,
 
83
                          gint              height,
 
84
                          guchar           *buffer);
 
85
static void   save_24    (FILE             *fp,
 
86
                          gint              width,
 
87
                          gint              height,
 
88
                          guchar           *buffer);
 
89
static void   writeline  (FILE             *fp,
 
90
                          guchar           *buffer,
 
91
                          gint              bytes);
 
92
 
 
93
 
 
94
const GimpPlugInInfo PLUG_IN_INFO =
 
95
{
 
96
  NULL,  /* init_proc  */
 
97
  NULL,  /* quit_proc  */
 
98
  query, /* query_proc */
 
99
  run,   /* run_proc   */
 
100
};
 
101
 
 
102
MAIN ()
 
103
 
 
104
static void
 
105
query (void)
 
106
{
 
107
  static const GimpParamDef load_args[] =
 
108
  {
 
109
    { GIMP_PDB_INT32,  "run-mode",     "Interactive, non-interactive" },
 
110
    { GIMP_PDB_STRING, "filename",     "The name of the file to load" },
 
111
    { GIMP_PDB_STRING, "raw-filename", "The name entered"             }
 
112
  };
 
113
  static const GimpParamDef load_return_vals[] =
 
114
  {
 
115
    { GIMP_PDB_IMAGE, "image", "Output image" }
 
116
  };
 
117
 
 
118
  static const GimpParamDef save_args[] =
 
119
  {
 
120
    { GIMP_PDB_INT32,    "run-mode",     "Interactive, non-interactive" },
 
121
    { GIMP_PDB_IMAGE,    "image",        "Input image"                  },
 
122
    { GIMP_PDB_DRAWABLE, "drawable",     "Drawable to save"             },
 
123
    { GIMP_PDB_STRING,   "filename",     "The name of the file to save the image in" },
 
124
    { GIMP_PDB_STRING,   "raw-filename", "The name entered"             }
 
125
  };
 
126
 
 
127
  gimp_install_procedure (LOAD_PROC,
 
128
                          "Loads files in Zsoft PCX file format",
 
129
                          "FIXME: write help for pcx_load",
 
130
                          "Francisco Bustamante & Nick Lamb",
 
131
                          "Nick Lamb <njl195@zepler.org.uk>",
 
132
                          "January 1997",
 
133
                          N_("ZSoft PCX image"),
 
134
                          NULL,
 
135
                          GIMP_PLUGIN,
 
136
                          G_N_ELEMENTS (load_args),
 
137
                          G_N_ELEMENTS (load_return_vals),
 
138
                          load_args, load_return_vals);
 
139
 
 
140
  gimp_register_file_handler_mime (LOAD_PROC, "image/x-pcx");
 
141
  gimp_register_magic_load_handler (LOAD_PROC,
 
142
                                    "pcx,pcc",
 
143
                                    "",
 
144
                                    "0&,byte,10,2&,byte,1,3&,byte,>0,3,byte,<9");
 
145
 
 
146
  gimp_install_procedure (SAVE_PROC,
 
147
                          "Saves files in ZSoft PCX file format",
 
148
                          "FIXME: write help for pcx_save",
 
149
                          "Francisco Bustamante & Nick Lamb",
 
150
                          "Nick Lamb <njl195@zepler.org.uk>",
 
151
                          "January 1997",
 
152
                          N_("ZSoft PCX image"),
 
153
                          "INDEXED, RGB, GRAY",
 
154
                          GIMP_PLUGIN,
 
155
                          G_N_ELEMENTS (save_args), 0,
 
156
                          save_args, NULL);
 
157
 
 
158
  gimp_register_file_handler_mime (SAVE_PROC, "image/x-pcx");
 
159
  gimp_register_save_handler (SAVE_PROC, "pcx,pcc", "");
 
160
}
 
161
 
 
162
static void
 
163
run (const gchar      *name,
 
164
     gint              nparams,
 
165
     const GimpParam  *param,
 
166
     gint             *nreturn_vals,
 
167
     GimpParam       **return_vals)
 
168
{
 
169
  static GimpParam   values[2];
 
170
  GimpRunMode        run_mode;
 
171
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
 
172
  gint32             image_ID;
 
173
  gint32             drawable_ID;
 
174
  GimpExportReturn   export = GIMP_EXPORT_CANCEL;
 
175
  GError            *error  = NULL;
 
176
 
 
177
  run_mode = param[0].data.d_int32;
 
178
 
 
179
  INIT_I18N ();
 
180
 
 
181
  *nreturn_vals = 1;
 
182
  *return_vals  = values;
 
183
 
 
184
  values[0].type          = GIMP_PDB_STATUS;
 
185
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
 
186
 
 
187
  if (strcmp (name, LOAD_PROC) == 0)
 
188
    {
 
189
      image_ID = load_image (param[1].data.d_string, &error);
 
190
 
 
191
      if (image_ID != -1)
 
192
        {
 
193
          *nreturn_vals = 2;
 
194
          values[1].type = GIMP_PDB_IMAGE;
 
195
          values[1].data.d_image = image_ID;
 
196
        }
 
197
      else
 
198
        {
 
199
          status = GIMP_PDB_EXECUTION_ERROR;
 
200
        }
 
201
    }
 
202
  else if (strcmp (name, SAVE_PROC) == 0)
 
203
    {
 
204
      image_ID    = param[1].data.d_int32;
 
205
      drawable_ID = param[2].data.d_int32;
 
206
 
 
207
      /*  eventually export the image */
 
208
      switch (run_mode)
 
209
        {
 
210
        case GIMP_RUN_INTERACTIVE:
 
211
        case GIMP_RUN_WITH_LAST_VALS:
 
212
          gimp_ui_init (PLUG_IN_BINARY, FALSE);
 
213
 
 
214
          export = gimp_export_image (&image_ID, &drawable_ID, "PCX",
 
215
                                      (GIMP_EXPORT_CAN_HANDLE_RGB |
 
216
                                       GIMP_EXPORT_CAN_HANDLE_GRAY |
 
217
                                       GIMP_EXPORT_CAN_HANDLE_INDEXED));
 
218
          if (export == GIMP_EXPORT_CANCEL)
 
219
            {
 
220
              values[0].data.d_status = GIMP_PDB_CANCEL;
 
221
              return;
 
222
            }
 
223
          break;
 
224
        default:
 
225
          break;
 
226
        }
 
227
 
 
228
      switch (run_mode)
 
229
        {
 
230
        case GIMP_RUN_INTERACTIVE:
 
231
          break;
 
232
 
 
233
        case GIMP_RUN_NONINTERACTIVE:
 
234
          if (nparams != 5)
 
235
            status = GIMP_PDB_CALLING_ERROR;
 
236
          break;
 
237
 
 
238
        case GIMP_RUN_WITH_LAST_VALS:
 
239
          break;
 
240
 
 
241
        default:
 
242
          break;
 
243
        }
 
244
 
 
245
      if (status == GIMP_PDB_SUCCESS)
 
246
        {
 
247
          if (! save_image (param[3].data.d_string, image_ID, drawable_ID,
 
248
                            &error))
 
249
            {
 
250
              status = GIMP_PDB_EXECUTION_ERROR;
 
251
            }
 
252
        }
 
253
 
 
254
      if (export == GIMP_EXPORT_EXPORT)
 
255
        gimp_image_delete (image_ID);
 
256
    }
 
257
  else
 
258
    {
 
259
      status = GIMP_PDB_CALLING_ERROR;
 
260
    }
 
261
 
 
262
  if (status != GIMP_PDB_SUCCESS && error)
 
263
    {
 
264
      *nreturn_vals = 2;
 
265
      values[1].type          = GIMP_PDB_STRING;
 
266
      values[1].data.d_string = error->message;
 
267
    }
 
268
 
 
269
  values[0].data.d_status = status;
 
270
}
 
271
 
 
272
static guchar mono[6]= { 0, 0, 0, 255, 255, 255 };
 
273
 
 
274
static struct
 
275
{
 
276
  guint8  manufacturer;
 
277
  guint8  version;
 
278
  guint8  compression;
 
279
  guint8  bpp;
 
280
  guint16 x1, y1;
 
281
  guint16 x2, y2;
 
282
  guint16 hdpi;
 
283
  guint16 vdpi;
 
284
  guint8  colormap[48];
 
285
  guint8  reserved;
 
286
  guint8  planes;
 
287
  guint16 bytesperline;
 
288
  guint16 color;
 
289
  guint8  filler[58];
 
290
} pcx_header;
 
291
 
 
292
static struct {
 
293
  size_t   size;
 
294
  gpointer address;
 
295
} pcx_header_buf_xlate[] = {
 
296
  { 1,  &pcx_header.manufacturer },
 
297
  { 1,  &pcx_header.version },
 
298
  { 1,  &pcx_header.compression },
 
299
  { 1,  &pcx_header.bpp },
 
300
  { 2,  &pcx_header.x1 },
 
301
  { 2,  &pcx_header.y1 },
 
302
  { 2,  &pcx_header.x2 },
 
303
  { 2,  &pcx_header.y2 },
 
304
  { 2,  &pcx_header.hdpi },
 
305
  { 2,  &pcx_header.vdpi },
 
306
  { 48, &pcx_header.colormap },
 
307
  { 1,  &pcx_header.reserved },
 
308
  { 1,  &pcx_header.planes },
 
309
  { 2,  &pcx_header.bytesperline },
 
310
  { 2,  &pcx_header.color },
 
311
  { 58, &pcx_header.filler },
 
312
  { 0,  NULL }
 
313
};
 
314
 
 
315
static void
 
316
pcx_header_from_buffer (guint8 *buf)
 
317
{
 
318
  gint i;
 
319
  gint buf_offset = 0;
 
320
 
 
321
  for (i = 0; pcx_header_buf_xlate[i].size != 0; i++)
 
322
    {
 
323
      g_memmove (pcx_header_buf_xlate[i].address, buf + buf_offset,
 
324
                 pcx_header_buf_xlate[i].size);
 
325
      buf_offset += pcx_header_buf_xlate[i].size;
 
326
    }
 
327
}
 
328
 
 
329
static void
 
330
pcx_header_to_buffer (guint8 *buf)
 
331
{
 
332
  gint i;
 
333
  gint buf_offset = 0;
 
334
 
 
335
  for (i = 0; pcx_header_buf_xlate[i].size != 0; i++)
 
336
    {
 
337
      g_memmove (buf + buf_offset, pcx_header_buf_xlate[i].address,
 
338
                 pcx_header_buf_xlate[i].size);
 
339
      buf_offset += pcx_header_buf_xlate[i].size;
 
340
    }
 
341
}
 
342
 
 
343
static gint32
 
344
load_image (const gchar  *filename,
 
345
            GError      **error)
 
346
{
 
347
  FILE         *fd;
 
348
  GimpDrawable *drawable;
 
349
  GimpPixelRgn  pixel_rgn;
 
350
  guint16       offset_x, offset_y, bytesperline;
 
351
  gint32        width, height;
 
352
  gint32        image, layer;
 
353
  guchar       *dest, cmap[768];
 
354
  guint8        header_buf[128];
 
355
 
 
356
  fd = g_fopen (filename, "rb");
 
357
 
 
358
  if (! fd)
 
359
    {
 
360
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
 
361
                   _("Could not open '%s' for reading: %s"),
 
362
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
 
363
      return -1;
 
364
    }
 
365
 
 
366
  gimp_progress_init_printf (_("Opening '%s'"),
 
367
                             gimp_filename_to_utf8 (filename));
 
368
 
 
369
  if (fread (header_buf, 128, 1, fd) == 0)
 
370
    {
 
371
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
 
372
                   _("Could not read header from '%s'"),
 
373
                   gimp_filename_to_utf8 (filename));
 
374
      return -1;
 
375
    }
 
376
 
 
377
  pcx_header_from_buffer (header_buf);
 
378
 
 
379
  if (pcx_header.manufacturer != 10)
 
380
    {
 
381
      g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
 
382
                   _("'%s' is not a PCX file"),
 
383
                   gimp_filename_to_utf8 (filename));
 
384
      return -1;
 
385
    }
 
386
 
 
387
  offset_x = GUINT16_FROM_LE (pcx_header.x1);
 
388
  offset_y = GUINT16_FROM_LE (pcx_header.y1);
 
389
  width = GUINT16_FROM_LE (pcx_header.x2) - offset_x + 1;
 
390
  height = GUINT16_FROM_LE (pcx_header.y2) - offset_y + 1;
 
391
  bytesperline = GUINT16_FROM_LE (pcx_header.bytesperline);
 
392
 
 
393
  if ((width < 0) || (width > GIMP_MAX_IMAGE_SIZE))
 
394
    {
 
395
      g_message (_("Unsupported or invalid image width: %d"), width);
 
396
      return -1;
 
397
    }
 
398
  if ((height < 0) || (height > GIMP_MAX_IMAGE_SIZE))
 
399
    {
 
400
      g_message (_("Unsupported or invalid image height: %d"), height);
 
401
      return -1;
 
402
    }
 
403
  if (bytesperline < (width * pcx_header.bpp) / 8)
 
404
    {
 
405
      g_message (_("Invalid number of bytes per line in PCX header"));
 
406
      return -1;
 
407
    }
 
408
 
 
409
  if (pcx_header.planes == 3 && pcx_header.bpp == 8)
 
410
    {
 
411
      image= gimp_image_new (width, height, GIMP_RGB);
 
412
      layer= gimp_layer_new (image, _("Background"), width, height,
 
413
                             GIMP_RGB_IMAGE, 100, GIMP_NORMAL_MODE);
 
414
    }
 
415
  else
 
416
    {
 
417
      image= gimp_image_new (width, height, GIMP_INDEXED);
 
418
      layer= gimp_layer_new (image, _("Background"), width, height,
 
419
                             GIMP_INDEXED_IMAGE, 100, GIMP_NORMAL_MODE);
 
420
    }
 
421
  gimp_image_set_filename (image, filename);
 
422
  gimp_image_add_layer (image, layer, 0);
 
423
  gimp_layer_set_offsets (layer, offset_x, offset_y);
 
424
  drawable = gimp_drawable_get (layer);
 
425
 
 
426
  if (pcx_header.planes == 1 && pcx_header.bpp == 1)
 
427
    {
 
428
      dest = (guchar *) g_malloc (width * height);
 
429
      load_1 (fd, width, height, dest, bytesperline);
 
430
      gimp_image_set_colormap (image, mono, 2);
 
431
    }
 
432
  else if (pcx_header.planes == 4 && pcx_header.bpp == 1)
 
433
    {
 
434
      dest = (guchar *) g_malloc (width * height);
 
435
      load_4 (fd, width, height, dest, bytesperline);
 
436
      gimp_image_set_colormap (image, pcx_header.colormap, 16);
 
437
    }
 
438
  else if (pcx_header.planes == 1 && pcx_header.bpp == 8)
 
439
    {
 
440
      dest = (guchar *) g_malloc (width * height);
 
441
      load_8 (fd, width, height, dest, bytesperline);
 
442
      fseek (fd, -768L, SEEK_END);
 
443
      fread (cmap, 768, 1, fd);
 
444
      gimp_image_set_colormap (image, cmap, 256);
 
445
    }
 
446
  else if (pcx_header.planes == 3 && pcx_header.bpp == 8)
 
447
    {
 
448
      dest = (guchar *) g_malloc (width * height * 3);
 
449
      load_24 (fd, width, height, dest, bytesperline);
 
450
    }
 
451
  else
 
452
    {
 
453
      g_message (_("Unusual PCX flavour, giving up"));
 
454
      return -1;
 
455
    }
 
456
 
 
457
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, TRUE, FALSE);
 
458
  gimp_pixel_rgn_set_rect (&pixel_rgn, dest, 0, 0, width, height);
 
459
 
 
460
  g_free (dest);
 
461
 
 
462
  gimp_drawable_flush (drawable);
 
463
  gimp_drawable_detach (drawable);
 
464
 
 
465
  return image;
 
466
}
 
467
 
 
468
static void
 
469
load_8 (FILE    *fp,
 
470
        gint     width,
 
471
        gint     height,
 
472
        guchar  *buffer,
 
473
        guint16  bytes)
 
474
{
 
475
  gint    row;
 
476
  guchar *line = g_new (guchar, bytes);
 
477
 
 
478
  for (row = 0; row < height; buffer += width, ++row)
 
479
    {
 
480
      readline (fp, line, bytes);
 
481
      memcpy (buffer, line, width);
 
482
      gimp_progress_update ((double) row / (double) height);
 
483
    }
 
484
 
 
485
  g_free (line);
 
486
}
 
487
 
 
488
static void
 
489
load_24 (FILE    *fp,
 
490
         gint     width,
 
491
         gint     height,
 
492
         guchar  *buffer,
 
493
         guint16  bytes)
 
494
{
 
495
  gint    x, y, c;
 
496
  guchar *line = g_new (guchar, bytes);
 
497
 
 
498
  for (y = 0; y < height; buffer += width * 3, ++y)
 
499
    {
 
500
      for (c = 0; c < 3; ++c)
 
501
        {
 
502
          readline (fp, line, bytes);
 
503
          for (x = 0; x < width; ++x)
 
504
            {
 
505
              buffer[x * 3 + c] = line[x];
 
506
            }
 
507
        }
 
508
      gimp_progress_update ((double) y / (double) height);
 
509
    }
 
510
 
 
511
  g_free (line);
 
512
}
 
513
 
 
514
static void
 
515
load_1 (FILE    *fp,
 
516
        gint     width,
 
517
        gint     height,
 
518
        guchar  *buffer,
 
519
        guint16  bytes)
 
520
{
 
521
  gint    x, y;
 
522
  guchar *line = g_new (guchar, bytes);
 
523
 
 
524
  for (y = 0; y < height; buffer += width, ++y)
 
525
    {
 
526
      readline (fp, line, bytes);
 
527
      for (x = 0; x < width; ++x)
 
528
        {
 
529
          if (line[x / 8] & (128 >> (x % 8)))
 
530
            buffer[x] = 1;
 
531
          else
 
532
            buffer[x] = 0;
 
533
        }
 
534
      gimp_progress_update ((double) y / (double) height);
 
535
    }
 
536
 
 
537
  g_free (line);
 
538
}
 
539
 
 
540
static void
 
541
load_4 (FILE    *fp,
 
542
        gint     width,
 
543
        gint     height,
 
544
        guchar  *buffer,
 
545
        guint16  bytes)
 
546
{
 
547
  gint    x, y, c;
 
548
  guchar *line = g_new (guchar, bytes);
 
549
 
 
550
  for (y = 0; y < height; buffer += width, ++y)
 
551
    {
 
552
      for (x = 0; x < width; ++x)
 
553
        buffer[x] = 0;
 
554
      for (c = 0; c < 4; ++c)
 
555
        {
 
556
          readline(fp, line, bytes);
 
557
          for (x = 0; x < width; ++x)
 
558
            {
 
559
              if (line[x / 8] & (128 >> (x % 8)))
 
560
                buffer[x] += (1 << c);
 
561
            }
 
562
        }
 
563
      gimp_progress_update ((double) y / (double) height);
 
564
    }
 
565
 
 
566
  g_free (line);
 
567
}
 
568
 
 
569
static void
 
570
readline (FILE   *fp,
 
571
          guchar *buffer,
 
572
          gint    bytes)
 
573
{
 
574
  static guchar count = 0, value = 0;
 
575
 
 
576
  if (pcx_header.compression)
 
577
    {
 
578
      while (bytes--)
 
579
        {
 
580
          if (count == 0)
 
581
            {
 
582
              value = fgetc (fp);
 
583
              if (value < 0xc0)
 
584
                {
 
585
                  count = 1;
 
586
                }
 
587
              else
 
588
                {
 
589
                  count = value - 0xc0;
 
590
                  value = fgetc (fp);
 
591
                }
 
592
            }
 
593
          count--;
 
594
          *(buffer++) = value;
 
595
        }
 
596
    }
 
597
  else
 
598
    {
 
599
      fread (buffer, bytes, 1, fp);
 
600
    }
 
601
}
 
602
 
 
603
static gint
 
604
save_image (const gchar  *filename,
 
605
            gint32        image,
 
606
            gint32        layer,
 
607
            GError      **error)
 
608
{
 
609
  FILE          *fp;
 
610
  GimpPixelRgn   pixel_rgn;
 
611
  GimpDrawable  *drawable;
 
612
  GimpImageType  drawable_type;
 
613
  guchar        *cmap= NULL;
 
614
  guchar        *pixels;
 
615
  gint           offset_x, offset_y;
 
616
  guint          width, height;
 
617
  gint           colors, i;
 
618
  guint8         header_buf[128];
 
619
 
 
620
  drawable = gimp_drawable_get (layer);
 
621
  drawable_type = gimp_drawable_type (layer);
 
622
  gimp_drawable_offsets (layer, &offset_x, &offset_y);
 
623
  width = drawable->width;
 
624
  height = drawable->height;
 
625
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);
 
626
 
 
627
  gimp_progress_init_printf (_("Saving '%s'"),
 
628
                             gimp_filename_to_utf8 (filename));
 
629
 
 
630
  pcx_header.manufacturer = 0x0a;
 
631
  pcx_header.version = 5;
 
632
  pcx_header.compression = 1;
 
633
 
 
634
  switch (drawable_type)
 
635
    {
 
636
    case GIMP_INDEXED_IMAGE:
 
637
      cmap = gimp_image_get_colormap (image, &colors);
 
638
      pcx_header.bpp = 8;
 
639
      pcx_header.bytesperline = GUINT16_TO_LE (width);
 
640
      pcx_header.planes = 1;
 
641
      pcx_header.color = GUINT16_TO_LE (1);
 
642
      break;
 
643
 
 
644
    case GIMP_RGB_IMAGE:
 
645
      pcx_header.bpp = 8;
 
646
      pcx_header.planes = 3;
 
647
      pcx_header.color = GUINT16_TO_LE (1);
 
648
      pcx_header.bytesperline = GUINT16_TO_LE (width);
 
649
      break;
 
650
 
 
651
    case GIMP_GRAY_IMAGE:
 
652
      pcx_header.bpp = 8;
 
653
      pcx_header.planes = 1;
 
654
      pcx_header.color = GUINT16_TO_LE (2);
 
655
      pcx_header.bytesperline = GUINT16_TO_LE (width);
 
656
      break;
 
657
 
 
658
    default:
 
659
      g_message (_("Cannot save images with alpha channel."));
 
660
      return FALSE;
 
661
  }
 
662
 
 
663
  if ((fp = g_fopen (filename, "wb")) == NULL)
 
664
    {
 
665
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
 
666
                   _("Could not open '%s' for writing: %s"),
 
667
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
 
668
      return FALSE;
 
669
    }
 
670
 
 
671
  pixels = (guchar *) g_malloc (width * height * pcx_header.planes);
 
672
  gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, 0, width, height);
 
673
 
 
674
  if ((offset_x < 0) || (offset_x > (1<<16)))
 
675
    {
 
676
      g_message (_("Invalid X offset: %d"), offset_x);
 
677
      return FALSE;
 
678
    }
 
679
 
 
680
  if ((offset_y < 0) || (offset_y > (1<<16)))
 
681
    {
 
682
      g_message (_("Invalid Y offset: %d"), offset_y);
 
683
      return FALSE;
 
684
    }
 
685
 
 
686
  if (offset_x + width - 1 > (1<<16))
 
687
    {
 
688
      g_message (_("Right border out of bounds (must be < %d): %d"), (1<<16),
 
689
                 offset_x + width - 1);
 
690
      return FALSE;
 
691
    }
 
692
 
 
693
  if (offset_y + height - 1 > (1<<16))
 
694
    {
 
695
      g_message (_("Bottom border out of bounds (must be < %d): %d"), (1<<16),
 
696
                 offset_y + height - 1);
 
697
      return FALSE;
 
698
    }
 
699
 
 
700
  pcx_header.x1 = GUINT16_TO_LE ((guint16)offset_x);
 
701
  pcx_header.y1 = GUINT16_TO_LE ((guint16)offset_y);
 
702
  pcx_header.x2 = GUINT16_TO_LE ((guint16)(offset_x + width - 1));
 
703
  pcx_header.y2 = GUINT16_TO_LE ((guint16)(offset_y + height - 1));
 
704
 
 
705
  pcx_header.hdpi = GUINT16_TO_LE (300);
 
706
  pcx_header.vdpi = GUINT16_TO_LE (300);
 
707
  pcx_header.reserved = 0;
 
708
 
 
709
  pcx_header_to_buffer (header_buf);
 
710
 
 
711
  fwrite (header_buf, 128, 1, fp);
 
712
 
 
713
  switch (drawable_type)
 
714
    {
 
715
    case GIMP_INDEXED_IMAGE:
 
716
      save_8 (fp, width, height, pixels);
 
717
      fputc (0x0c, fp);
 
718
      fwrite (cmap, colors, 3, fp);
 
719
      for (i = colors; i < 256; i++)
 
720
        {
 
721
          fputc (0, fp);
 
722
          fputc (0, fp);
 
723
          fputc (0, fp);
 
724
        }
 
725
      break;
 
726
 
 
727
    case GIMP_RGB_IMAGE:
 
728
      save_24 (fp, width, height, pixels);
 
729
      break;
 
730
 
 
731
    case GIMP_GRAY_IMAGE:
 
732
      save_8 (fp, width, height, pixels);
 
733
      fputc (0x0c, fp);
 
734
      for (i = 0; i < 256; i++)
 
735
        {
 
736
          fputc ((guchar) i, fp);
 
737
          fputc ((guchar) i, fp);
 
738
          fputc ((guchar) i, fp);
 
739
        }
 
740
      break;
 
741
 
 
742
    default:
 
743
      return FALSE;
 
744
    }
 
745
 
 
746
  gimp_drawable_detach (drawable);
 
747
  g_free (pixels);
 
748
 
 
749
  if (fclose (fp) != 0)
 
750
    {
 
751
      g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
 
752
                   _("Writing to file '%s' failed: %s"),
 
753
                   gimp_filename_to_utf8 (filename), g_strerror (errno));
 
754
      return FALSE;
 
755
    }
 
756
 
 
757
  return TRUE;
 
758
}
 
759
 
 
760
static void
 
761
save_8 (FILE   *fp,
 
762
        gint    width,
 
763
        gint    height,
 
764
        guchar *buffer)
 
765
{
 
766
  int row;
 
767
 
 
768
  for (row = 0; row < height; ++row)
 
769
    {
 
770
      writeline (fp, buffer, width);
 
771
      buffer += width;
 
772
      gimp_progress_update ((double) row / (double) height);
 
773
    }
 
774
}
 
775
 
 
776
static void
 
777
save_24 (FILE   *fp,
 
778
         gint    width,
 
779
         gint    height,
 
780
         guchar *buffer)
 
781
{
 
782
  int     x, y, c;
 
783
  guchar *line;
 
784
 
 
785
  line = (guchar *) g_malloc (width);
 
786
 
 
787
  for (y = 0; y < height; ++y)
 
788
    {
 
789
      for (c = 0; c < 3; ++c)
 
790
        {
 
791
          for (x = 0; x < width; ++x)
 
792
            {
 
793
              line[x] = buffer[(3*x) + c];
 
794
            }
 
795
          writeline (fp, line, width);
 
796
        }
 
797
      buffer += width * 3;
 
798
      gimp_progress_update ((double) y / (double) height);
 
799
    }
 
800
  g_free (line);
 
801
}
 
802
 
 
803
static void
 
804
writeline (FILE   *fp,
 
805
           guchar *buffer,
 
806
           gint    bytes)
 
807
{
 
808
  guchar  value, count;
 
809
  guchar *finish = buffer + bytes;
 
810
 
 
811
  while (buffer < finish)
 
812
    {
 
813
      value = *(buffer++);
 
814
      count = 1;
 
815
 
 
816
      while (buffer < finish && count < 63 && *buffer == value)
 
817
        {
 
818
          count++; buffer++;
 
819
        }
 
820
 
 
821
      if (value < 0xc0 && count == 1)
 
822
        {
 
823
          fputc (value, fp);
 
824
        }
 
825
      else
 
826
        {
 
827
          fputc (0xc0 + count, fp);
 
828
          fputc (value, fp);
 
829
        }
 
830
    }
 
831
}