file-pix.c 22.1 KB
Newer Older
1
/* GIMP - The GNU Image Manipulation Program
Elliot Lee's avatar
Elliot Lee committed
2
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3
 * Alias|Wavefront pix/matte image reading and writing code
Elliot Lee's avatar
Elliot Lee committed
4
5
6
 * Copyright (C) 1997 Mike Taylor
 * (email: mtaylor@aw.sgi.com, WWW: http://reality.sgi.com/mtaylor)
 *
7
 * This program is free software: you can redistribute it and/or modify
Elliot Lee's avatar
Elliot Lee committed
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 3 of the License, or
Elliot Lee's avatar
Elliot Lee committed
10
11
12
13
14
15
16
17
 * (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
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
19
20
21
 *
 */

22
/* This plug-in was written using the online documentation from
Elliot Lee's avatar
Elliot Lee committed
23
24
25
26
27
28
29
 * Alias|Wavefront Inc's PowerAnimator product.
 *
 * Bug reports or suggestions should be e-mailed to mtaylor@aw.sgi.com
 */

/* Event history:
 * V 1.0, MT, 02-Jul-97: initial version of plug-in
30
 * V 1.1, MT, 04-Dec-97: added .als file extension
Elliot Lee's avatar
Elliot Lee committed
31
32
33
 */

/* Features
34
 *  - loads and exports
35
 *    - 24-bit (.pix)
Elliot Lee's avatar
Elliot Lee committed
36
37
38
 *    - 8-bit (.matte, .alpha, or .mask) images
 *
 * NOTE: pix and matte files do not support alpha channels or indexed
39
 *       color, so neither does this plug-in
Elliot Lee's avatar
Elliot Lee committed
40
 */
41
42
43

#include "config.h"

44
#include <errno.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
45
#include <string.h>
46

47
#include <glib/gstdio.h>
48
49
50
51

#include <libgimp/gimp.h>
#include <libgimp/gimpui.h>

52
#include "libgimp/stdplugins-intl.h"
Elliot Lee's avatar
Elliot Lee committed
53

54

55
56
#define LOAD_PROC      "file-pix-load"
#define SAVE_PROC      "file-pix-save"
57
#define PLUG_IN_BINARY "file-pix"
58
#define PLUG_IN_ROLE   "gimp-file-pix"
59
60


Elliot Lee's avatar
Elliot Lee committed
61
62
63
/* #define PIX_DEBUG */

#ifdef PIX_DEBUG
64
#    define PIX_DEBUG_PRINT(a,b) g_printerr (a,b)
Elliot Lee's avatar
Elliot Lee committed
65
#else
66
#    define PIX_DEBUG_PRINT(a,b)
Elliot Lee's avatar
Elliot Lee committed
67
68
#endif

69

70
71
72
73
74
75
76
77
78
typedef struct _Pix      Pix;
typedef struct _PixClass PixClass;

struct _Pix
{
  GimpPlugIn      parent_instance;
};

struct _PixClass
Elliot Lee's avatar
Elliot Lee committed
79
{
80
  GimpPlugInClass parent_class;
Elliot Lee's avatar
Elliot Lee committed
81
82
};

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

#define PIX_TYPE  (pix_get_type ())
#define PIX (obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PIX_TYPE, Pix))

GType                   pix_get_type         (void) G_GNUC_CONST;

static GList          * pix_query_procedures (GimpPlugIn           *plug_in);
static GimpProcedure  * pix_create_procedure (GimpPlugIn           *plug_in,
                                              const gchar          *name);

static GimpValueArray * pix_load             (GimpProcedure        *procedure,
                                              GimpRunMode           run_mode,
                                              GFile                *file,
                                              const GimpValueArray *args,
                                              gpointer              run_data);
static GimpValueArray * pix_save             (GimpProcedure        *procedure,
                                              GimpRunMode           run_mode,
                                              GimpImage            *image,
101
102
                                              gint                  n_drawables,
                                              GimpDrawable        **drawables,
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
                                              GFile                *file,
                                              const GimpValueArray *args,
                                              gpointer              run_data);

static GimpImage      * load_image           (GFile                *file,
                                              GError              **error);
static gboolean         save_image           (GFile                *file,
                                              GimpImage            *image,
                                              GimpDrawable         *drawable,
                                              GError              **error);

static gboolean         get_short            (GInputStream         *input,
                                              guint16              *value,
                                              GError              **error);
static gboolean         put_short            (GOutputStream        *output,
                                              guint16               value,
                                              GError              **error);


G_DEFINE_TYPE (Pix, pix, GIMP_TYPE_PLUG_IN)

GIMP_MAIN (PIX_TYPE)
125
DEFINE_STD_SET_I18N
126

Elliot Lee's avatar
Elliot Lee committed
127

128
static void
129
pix_class_init (PixClass *klass)
Elliot Lee's avatar
Elliot Lee committed
130
{
131
  GimpPlugInClass *plug_in_class = GIMP_PLUG_IN_CLASS (klass);
Elliot Lee's avatar
Elliot Lee committed
132

133
134
  plug_in_class->query_procedures = pix_query_procedures;
  plug_in_class->create_procedure = pix_create_procedure;
135
  plug_in_class->set_i18n         = STD_SET_I18N;
136
}
137

138
static void
139
pix_init (Pix *pix)
Elliot Lee's avatar
Elliot Lee committed
140
{
141
}
142

143
144
145
146
static GList *
pix_query_procedures (GimpPlugIn *plug_in)
{
  GList *list = NULL;
147

148
149
  list = g_list_append (list, g_strdup (LOAD_PROC));
  list = g_list_append (list, g_strdup (SAVE_PROC));
150

151
152
  return list;
}
153

154
155
156
157
158
static GimpProcedure *
pix_create_procedure (GimpPlugIn  *plug_in,
                      const gchar *name)
{
  GimpProcedure *procedure = NULL;
159

160
  if (! strcmp (name, LOAD_PROC))
161
    {
162
163
      procedure = gimp_load_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
164
165
166
167
168
                                           pix_load, NULL, NULL);

      gimp_file_procedure_set_handles_remote (GIMP_FILE_PROCEDURE (procedure),
                                              TRUE);

169
      gimp_procedure_set_menu_label (procedure, _("Alias Pix image"));
170
171
172
173
174
175
176
177
178
179
180
181
182
183

      gimp_procedure_set_documentation (procedure,
                                        "Loads files of the Alias|Wavefront "
                                        "Pix file format",
                                        "Loads files of the Alias|Wavefront "
                                        "Pix file format",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Michael Taylor",
                                      "Michael Taylor",
                                      "1997");

      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "pix,matte,mask,alpha,als");
184
    }
185
  else if (! strcmp (name, SAVE_PROC))
186
    {
187
188
      procedure = gimp_save_procedure_new (plug_in, name,
                                           GIMP_PDB_PROC_TYPE_PLUGIN,
189
                                           pix_save, NULL, NULL);
190

191
      gimp_procedure_set_image_types (procedure, "*");
192

193
194
      gimp_file_procedure_set_handles_remote (GIMP_FILE_PROCEDURE (procedure),
                                              TRUE);
195

196
      gimp_procedure_set_menu_label (procedure, _("Alias Pix image"));
197

198
199
200
201
202
203
204
205
206
207
      gimp_procedure_set_documentation (procedure,
                                        "Export file in the Alias|Wavefront "
                                        "pix/matte file format",
                                        "Export file in the Alias|Wavefront "
                                        "pix/matte file format",
                                        name);
      gimp_procedure_set_attribution (procedure,
                                      "Michael Taylor",
                                      "Michael Taylor",
                                      "1997");
208

209
210
      gimp_file_procedure_set_extensions (GIMP_FILE_PROCEDURE (procedure),
                                          "pix,matte,mask,alpha,als");
211
    }
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

  return procedure;
}

static GimpValueArray *
pix_load (GimpProcedure        *procedure,
          GimpRunMode           run_mode,
          GFile                *file,
          const GimpValueArray *args,
          gpointer              run_data)
{
  GimpValueArray *return_vals;
  GimpImage      *image;
  GError         *error = NULL;

  gegl_init (NULL, NULL);

  image = load_image (file, &error);

  if (! image)
    return gimp_procedure_new_return_values (procedure,
                                             GIMP_PDB_EXECUTION_ERROR,
                                             error);

  return_vals = gimp_procedure_new_return_values (procedure,
                                                  GIMP_PDB_SUCCESS,
                                                  NULL);

  GIMP_VALUES_SET_IMAGE (return_vals, 1, image);

  return return_vals;
}

static GimpValueArray *
pix_save (GimpProcedure        *procedure,
          GimpRunMode           run_mode,
          GimpImage            *image,
249
250
          gint                  n_drawables,
          GimpDrawable        **drawables,
251
252
253
254
255
256
257
258
259
260
261
          GFile                *file,
          const GimpValueArray *args,
          gpointer              run_data)
{
  GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
  GimpExportReturn   export = GIMP_EXPORT_CANCEL;
  GError            *error  = NULL;

  gegl_init (NULL, NULL);

  switch (run_mode)
262
    {
263
264
    case GIMP_RUN_INTERACTIVE:
    case GIMP_RUN_WITH_LAST_VALS:
265
      gimp_ui_init (PLUG_IN_BINARY);
266

267
      export = gimp_export_image (&image, &n_drawables, &drawables, "PIX",
268
269
270
271
272
273
274
275
276
277
278
279
                                  GIMP_EXPORT_CAN_HANDLE_RGB  |
                                  GIMP_EXPORT_CAN_HANDLE_GRAY |
                                  GIMP_EXPORT_CAN_HANDLE_INDEXED);

      if (export == GIMP_EXPORT_CANCEL)
        return gimp_procedure_new_return_values (procedure,
                                                 GIMP_PDB_CANCEL,
                                                 NULL);
      break;

    default:
      break;
280
    }
281

282
283
284
285
286
287
288
289
290
291
292
  if (n_drawables != 1)
    {
      g_set_error (&error, G_FILE_ERROR, 0,
                   _("PIX format does not support multiple layers."));

      return gimp_procedure_new_return_values (procedure,
                                               GIMP_PDB_CALLING_ERROR,
                                               error);
    }

  if (! save_image (file, image, drawables[0], &error))
293
    {
294
      status = GIMP_PDB_EXECUTION_ERROR;
295
296
    }

297
  if (export == GIMP_EXPORT_EXPORT)
298
299
300
301
    {
      gimp_image_delete (image);
      g_free (drawables);
    }
Elliot Lee's avatar
Elliot Lee committed
302

303
304
  return gimp_procedure_new_return_values (procedure, status, error);
}
305
306

/*
307
308
 * Description:
 *     Reads a 16-bit integer from a file in such a way that the machine's
309
 *     byte order should not matter.
310
311
 */

312
313
314
315
static gboolean
get_short (GInputStream  *input,
           guint16       *value,
           GError       **error)
Elliot Lee's avatar
Elliot Lee committed
316
{
317
  guchar buf[2];
318
319
320
321
322
323
324
325
  gsize  bytes_read;

  if (! g_input_stream_read_all (input, buf, 2,
                                 &bytes_read, NULL, error) ||
      bytes_read != 2)
    {
      return FALSE;
    }
326

327
328
  if (value)
    *value = (buf[0] << 8) + buf[1];
329

330
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
331
}
332
333

/*
334
 * Description:
335
336
 *     Writes a 16-bit integer to a file in such a way that the machine's
 *     byte order should not matter.
337
338
 */

339
340
341
342
static gboolean
put_short (GOutputStream  *output,
           guint16         value,
           GError        **error)
Elliot Lee's avatar
Elliot Lee committed
343
{
344
  guchar buf[2];
345

346
347
  buf[0] = (value >> 8) & 0xFF;
  buf[1] = value & 0xFF;
Elliot Lee's avatar
Elliot Lee committed
348

349
  return g_output_stream_write_all (output, buf, 2, NULL, NULL, error);
350
}
Elliot Lee's avatar
Elliot Lee committed
351

352
353
354
/*
 *  Description:
 *      load the given image into gimp
355
 *
356
357
358
359
360
 *  Arguments:
 *      filename      - name on the file to read
 *
 *  Return Value:
 *      Image id for the loaded image
361
 *
362
363
 */

364
static GimpImage *
365
366
load_image (GFile   *file,
            GError **error)
Elliot Lee's avatar
Elliot Lee committed
367
{
368
  GInputStream      *input;
369
  GeglBuffer        *buffer;
Sven Neumann's avatar
Sven Neumann committed
370
  GimpImageBaseType  imgtype;
371
372
373
  GimpImageType      gdtype;
  guchar            *dest;
  guchar            *dest_base;
374
375
  GimpImage         *image;
  GimpLayer         *layer;
376
377
  gushort            width, height, depth;
  gint               i, j, tile_height, row;
378

379
  PIX_DEBUG_PRINT ("Opening file: %s\n", gimp_file_get_utf8_name (file));
380

381
382
383
  gimp_progress_init_printf (_("Opening '%s'"),
                             g_file_get_parse_name (file));

384
385
  input = G_INPUT_STREAM (g_file_read (file, NULL, error));
  if (! input)
386
    return NULL;
387

388
  /* Read header information */
389
390
391
392
393
394
395
  if (! get_short (input, &width,  error) ||
      ! get_short (input, &height, error) ||
      ! get_short (input, NULL,    error) || /* Discard obsolete field */
      ! get_short (input, NULL,    error) || /* Discard obsolete field */
      ! get_short (input, &depth,  error))
    {
      g_object_unref (input);
396
      return NULL;
397
    }
398

399
  PIX_DEBUG_PRINT ("Width %hu\n",  width);
400
401
402
  PIX_DEBUG_PRINT ("Height %hu\n", height);

  if (depth == 8)
403
404
    {
      /* Loading a matte file */
Sven Neumann's avatar
Sven Neumann committed
405
      imgtype = GIMP_GRAY;
406
      gdtype  = GIMP_GRAY_IMAGE;
407
408
    }
  else if (depth == 24)
409
410
    {
      /* Loading an RGB file */
Sven Neumann's avatar
Sven Neumann committed
411
      imgtype = GIMP_RGB;
412
      gdtype  = GIMP_RGB_IMAGE;
413
414
    }
  else
415
416
    {
      /* Header is invalid */
417
      g_object_unref (input);
418
      return NULL;
419
    }
420

421
  image = gimp_image_new (width, height, imgtype);
422
  gimp_image_set_file (image, file);
423

424
425
426
427
428
429
  layer = gimp_layer_new (image, _("Background"),
                          width, height,
                          gdtype,
                          100,
                          gimp_image_get_default_new_layer_mode (image));
  gimp_image_insert_layer (image, layer, NULL, 0);
430

431
  buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
432

433
  tile_height = gimp_tile_height ();
434
435

  if (depth == 24)
436
437
    {
      /* Read a 24-bit Pix image */
438

439
      dest_base = dest = g_new (guchar, 3 * width * tile_height);
440
441

      for (i = 0; i < height;)
David Odin's avatar
David Odin committed
442
443
444
445
446
        {
          for (dest = dest_base, row = 0;
               row < tile_height && i < height;
               i++, row++)
            {
447
448
              guchar record[4];
              gsize  bytes_read;
David Odin's avatar
David Odin committed
449
450
451
452
453
454
              guchar count;

              /* Read a row of the image */
              j = 0;
              while (j < width)
                {
455
456
457
                  if (! g_input_stream_read_all (input, record, 4,
                                                 &bytes_read, NULL, error) ||
                      bytes_read != 4)
David Odin's avatar
David Odin committed
458
459
460
461
462
463
464
465
466
467
468
469
470
471
                    break;

                  for (count = 0; count < record[0]; ++count)
                    {
                      dest[0]   = record[3];
                      dest[1]   = record[2];
                      dest[2]   = record[1];
                      dest += 3;
                      j++;
                      if (j >= width)
                        break;
                    }
                }
            }
472
473
474
475

          gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
                           NULL, dest_base, GEGL_AUTO_ROWSTRIDE);

David Odin's avatar
David Odin committed
476
477
          gimp_progress_update ((double) i / (double) height);
        }
478

479
      g_free (dest_base);
480
481
    }
  else
482
483
484
    {
      /* Read an 8-bit Matte image */

485
486
487
      dest_base = dest = g_new (guchar, width * tile_height);

      for (i = 0; i < height;)
David Odin's avatar
David Odin committed
488
489
490
491
492
        {
          for (dest = dest_base, row = 0;
               row < tile_height && i < height;
               i++, row++)
            {
493
494
              guchar record[2];
              gsize  bytes_read;
David Odin's avatar
David Odin committed
495
496
497
498
499
500
              guchar count;

              /* Read a row of the image */
              j = 0;
              while (j < width)
                {
501
502
503
                  if (! g_input_stream_read_all (input, record, 2,
                                                 &bytes_read, NULL, error) ||
                      bytes_read != 2)
David Odin's avatar
David Odin committed
504
505
506
507
508
509
510
511
512
513
                    break;

                  for (count = 0; count < record[0]; ++count)
                    {
                      dest[j]   = record[1];
                      j++;
                      if (j >= width)
                        break;
                    }
                }
514

David Odin's avatar
David Odin committed
515
516
              dest += width;
            }
517
518
519
520

          gegl_buffer_set (buffer, GEGL_RECTANGLE (0, i - row, width, row), 0,
                           NULL, dest_base, GEGL_AUTO_ROWSTRIDE);

David Odin's avatar
David Odin committed
521
522
          gimp_progress_update ((double) i / (double) height);
        }
523

524
      g_free (dest_base);
525
    }
526

527
  g_object_unref (buffer);
528
  g_object_unref (input);
529

530
531
  gimp_progress_update (1.0);

532
  return image;
Elliot Lee's avatar
Elliot Lee committed
533
534
}

535
/*
Elliot Lee's avatar
Elliot Lee committed
536
537
 *  Description:
 *      save the given file out as an alias pix or matte file
538
539
 *
 *  Arguments:
Elliot Lee's avatar
Elliot Lee committed
540
 *      filename    - name of file to save to
541
542
 *      image       - image to save
 *      drawable    - current drawable
Elliot Lee's avatar
Elliot Lee committed
543
 */
544

545
static gboolean
546
547
548
549
save_image (GFile         *file,
            GimpImage     *image,
            GimpDrawable  *drawable,
            GError       **error)
550
{
551
552
553
  GOutputStream *output;
  GeglBuffer    *buffer;
  const Babl    *format;
554
  GCancellable  *cancellable;
555
556
557
558
559
560
561
  gint           width;
  gint           height;
  gint           depth, i, j, row, tile_height, rectHeight;
  gboolean       savingColor = TRUE;
  guchar        *src;
  guchar        *src_base;

562
  gimp_progress_init_printf (_("Exporting '%s'"),
563
564
                             g_file_get_parse_name (file));

565
566
567
  output = G_OUTPUT_STREAM (g_file_replace (file,
                                            NULL, FALSE, G_FILE_CREATE_NONE,
                                            NULL, error));
568
569
570
  if (! output)
    return FALSE;

571
  /* Get info about image */
572
  buffer = gimp_drawable_get_buffer (drawable);
573

574
575
  width  = gegl_buffer_get_width  (buffer);
  height = gegl_buffer_get_height (buffer);
576

577
  savingColor = ! gimp_drawable_is_gray (drawable);
578
579
580
581
582
583

  if (savingColor)
    format = babl_format ("R'G'B' u8");
  else
    format = babl_format ("Y' u8");

584
  depth = babl_format_get_bytes_per_pixel (format);
585

586
  /* Write the image header */
587
588
  PIX_DEBUG_PRINT ("Width %hu\n",  width);
  PIX_DEBUG_PRINT ("Height %hu\n", height);
589
590
591
592
593
594

  if (! put_short (output, width,  error) ||
      ! put_short (output, height, error) ||
      ! put_short (output, 0,      error) ||
      ! put_short (output, 0,      error))
    {
595
596
597
598
599
      cancellable = g_cancellable_new ();
      g_cancellable_cancel (cancellable);
      g_output_stream_close (output, cancellable, NULL);
      g_object_unref (cancellable);

600
601
602
603
      g_object_unref (output);
      g_object_unref (buffer);
      return FALSE;
    }
604
605

  tile_height = gimp_tile_height ();
606
  src_base    = g_new (guchar, tile_height * width * depth);
607
608

  if (savingColor)
609
610
    {
      /* Writing a 24-bit Pix image */
611

612
613
      if (! put_short (output, 24, error))
        goto fail;
614

615
      for (i = 0; i < height;)
David Odin's avatar
David Odin committed
616
        {
617
618
          rectHeight = (tile_height < (height - i)) ?
                        tile_height : (height - i);
619
620
621
622

          gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
                           format, src_base,
                           GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
David Odin's avatar
David Odin committed
623
624

          for (src = src_base, row = 0;
625
               row < tile_height && i < height;
David Odin's avatar
David Odin committed
626
627
628
               i += 1, row += 1)
            {
              /* Write a row of the image */
629
630
631

              guchar record[4];

David Odin's avatar
David Odin committed
632
633
634
635
636
              record[0] = 1;
              record[3] = src[0];
              record[2] = src[1];
              record[1] = src[2];
              src += depth;
637
              for (j = 1; j < width; ++j)
David Odin's avatar
David Odin committed
638
639
                {
                  if ((record[3] != src[0]) ||
640
641
642
                      (record[2] != src[1]) ||
                      (record[1] != src[2]) ||
                      (record[0] == 255))
David Odin's avatar
David Odin committed
643
644
645
                    {
                      /* Write current RLE record and start a new one */

646
                      if (! g_output_stream_write_all (output, record, 4,
647
                                                       NULL, NULL, error))
648
649
650
651
                        {
                          goto fail;
                        }

David Odin's avatar
David Odin committed
652
653
654
655
656
657
658
659
660
661
662
663
                      record[0] = 1;
                      record[3] = src[0];
                      record[2] = src[1];
                      record[1] = src[2];
                    }
                  else
                    {
                      /* increment run length in current record */
                      record[0]++;
                    }
                  src += depth;
                }
664

David Odin's avatar
David Odin committed
665
              /* Write last record in row */
666
667

              if (! g_output_stream_write_all (output, record, 4,
668
                                               NULL, NULL, error))
669
670
671
                {
                  goto fail;
                }
David Odin's avatar
David Odin committed
672
            }
673
674

          gimp_progress_update ((double) i / (double) height);
David Odin's avatar
David Odin committed
675
        }
676
677
    }
  else
678
679
    {
      /* Writing a 8-bit Matte (Mask) image */
680

681
682
      if (! put_short (output, 8, error))
        goto fail;
683

684
      for (i = 0; i < height;)
David Odin's avatar
David Odin committed
685
        {
686
687
          rectHeight = (tile_height < (height - i)) ?
                        tile_height : (height - i);
688
689
690
691

          gegl_buffer_get (buffer, GEGL_RECTANGLE (0, i, width, rectHeight), 1.0,
                           format, src_base,
                           GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
David Odin's avatar
David Odin committed
692
693

          for (src = src_base, row = 0;
694
               row < tile_height && i < height;
David Odin's avatar
David Odin committed
695
696
697
               i += 1, row += 1)
            {
              /* Write a row of the image */
698
699
700

              guchar record[2];

David Odin's avatar
David Odin committed
701
702
703
              record[0] = 1;
              record[1] = src[0];
              src += depth;
704
              for (j = 1; j < width; ++j)
David Odin's avatar
David Odin committed
705
706
707
708
                {
                  if ((record[1] != src[0]) || (record[0] == 255))
                    {
                      /* Write current RLE record and start a new one */
709
710

                      if (! g_output_stream_write_all (output, record, 2,
711
                                                       NULL, NULL, error))
712
713
714
715
                        {
                          goto fail;
                        }

David Odin's avatar
David Odin committed
716
717
718
719
720
721
722
723
724
725
                      record[0] = 1;
                      record[1] = src[0];
                    }
                  else
                    {
                      /* increment run length in current record */
                      record[0] ++;
                    }
                  src += depth;
                }
726

David Odin's avatar
David Odin committed
727
              /* Write last record in row */
728
729

              if (! g_output_stream_write_all (output, record, 2,
730
                                               NULL, NULL, error))
731
732
733
                {
                  goto fail;
                }
David Odin's avatar
David Odin committed
734
            }
735
736

          gimp_progress_update ((double) i / (double) height);
David Odin's avatar
David Odin committed
737
        }
738
    }
739
740

  g_free (src_base);
741
  g_object_unref (output);
742
743
744
745
  g_object_unref (buffer);

  gimp_progress_update (1.0);

746
  return TRUE;
747
748
749

 fail:

750
751
752
753
754
  cancellable = g_cancellable_new ();
  g_cancellable_cancel (cancellable);
  g_output_stream_close (output, cancellable, NULL);
  g_object_unref (cancellable);

755
756
757
758
759
  g_free (src_base);
  g_object_unref (output);
  g_object_unref (buffer);

  return FALSE;
760
}