psd-save.c 75.3 KB
Newer Older
1
/*
2
3
 * PSD Export Plugin version 1.0 (BETA)
 * This GIMP plug-in is designed to export Adobe Photoshop(tm) files (.PSD)
4
5
6
 *
 * Monigotes
 *
7
 *     If this plug-in fails to export a file which you think it should,
8
 *     please tell me what seemed to go wrong, and anything you know
9
 *     about the image you tried to export.  Please don't send big PSD
10
11
12
13
 *     files to me without asking first.
 *
 *          Copyright (C) 2000 Monigotes
 *
14
 * This program is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation; either version 3 of the License, or
17
18
19
20
21
22
23
24
 * (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
25
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
26
27
28
29
30
31
32
33
34
35
36
37
38
 */

/*
 * Adobe and Adobe Photoshop are trademarks of Adobe Systems
 * Incorporated that may be registered in certain jurisdictions.
 */

/*
 * Revision history:
 *
 *  2000.02 / v1.0 / Monigotes
 *       First version.
 *
39
40
41
42
 *  2003-05-10  Pedro Gimeno  <pggimeno@wanadoo.es>
 *       - Cleaned up and GNUstylized.
 *       - Translated all comments and vars in Spanish to English.
 *
43
44
 *  2005-2-11 Jay Cox <jaycox@gimp.org>
 *       Rewrote all the code that deals with pixels to be stingy with
45
 *       memory and operate on tile-size chunks.  Create a flattened
46
47
 *       copy of the image when necessary. Fixes file corruption bug
 *       #167139 and memory bug #121871
48
49
50
51
52
53
54
 *
 *  2006-03-29 Guillermo S. Romero <gsr.bugs@infernal-iceberg.com>
 *       - Added/enabled basic support for layer masks based in psd.c
 *         and whatever was already here.
 *         Layers that are not canvas sized need investigation, here
 *         or in the import plugin something seems wrong.
 *       - Cosmetic changes about the debug messages, more like psd.c.
55
56
57
58
 */

/*
 * TODO:
59
 *       Export preview
60
61
62
63
64
65
 */

/*
 * BUGS:
 */

66
67
68
69
70
#include "config.h"

#include <errno.h>
#include <string.h>

71
#include <glib/gstdio.h>
72
73

#include "libgimp/gimp.h"
74
#include "libgimp/gimpui.h"
75

76
77
#include "libgimpmath/gimpmath.h"

78
#include "psd.h"
79
#include "psd-util.h"
80
#include "psd-save.h"
81

82
#include "libgimp/stdplugins-intl.h"
83

84

85
86
87
#define PSD_UNIT_INCH 1
#define PSD_UNIT_CM   2

88
89
90
91

/* Local types etc
 */

92
typedef enum PsdLayerType
93
{
94
95
96
97
  PSD_LAYER_TYPE_LAYER,
  PSD_LAYER_TYPE_GROUP_START,
  PSD_LAYER_TYPE_GROUP_END
} PSD_Layer_Type;
98

99
typedef struct PsdLayer
100
{
101
102
  GimpLayer      *layer;
  PSD_Layer_Type  type;
103
} PSD_Layer;
104

105
106
107
typedef struct PsdImageData
{
  gboolean           compression;
108

109
110
  gint32             image_height;
  gint32             image_width;
111

112
  GimpImageBaseType  baseType;
113

114
  GimpLayer         *merged_layer;/* Merged image,
115
                                     to be used for the image data section */
116

117
  gint               nChannels;   /* Number of user channels in the image */
118
  GList             *lChannels;   /* List of GimpChannel (User channels in the image) */
119

120
  gint               nLayers;     /* Number of layers in the image */
121
  GList             *lLayers;     /* List of PSD_Layer */
122
123
124
125
126
127
128
} PSD_Image_Data;

static PSD_Image_Data PSDImageData;

/* Declare some local functions.
 */

129
130
static const gchar * psd_lmode_layer      (GimpLayer      *layer,
                                           gboolean        section_divider);
131

132
static void          reshuffle_cmap_write (guchar         *mapGimp);
133

134
static void          save_header          (GOutputStream  *output,
135
                                           GimpImage      *image,
Alx Sa's avatar
Alx Sa committed
136
                                           gboolean        export_cmyk,
137
                                           gboolean        export_duotone);
138

139
static void          save_color_mode_data (GOutputStream  *output,
140
141
                                           GimpImage      *image,
                                           gboolean        export_duotone);
142

143
static void          save_resources       (GOutputStream  *output,
144
                                           GimpImage      *image,
Alx Sa's avatar
Alx Sa committed
145
                                           gboolean        export_cmyk,
146
                                           gboolean        export_duotone);
147

148
static void          save_layer_and_mask  (GOutputStream  *output,
Alx Sa's avatar
Alx Sa committed
149
150
                                           GimpImage      *image,
                                           gboolean        export_cmyk);
151

152
static void          save_data            (GOutputStream  *output,
Alx Sa's avatar
Alx Sa committed
153
154
                                           GimpImage      *image,
                                           gboolean        export_cmyk);
155

156
157
158
159
static void          xfwrite              (GOutputStream  *output,
                                           gconstpointer   buf,
                                           gsize           len,
                                           const gchar    *why);
160

161
162
163
164
static void          write_pascalstring   (GOutputStream  *output,
                                           const gchar    *val,
                                           gint            padding,
                                           const gchar    *why);
165

166
167
168
static void          write_string         (GOutputStream  *output,
                                           const gchar    *val,
                                           const gchar    *why);
169

170
171
172
static void          write_gchar          (GOutputStream  *output,
                                           guchar          val,
                                           const gchar    *why);
173

174
175
176
static void          write_gint16         (GOutputStream  *output,
                                           gint16          val,
                                           const gchar    *why);
177

178
179
180
static void          write_gint32         (GOutputStream  *output,
                                           gint32          val,
                                           const gchar    *why);
181

182
183
184
static void          write_datablock_luni (GOutputStream  *output,
                                           const gchar    *val,
                                           const gchar    *why);
185

186

187
static void          write_pixel_data     (GOutputStream  *output,
Alx Sa's avatar
Alx Sa committed
188
                                           GimpImage      *image,
189
190
191
                                           GimpDrawable   *drawable,
                                           goffset        *ChanLenPosition,
                                           goffset         rowlenOffset,
Alx Sa's avatar
Alx Sa committed
192
193
                                           gboolean        write_mask,
                                           gboolean        export_cmyk);
194

195
static GimpLayer   * create_merged_image  (GimpImage      *image);
196

197
198
199
200
static gint          get_bpc              (GimpImage      *image);
static const Babl  * get_pixel_format     (GimpDrawable   *drawable);
static const Babl  * get_channel_format   (GimpDrawable   *drawable);
static const Babl  * get_mask_format      (GimpLayerMask  *mask);
201

202
203
static GList       * image_get_all_layers (GimpImage      *image,
                                           gint           *n_layers);
204

205
static const gchar *
206
207
psd_lmode_layer (GimpLayer *layer,
                 gboolean   section_divider)
208
{
209
  LayerModeInfo mode_info;
Sven Neumann's avatar
Sven Neumann committed
210

211
212
213
214
  mode_info.mode            = gimp_layer_get_mode (layer);
  mode_info.blend_space     = gimp_layer_get_blend_space (layer);
  mode_info.composite_space = gimp_layer_get_composite_space (layer);
  mode_info.composite_mode  = gimp_layer_get_composite_mode (layer);
Sven Neumann's avatar
Sven Neumann committed
215

216
217
218
  /* pass-through groups use normal mode in their layer record; the
   * pass-through mode is specified in their section divider resource.
   */
219
  if (mode_info.mode == GIMP_LAYER_MODE_PASS_THROUGH && ! section_divider)
220
221
    mode_info.mode = GIMP_LAYER_MODE_NORMAL;

222
  return gimp_to_psd_blend_mode (&mode_info);
223
224
}

225
static void
226
227
228
write_string (GOutputStream  *output,
              const gchar    *val,
              const gchar    *why)
229
{
230
231
  write_gchar (output, strlen (val), why);
  xfwrite (output, val, strlen (val), why);
232
233
}

234
static void
235
236
237
238
write_pascalstring (GOutputStream  *output,
                    const gchar    *val,
                    gint            padding,
                    const gchar    *why)
239
{
Sven Neumann's avatar
Sven Neumann committed
240
241
  guchar len;
  gint   i;
242

243
  /* Calculate string length to write and limit it to 255 */
244

Sven Neumann's avatar
Sven Neumann committed
245
  len = (strlen (val) > 255) ? 255 : (guchar) strlen (val);
246

247
  /* Perform actual writing */
248

249
250
  if (len !=  0)
    {
251
252
      write_gchar (output, len, why);
      xfwrite (output, val, len, why);
253
    }
254
  else
255
    {
256
      write_gchar (output, 0, why);
257
    }
258

259
260
  /* If total length (length byte + content) is not a multiple of PADDING,
     add zeros to pad it.  */
261

262
  len++;           /* Add the length field */
263

264
  if ((len % padding) == 0)
265
266
    return;

267
  for (i = 0; i < (padding - (len % padding)); i++)
268
    write_gchar (output, 0, why);
269
270
}

271
static void
272
273
274
275
xfwrite (GOutputStream  *output,
         gconstpointer   buf,
         gsize           len,
         const gchar    *why)
276
{
277
278
  gsize bytes_written;

279
280
281
  if (len == 0)
    return;

282
283
284
285
286
  /* FIXME Instead of NULL use error parameter and add error to all functions!
   * and then we also need to change functions from void to gboolean or something
   * and check the return values. */
  if (! g_output_stream_write_all (output, buf, len,
                                   &bytes_written, NULL, NULL))
287
    {
288
      g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
289
290
      gimp_quit ();
    }
291
292
}

293
static void
294
295
296
write_gchar (GOutputStream  *output,
             guchar          val,
             const gchar    *why)
297
{
298
299
300
  guchar   b[2];
  goffset  pos;
  gsize    bytes_written;
301
302
303
304

  b[0] = val;
  b[1] = 0;

305
306
307
308
  pos = g_seekable_tell (G_SEEKABLE (output));
  /* FIXME: Use error in write and seek */
  if (! g_output_stream_write_all (output, &b, 2,
                                   &bytes_written, NULL, NULL))
309
    {
310
      g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
311
312
      gimp_quit ();
    }
313
314
315
  g_seekable_seek (G_SEEKABLE (output),
                   pos + 1, G_SEEK_SET,
                   NULL, NULL);
316
317
}

318
static void
319
320
321
write_gint16 (GOutputStream  *output,
              gint16          val,
              const gchar    *why)
322
{
Sven Neumann's avatar
Sven Neumann committed
323
  guchar b[2];
324
325
  gsize  bytes_written;

326
327
328
329
330
331
  /*  b[0] = val & 255;
      b[1] = (val >> 8) & 255;*/

  b[1] = val & 255;
  b[0] = (val >> 8) & 255;

332
333
334
  /* FIXME: Use error */
  if (! g_output_stream_write_all (output, &b, 2,
                                   &bytes_written, NULL, NULL))
335
    {
336
      g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
337
338
      gimp_quit ();
    }
339
340
}

341
static void
342
343
344
write_gint32 (GOutputStream  *output,
              gint32          val,
              const gchar    *why)
345
{
Sven Neumann's avatar
Sven Neumann committed
346
  guchar b[4];
347
  gsize  bytes_written;
348
349
350
351
352
353

  b[3] = val & 255;
  b[2] = (val >> 8) & 255;
  b[1] = (val >> 16) & 255;
  b[0] = (val >> 24) & 255;

354
355
356
  /* FIXME: Use error */
  if (! g_output_stream_write_all (output, &b, 4,
                                   &bytes_written, NULL, NULL))
357
    {
358
      g_printerr ("%s: Error while writing '%s'\n", G_STRFUNC, why);
359
360
      gimp_quit ();
    }
361
362
}

363
static void
364
365
366
write_datablock_luni (GOutputStream  *output,
                      const gchar    *val,
                      const gchar    *why)
367
368
{
  if (val)
Sven Neumann's avatar
Sven Neumann committed
369
    {
370
371
372
373
      guint32    count;
      guint32    xdBlockSize;
      glong      numchars;
      gunichar2 *luniName;
374

375
      luniName = g_utf8_to_utf16 (val, -1, NULL, &numchars, NULL);
376

Sven Neumann's avatar
Sven Neumann committed
377
378
      if (luniName)
        {
379
          guchar len = MIN (numchars, 255);
380

Sven Neumann's avatar
Sven Neumann committed
381
382
383
384
385
          /* Only pad to even num of chars */
          if( len % 2 )
            xdBlockSize = len + 1;
          else
            xdBlockSize = len;
386

Sven Neumann's avatar
Sven Neumann committed
387
388
          /* 2 bytes / char + 4 bytes for pascal num chars */
          xdBlockSize = (xdBlockSize * 2) + 4;
389

390
391
392
          xfwrite (output, "8BIMluni", 8, "luni xdb signature");
          write_gint32 (output, xdBlockSize, "luni xdb size");
          write_gint32 (output, len, "luni xdb pascal string");
393

394
          for (count = 0; count < len; count++)
395
            write_gint16 (output, luniName[count], "luni xdb pascal string");
396

Sven Neumann's avatar
Sven Neumann committed
397
398
          /* Pad to an even number of chars */
          if (len % 2)
399
            write_gint16 (output, 0x0000, "luni xdb pascal string padding");
Sven Neumann's avatar
Sven Neumann committed
400
        }
401
402
403
    }
}

404
static gint32
Sven Neumann's avatar
Sven Neumann committed
405
406
407
pack_pb_line (guchar *start,
              gint32  length,
              guchar *dest_ptr)
408
{
Sven Neumann's avatar
Sven Neumann committed
409
410
411
  gint32  remaining = length;
  gint    i, j;

412
  length = 0;
413
  while (remaining > 0)
414
    {
415
      /* Look for characters matching the first */
416
417

      i = 0;
418
      while ((i < 128) &&
419
             (remaining - i > 0) &&
420
             (start[0] == start[i]))
421
422
        i++;

423
      if (i > 1)              /* Match found */
424
425
        {

426
427
          *dest_ptr++ = -(i - 1);
          *dest_ptr++ = *start;
428

429
          start += i;
430
          remaining -= i;
431
          length += 2;
432
433
434
435
        }
      else       /* Look for characters different from the previous */
        {
          i = 0;
436
          while ((i < 128)                 &&
437
                 (remaining - (i + 1) > 0) &&
438
439
                 (start[i] != start[i + 1] ||
                  remaining - (i + 2) <= 0 || start[i] != start[i+2]))
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
            i++;

          /* If there's only 1 remaining, the previous WHILE stmt doesn't
             catch it */

          if (remaining == 1)
            {
              i = 1;
            }

          if (i > 0)               /* Some distinct ones found */
            {
              *dest_ptr++ = i - 1;
              for (j = 0; j < i; j++)
                {
455
                  *dest_ptr++ = start[j];
456
                }
457
              start += i;
458
              remaining -= i;
459
              length += i + 1;
460
            }
461

462
463
        }
    }
464
  return length;
465
466
}

467
static gint
468
gimpBaseTypeToPsdMode (GimpImageBaseType gimpBaseType)
469
470
{
  switch (gimpBaseType)
471
    {
472
    case GIMP_RGB:
473
      return 3;                                         /* RGB */
474
    case GIMP_GRAY:
475
      return 1;                                         /* Grayscale */
476
    case GIMP_INDEXED:
477
      return 2;                                         /* Indexed */
478
    default:
479
      g_message (_("Error: Can't convert GIMP base imagetype to PSD mode"));
480
481
      IFDBG(1) g_debug ("PSD Export: gimpBaseType value is %d, "
                        "can't convert to PSD mode", gimpBaseType);
482
483
484
      gimp_quit ();
      return 3;                            /* Return RGB by default */
    }
485
486
}

487
static gint
Sven Neumann's avatar
Sven Neumann committed
488
489
490
nChansLayer (gint gimpBaseType,
             gint hasAlpha,
             gint hasMask)
491
{
492
  gint incAlpha = 0;
493
  gint incMask = 0;
494

495
  incAlpha = (hasAlpha == 0) ? 0 : 1;
496
  incMask = (hasMask == 0) ? 0 : 1;
497
498

  switch (gimpBaseType)
499
    {
500
    case GIMP_RGB:
501
      return 3 + incAlpha + incMask;     /* R,G,B & Alpha & Mask (if any) */
502
    case GIMP_GRAY:
503
      return 1 + incAlpha + incMask;     /* G & Alpha & Mask (if any) */
504
    case GIMP_INDEXED:
505
      return 1 + incAlpha + incMask;     /* I & Alpha & Mask (if any) */
506
    default:
507
508
      return 0;                          /* Return 0 channels by default */
    }
509
510
}

511
512
static void
reshuffle_cmap_write (guchar *mapGimp)
513
514
{
  guchar *mapPSD;
Sven Neumann's avatar
Sven Neumann committed
515
  gint    i;
516

517
  mapPSD = g_malloc (768);
518

519
520
521
522
523
524
  for (i = 0; i < 256; i++)
    {
      mapPSD[i] = mapGimp[i * 3];
      mapPSD[i + 256] = mapGimp[i * 3 + 1];
      mapPSD[i + 512] = mapGimp[i * 3 + 2];
    }
525

526
527
528
529
  for (i = 0; i < 768; i++)
    {
      mapGimp[i] = mapPSD[i];
    }
530

531
  g_free (mapPSD);
532
533
}

534
static void
535
save_header (GOutputStream  *output,
536
             GimpImage      *image,
Alx Sa's avatar
Alx Sa committed
537
             gboolean        export_cmyk,
538
             gboolean        export_duotone)
539
{
Alx Sa's avatar
Alx Sa committed
540
541
  gint nChannels;

542
543
544
545
546
547
548
  IFDBG(1) g_debug ("Function: save_header\n"
                    "\tRows: %d\n"
                    "\tColumns: %d\n"
                    "\tBase type: %d\n"
                    "\tNumber of channels: %d\n",
                    PSDImageData.image_height, PSDImageData.image_width,
                    PSDImageData.baseType,     PSDImageData.nChannels);
549

Alx Sa's avatar
Alx Sa committed
550
551
552
553
554
555
556
557
558
559
560
561
  if (export_cmyk)
    {
      nChannels =
        gimp_drawable_has_alpha (GIMP_DRAWABLE (PSDImageData.merged_layer)) ?
        5 : 4;
    }
  else
    {
      nChannels = (PSDImageData.nChannels + nChansLayer (PSDImageData.baseType,
        gimp_drawable_has_alpha (GIMP_DRAWABLE (PSDImageData.merged_layer)), 0));
    }

562
563
564
565
  xfwrite (output, "8BPS", 4, "signature");
  write_gint16 (output, 1, "version");
  write_gint32 (output, 0, "reserved 1");      /* 6 for the 'reserved' field + 4 bytes for a long */
  write_gint16 (output, 0, "reserved 1");      /* and 2 bytes for a short */
Alx Sa's avatar
Alx Sa committed
566
  write_gint16 (output, nChannels, "channels");
567
568
569
  write_gint32 (output, PSDImageData.image_height, "rows");
  write_gint32 (output, PSDImageData.image_width, "columns");
  write_gint16 (output, 8 * get_bpc (image), "depth");
Alx Sa's avatar
Alx Sa committed
570
571
572
  if (export_cmyk)
    write_gint16 (output, PSD_CMYK, "mode");
  else if (export_duotone)
573
574
575
    write_gint16 (output, PSD_DUOTONE, "mode");
  else
    write_gint16 (output, gimpBaseTypeToPsdMode (PSDImageData.baseType), "mode");
576
577
}

578
static void
579
save_color_mode_data (GOutputStream  *output,
580
581
                      GimpImage      *image,
                      gboolean        export_duotone)
582
{
583
584
585
586
587
  guchar       *cmap;
  guchar       *cmap_modified;
  gint          i;
  gint32        nColors;
  GimpParasite *parasite = NULL;
588

589
  IFDBG(1) g_debug ("Function: save_color_mode_data");
590

591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
  parasite = gimp_image_get_parasite (image, PSD_PARASITE_DUOTONE_DATA);
  if (export_duotone && parasite)
    {
      const guchar *parasite_data;
      guint32       parasite_size;

      IFDBG(1) g_debug ("\tImage type: DUOTONE");

      parasite_data = (const guchar *) gimp_parasite_get_data (parasite, &parasite_size);

      write_gint32 (output, parasite_size, "color data length");
      xfwrite (output, parasite_data, parasite_size, "colormap");

      gimp_parasite_free (parasite);
      return;
    }

608
  switch (PSDImageData.baseType)
609
    {
610
    case GIMP_INDEXED:
611
      IFDBG(1) g_debug ("\tImage type: INDEXED");
612

613
      cmap = gimp_image_get_colormap (image, &nColors);
614
      IFDBG(1) g_debug ("\t\tLength of colormap returned by gimp_image_get_colormap: %d", nColors);
615

616
617
      if (nColors == 0)
        {
618
          IFDBG(1) g_debug ("\t\tThe indexed image lacks a colormap");
619
          write_gint32 (output, 0, "color data length");
620
621
622
        }
      else if (nColors != 256)
        {
623
624
          IFDBG(1) g_debug ("\t\tThe indexed image has %d!=256 colors", nColors);
          IFDBG(1) g_debug ("\t\tPadding with zeros up to 256");
625
          write_gint32 (output, 768, "color data length");
626
            /* For this type, length is always 768 */
627

628
          cmap_modified = g_malloc (768);
629
630
          for (i = 0; i < nColors * 3; i++)
            cmap_modified[i] = cmap[i];
631

632
633
          for (i = nColors * 3; i < 768; i++)
            cmap_modified[i] = 0;
634

635
          reshuffle_cmap_write (cmap_modified);
636
          xfwrite (output, cmap_modified, 768, "colormap");  /* Write readjusted colormap */
637

638
639
640
641
          g_free (cmap_modified);
        }
      else         /* nColors equals 256 */
        {
642
          write_gint32 (output, 768, "color data length");   /* For this type, length is always 768 */
643
          reshuffle_cmap_write (cmap);
644
          xfwrite (output, cmap, 768, "colormap");  /* Write readjusted colormap */
645
        }
646
647
648
      break;

    default:
649
      IFDBG(1) g_debug ("\tImage type: Not INDEXED");
650
      write_gint32 (output, 0, "color data length");
651
    }
652
653
}

654
static void
655
save_resources (GOutputStream  *output,
656
                GimpImage      *image,
Alx Sa's avatar
Alx Sa committed
657
                gboolean        export_cmyk,
658
                gboolean        export_duotone)
659
{
660
  GList        *iter;
661
  gint          i;
662
  GList        *SelLayers;           /* The selected layers */
663

664
665
666
  goffset       eof_pos;             /* Position for End of file */
  goffset       rsc_pos;             /* Position for Lengths of Resources section */
  goffset       name_sec;            /* Position for Lengths of Channel Names */
667
668


669
670
  /* Only relevant resources in GIMP are: 0x03EE, 0x03F0 & 0x0400 */
  /* For Adobe Photoshop version 4.0 these can also be considered:
671
     0x0408, 0x040A & 0x040B (1006, 1008, 1024, 1032, 1034, and 1035) */
672

673
  IFDBG(1) g_debug ("Function: save_resources");
674
675


676
  /* Get the image title from its filename */
677

678
679
  IFDBG(1) g_debug ("\tImage title: %s",
                    g_file_peek_path (gimp_image_get_file (image)));
680

681
  /* Get the selected layers */
682

683
  SelLayers = gimp_image_list_selected_layers (image);
684

685
  /* Here's where actual writing starts */
686

687
688
  rsc_pos = g_seekable_tell (G_SEEKABLE (output));
  write_gint32 (output, 0, "image resources length");
689
690


691
  /* --------------- Write Channel names --------------- */
Alx Sa's avatar
Alx Sa committed
692
  if (! export_cmyk)
693
    {
Alx Sa's avatar
Alx Sa committed
694
695
696
697
698
699
700
      if (PSDImageData.nChannels > 0 ||
          gimp_drawable_has_alpha (GIMP_DRAWABLE (PSDImageData.merged_layer)))
        {
          xfwrite (output, "8BIM", 4, "imageresources signature");
          write_gint16 (output, 0x03EE, "0x03EE Id"); /* 1006 */
          /* write_pascalstring (output, Name, "Id name"); */
          write_gint16 (output, 0, "Id name"); /* Set to null string (two zeros) */
701

Alx Sa's avatar
Alx Sa committed
702
          /* Mark current position in the file */
703

Alx Sa's avatar
Alx Sa committed
704
705
          name_sec = g_seekable_tell (G_SEEKABLE (output));
          write_gint32 (output, 0, "0x03EE resource size");
706

Alx Sa's avatar
Alx Sa committed
707
          /* Write all strings */
708

Alx Sa's avatar
Alx Sa committed
709
710
711
          /* if the merged_image contains transparency, write a name for it first */
          if (gimp_drawable_has_alpha (GIMP_DRAWABLE (PSDImageData.merged_layer)))
            write_string (output, "Transparency", "channel name");
712

Alx Sa's avatar
Alx Sa committed
713
714
715
716
717
718
719
          for (iter = PSDImageData.lChannels; iter; iter = g_list_next (iter))
            {
              char *chName = gimp_item_get_name (iter->data);
              write_string (output, chName, "channel name");
              g_free (chName);
            }
          /* Calculate and write actual resource's length */
720

Alx Sa's avatar
Alx Sa committed
721
          eof_pos = g_seekable_tell (G_SEEKABLE (output));
722

Alx Sa's avatar
Alx Sa committed
723
724
725
726
727
728
          g_seekable_seek (G_SEEKABLE (output),
                           name_sec, G_SEEK_SET,
                           NULL, NULL /*FIXME: error*/);
          write_gint32 (output, eof_pos - name_sec - sizeof (gint32), "0x03EE resource size");
          IFDBG(1) g_debug ("\tTotal length of 0x03EE resource: %d",
                            (int) (eof_pos - name_sec - sizeof (gint32)));
729

Alx Sa's avatar
Alx Sa committed
730
          /* Return to EOF to continue writing */
731

Alx Sa's avatar
Alx Sa committed
732
733
734
          g_seekable_seek (G_SEEKABLE (output),
                           eof_pos, G_SEEK_SET,
                           NULL, NULL /*FIXME: error*/);
735

Alx Sa's avatar
Alx Sa committed
736
          /* Pad if length is odd */
737

Alx Sa's avatar
Alx Sa committed
738
739
740
          if ((eof_pos - name_sec - sizeof (gint32)) & 1)
            write_gchar (output, 0, "pad byte");
        }
741

Alx Sa's avatar
Alx Sa committed
742
      /* --------------- Write Channel properties --------------- */
743

Alx Sa's avatar
Alx Sa committed
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
      if (PSDImageData.nChannels > 0 ||
          gimp_drawable_has_alpha (GIMP_DRAWABLE (PSDImageData.merged_layer)))
        {
          xfwrite (output, "8BIM", 4, "imageresources signature");
          write_gint16 (output, 0x0435, "0x0435 Id"); /* 1077 */
          /* write_pascalstring (output, Name, "Id name"); */
          write_gint16 (output, 0, "Id name"); /* Set to null string (two zeros) */
          write_gint32 (output,
                        4  +
                        13 * (gimp_drawable_has_alpha (
                                GIMP_DRAWABLE (PSDImageData.merged_layer)) +
                              PSDImageData.nChannels),
                        "0x0435 resource size");

          /* The function of the first 4 bytes is unclear. As per
           * load_resource_1077() in psd-image-res-load.c, it seems to be a version
           * number that is always one.
           */
          write_gint32 (output, 1, "0x0435 version");
763

Alx Sa's avatar
Alx Sa committed
764
          /* Write all channel properties */
765

Alx Sa's avatar
Alx Sa committed
766
          #define DOUBLE_TO_INT16(x) ROUND (SAFE_CLAMP (x, 0.0, 1.0) * 0xffff)
767

Alx Sa's avatar
Alx Sa committed
768
769
770
771
772
773
774
775
776
777
778
          /* if the merged_image contains transparency, write its properties first */
          if (gimp_drawable_has_alpha (GIMP_DRAWABLE (PSDImageData.merged_layer)))
            {
              write_gint16 (output, PSD_CS_RGB, "channel color space");
              write_gint16 (output, DOUBLE_TO_INT16 (1.0), "channel color r");
              write_gint16 (output, DOUBLE_TO_INT16 (0.0), "channel color g");
              write_gint16 (output, DOUBLE_TO_INT16 (0.0), "channel color b");
              write_gint16 (output, 0,                     "channel color padding");
              write_gint16 (output, 100,                   "channel opacity");
              write_gchar  (output, 1,                     "channel mode");
            }
779

Alx Sa's avatar
Alx Sa committed
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
          for (iter = PSDImageData.lChannels; iter; iter = g_list_next (iter))
            {
              GimpChannel *channel = iter->data;
              GimpRGB      color;
              gdouble      opacity;

              gimp_channel_get_color (channel, &color);
              opacity = gimp_channel_get_opacity (channel);

              write_gint16 (output, PSD_CS_RGB,                "channel color space");
              write_gint16 (output, DOUBLE_TO_INT16 (color.r), "channel color r");
              write_gint16 (output, DOUBLE_TO_INT16 (color.g), "channel color g");
              write_gint16 (output, DOUBLE_TO_INT16 (color.b), "channel color b");
              write_gint16 (output, 0,                         "channel color padding");
              write_gint16 (output, ROUND (opacity),           "channel opacity");
              write_gchar  (output, 1,                         "channel mode");
            }
797

Alx Sa's avatar
Alx Sa committed
798
          #undef DOUBLE_TO_INT16
799

Alx Sa's avatar
Alx Sa committed
800
          /* Pad if length is odd */
801

Alx Sa's avatar
Alx Sa committed
802
803
804
          if (g_seekable_tell (G_SEEKABLE (output)) & 1)
            write_gchar (output, 0, "pad byte");
        }
805
    }
jaycox's avatar
jaycox committed
806
  /* --------------- Write Guides --------------- */
807
  if (gimp_image_find_next_guide (image, 0))
jaycox's avatar
jaycox committed
808
809
    {
      gint n_guides = 0;
Alx Sa's avatar
Alx Sa committed
810
      gint guide_id = 0;
jaycox's avatar
jaycox committed
811
812

      /* Count the guides */
813
      while ((guide_id = gimp_image_find_next_guide(image, guide_id)))
814
        n_guides++;
jaycox's avatar
jaycox committed
815

816
817
818
819
820
      xfwrite (output, "8BIM", 4, "imageresources signature");
      write_gint16 (output, 0x0408, "0x0408 Id (Guides)"); /* 1032 */
      /* write_pascalstring (output, Name, "Id name"); */
      write_gint16 (output, 0, "Id name"); /* Set to null string (two zeros) */
      write_gint32 (output, 16 + 5 * n_guides, "0x0408 resource size");
jaycox's avatar
jaycox committed
821
      /* Save grid and guide header */
822
823
824
825
      write_gint32 (output,   1, "grid/guide header version");
      write_gint32 (output, 576, "grid custom spacing horizontal");/* dpi*32/4??*/
      write_gint32 (output, 576, "grid custom spacing vertical");  /* dpi*32/4??*/
      write_gint32 (output, n_guides, "number of guides");
jaycox's avatar
jaycox committed
826
827

      /* write the guides */
828
      while ((guide_id = gimp_image_find_next_guide (image, guide_id)))
jaycox's avatar
jaycox committed
829
        {
830
831
          gchar orientation;
          gint32 position;
832
833
          orientation = gimp_image_get_guide_orientation (image, guide_id);
          position    = 32 * gimp_image_get_guide_position (image, guide_id);
834
          orientation ^= 1; /* in the psd vert =0 , horiz = 1 */
835
836
          write_gint32 (output, position, "Position of guide");
          write_gchar (output, orientation, "Orientation of guide");
837
838
          n_guides--;
        }
839
840
      if ((g_seekable_tell (G_SEEKABLE (output)) & 1))
        write_gchar(output, 0, "pad byte");
jaycox's avatar
jaycox committed
841
      if (n_guides != 0)
842
        g_warning("Screwed up guide resource:: wrong number of guides\n");
843
844
      IFDBG(1) g_debug ("\tTotal length of 0x0400 resource: %d",
                        (int) sizeof (gint16));
jaycox's avatar
jaycox committed
845
    }
846

847
848
  /* --------------- Write resolution data ------------------- */
  {
849
850
    gdouble  xres = 0, yres = 0;
    guint32  xres_fix, yres_fix;
851
    GimpUnit g_unit;
852
    gint16   psd_unit;
853

854
855
    g_unit = gimp_image_get_unit (image);
    gimp_image_get_resolution (image, &xres, &yres);
856
857
858

    if (g_unit == GIMP_UNIT_MM)
      {
859
        psd_unit = PSD_UNIT_CM;
860
861
862
      }
    else
      {
863
        psd_unit = PSD_UNIT_INCH;
864
865
      }

866
867
    /* Don't convert resolution based on g_unit which is a display unit.
     * PSD resolution is always in pixels/inch. */
868
869
870
    xres_fix = xres * 65536.0 + .5; /* Convert to 16.16 fixed point */
    yres_fix = yres * 65536.0 + .5; /* Convert to 16.16 fixed point */

871
872
873
874
    xfwrite (output, "8BIM", 4, "imageresources signature (for resolution)");
    write_gint16(output, 0x03ed, "0x03ed Id (resolution)"); /* 1005 */
    write_gint16 (output, 0, "Id name"); /* Set to null string (two zeros) */
    write_gint32 (output, 16, "0x0400 resource size");
875
    write_gint32 (output, xres_fix, "hRes (16.16 fixed point)");
876
877
    write_gint16 (output, psd_unit, "hRes unit");
    write_gint16 (output, psd_unit, "width unit");
878
    write_gint32 (output, yres_fix, "vRes (16.16 fixed point)");
879
880
    write_gint16 (output, psd_unit, "vRes unit");
    write_gint16 (output, psd_unit, "height unit");
881
  }
882