Blind fix macOS build
[geeqie.git] / src / image-load-psd.cc
1 /*
2  * Copyright (C) 20019 - The Geeqie Team
3  *
4  * Author: Colin Clark
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  *
21  * Derived from:
22  *
23  * GdkPixbuf library - PSD image loader
24  *
25  * Copyright (C) 2008 Jan Dudek
26  *
27  * Authors: Jan Dudek <jd@jandudek.com>
28  *
29  * This library is free software; you can redistribute it and/or
30  * modify it under the terms of the GNU Lesser General Public
31  * License as published by the Free Software Foundation; either
32  * version 2 of the License, or (at your option) any later version.
33  *
34  * This library is distributed in the hope that it will be useful,
35  * but WITHOUT ANY WARRANTY; without even the implied warranty of
36  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
37  * Lesser General Public License for more
38  * You should have received a copy of the GNU Lesser General Public
39  * License along with this library; if not, write to the
40  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
41  * Boston, MA 02111-1307, USA.
42  */
43
44 /*
45  * TODO
46  * - report errors from parse_psd_header
47  * - other color modes (CMYK at least)
48  * - i18n
49  */
50
51 #include "image-load-psd.h"
52
53 #include <cstring>
54
55 #include <gdk-pixbuf/gdk-pixbuf.h>
56 #include <glib-object.h>
57 #include <glib.h>
58
59 #include "debug.h"
60 #include "image-load.h"
61
62 namespace
63 {
64
65 struct ImageLoaderPSD : public ImageLoaderBackend
66 {
67 public:
68         ~ImageLoaderPSD() override;
69
70         void init(AreaUpdatedCb area_updated_cb, SizePreparedCb size_prepared_cb, AreaPreparedCb area_prepared_cb, gpointer data) override;
71         gboolean write(const guchar *buf, gsize &chunk_size, gsize count, GError **error) override;
72         GdkPixbuf *get_pixbuf() override;
73         gchar *get_format_name() override;
74         gchar **get_format_mime_types() override;
75
76 private:
77         AreaUpdatedCb area_updated_cb;
78         gpointer data;
79
80         GdkPixbuf *pixbuf;
81 };
82
83 struct PsdHeader
84 {
85         guchar  signature[4];  /* file ID, always "8BPS" */
86         guint16 version;       /* version number, always 1 */
87         guchar  resetved[6];
88         guint16 channels;      /* number of color channels (1-24) */
89         guint32 rows;          /* height of image in pixels (1-30000) */
90         guint32 columns;       /* width of image in pixels (1-30000) */
91         guint16 depth;         /* number of bits per channel (1, 8, 16 or 32) */
92         guint16 color_mode;    /* color mode as defined below */
93 };
94
95 constexpr guint PSD_HEADER_SIZE = 26;
96
97 enum PsdColorMode
98 {
99         PSD_MODE_MONO = 0,
100         PSD_MODE_GRAYSCALE = 1,
101         PSD_MODE_INDEXED = 2,
102         PSD_MODE_RGB = 3,
103         PSD_MODE_CMYK = 4,
104         PSD_MODE_MULTICHANNEL = 7,
105         PSD_MODE_DUOTONE = 8,
106         PSD_MODE_LAB = 9,
107 };
108
109 enum PsdCompressionType
110 {
111         PSD_COMPRESSION_NONE = 0,
112         PSD_COMPRESSION_RLE = 1
113 };
114
115 enum PsdReadState
116 {
117         PSD_STATE_HEADER,
118         PSD_STATE_COLOR_MODE_BLOCK,
119         PSD_STATE_RESOURCES_BLOCK,
120         PSD_STATE_LAYERS_BLOCK,
121         PSD_STATE_COMPRESSION,
122         PSD_STATE_LINES_LENGTHS,
123         PSD_STATE_CHANNEL_DATA,
124         PSD_STATE_DONE
125 };
126
127 struct PsdContext
128 {
129         PsdReadState       state;
130
131         GdkPixbuf*         pixbuf;
132         gpointer           user_data;
133
134         guchar*            buffer;
135         guint              bytes_read;
136         guint32            bytes_to_skip;
137         gboolean           bytes_to_skip_known;
138
139         guint32            width;
140         guint32            height;
141         guint16            channels;
142         guint16            depth;
143         guint16            depth_bytes;
144         PsdColorMode       color_mode;
145         PsdCompressionType compression;
146
147         guchar**           ch_bufs;       /* channels buffers */
148         guint              curr_ch;       /* current channel */
149         guint              curr_row;
150         guint              pos;
151         guint16*           lines_lengths;
152         gboolean           finalized;
153 };
154
155
156 guint16 read_uint16 (const guchar* buf)
157 {
158         return (buf[0] << 8) | buf[1];
159 }
160
161 guint32 read_uint32 (const guchar* buf)
162 {
163         return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
164 }
165
166
167 /*
168  * Parse Psdheader from buffer
169  *
170  * str is expected to be at least PSD_HEADER_SIZE long
171  */
172 PsdHeader psd_parse_header (guchar* str)
173 {
174         PsdHeader hd;
175
176         memcpy(hd.signature, str, 4);
177         hd.version = read_uint16(str + 4);
178         hd.channels = read_uint16(str + 12);
179         hd.rows = read_uint32(str + 14);
180         hd.columns = read_uint32(str + 18);
181         hd.depth = read_uint16(str + 22);
182         hd.color_mode = read_uint16(str + 24);
183
184         return hd;
185 }
186
187 /*
188  * Attempts to read bytes_needed bytes from data and stores them in buffer.
189  *
190  * Returns true if there were enough bytes and false otherwise
191  * (which means we need to call feed_buffer again)
192  */
193 gboolean
194 feed_buffer (guchar*        buffer,
195              guint*         bytes_read,
196              const guchar** data,
197              guint*         size,
198              guint          bytes_needed)
199 {
200         guint how_many = bytes_needed - *bytes_read;
201         if (how_many > *size) {
202                 how_many = *size;
203         }
204         memcpy(buffer + *bytes_read, *data, how_many);
205         *bytes_read += how_many;
206         *data += how_many;
207         *size -= how_many;
208         return (*bytes_read == bytes_needed);
209 }
210
211 /*
212  * Attempts to read size of the block and then skip this block.
213  *
214  * Returns true when finishes consuming block data, otherwise false
215  * (false means we need to call skip_block again)
216  */
217 gboolean skip_block (PsdContext* context, const guchar** data, guint* size)
218 {
219         if (!context->bytes_to_skip_known) {
220                 context->bytes_read = 0;
221                 if (feed_buffer(context->buffer, &context->bytes_read, data, size, 4)) {
222                         context->bytes_to_skip = read_uint32(context->buffer);
223                         context->bytes_to_skip_known = TRUE;
224                 } else {
225                         return FALSE;
226                 }
227         }
228
229         if (*size < context->bytes_to_skip)
230                 {
231                 *data += *size;
232                 context->bytes_to_skip -= *size;
233                 *size = 0;
234                 return FALSE;
235                 }
236
237         *size -= context->bytes_to_skip;
238         *data += context->bytes_to_skip;
239         return TRUE;
240 }
241
242 /*
243  * Decodes RLE-compressed data
244  */
245 void decompress_line(const guchar* src, guint line_length, guchar* dest)
246 {
247         guint16 bytes_read = 0;
248         int k;
249         while (bytes_read < line_length)
250                 {
251                 gchar byte = src[bytes_read];
252                 ++bytes_read;
253
254                 if (byte == -128)
255                         {
256                         continue;
257                         }
258
259                 if (byte > -1)
260                         {
261                         gint count = byte + 1;
262
263                         /* copy next count bytes */
264                         for (k = 0; k < count; ++k)
265                                 {
266                                 *dest = src[bytes_read];
267                                 ++dest;
268                                 ++bytes_read;
269                                 }
270                         }
271                 else
272                         {
273                         gint count = -byte + 1;
274
275                         /* copy next byte count times */
276                         guchar next_byte = src[bytes_read];
277                         ++bytes_read;
278                         for (k = 0; k < count; ++k)
279                                 {
280                                 *dest = next_byte;
281                                 ++dest;
282                                 }
283                         }
284                 }
285 }
286
287 void reset_context_buffer(PsdContext* ctx)
288 {
289         ctx->bytes_read = 0;
290         ctx->bytes_to_skip = 0;
291         ctx->bytes_to_skip_known = FALSE;
292 }
293
294 void free_context(PsdContext *ctx)
295 {
296         g_free(ctx->buffer);
297         g_free(ctx->lines_lengths);
298         if (ctx->ch_bufs) {
299                 int i;
300                 for (i = 0; i < ctx->channels; i++) {
301                         g_free(ctx->ch_bufs[i]);
302                 }
303         }
304         g_free(ctx);
305 }
306
307 gboolean ImageLoaderPSD::write(const guchar *buf, gsize &chunk_size, gsize count, GError **)
308 {
309         auto ctx = g_new0(PsdContext, 1);
310         guint i;
311         guint32 j;
312         guint size = count;
313
314         ctx->state = PSD_STATE_HEADER;
315
316         /* we'll allocate larger buffer once we know image size */
317         ctx->buffer = static_cast<guchar *>(g_malloc(PSD_HEADER_SIZE));
318         reset_context_buffer(ctx);
319
320         ctx->ch_bufs = nullptr;
321         ctx->curr_ch = 0;
322         ctx->curr_row = 0;
323         ctx->pos = 0;
324         ctx->lines_lengths = nullptr;
325         ctx->finalized = FALSE;
326
327         while (size > 0) {
328                 switch (ctx->state) {
329                         case PSD_STATE_HEADER:
330                                 if (feed_buffer(
331                                                 ctx->buffer, &ctx->bytes_read,
332                                                 &buf, &size, PSD_HEADER_SIZE))
333                                 {
334                                         PsdHeader hd = psd_parse_header(ctx->buffer);
335
336                                         ctx->width = hd.columns;
337                                         ctx->height = hd.rows;
338                                         ctx->channels = hd.channels;
339                                         ctx->depth = hd.depth;
340                                         ctx->depth_bytes = (ctx->depth/8 > 0 ? ctx->depth/8 : 1);
341                                         ctx->color_mode = static_cast<PsdColorMode>(hd.color_mode);
342
343                                         if (ctx->color_mode != PSD_MODE_RGB
344                                             && ctx->color_mode != PSD_MODE_GRAYSCALE
345                                             && ctx->color_mode != PSD_MODE_CMYK
346                                             && ctx->color_mode != PSD_MODE_DUOTONE
347                                         ) {
348                                                 log_printf("warning: psd - Unsupported color mode\n");
349                                                 free_context(ctx);
350                                                 return FALSE;
351                                         }
352
353                                         if (ctx->depth != 8 && ctx->depth != 16) {
354                                                 log_printf("warning: psd - Unsupported color depth\n");
355                                                 free_context(ctx);
356                                                 return FALSE;
357                                         }
358
359                                         /* we need buffer that can contain one channel data for one
360                                            row in RLE compressed format. 2*width should be enough */
361                                         g_free(ctx->buffer);
362                                         ctx->buffer = static_cast<guchar *>(g_malloc(ctx->width * 2 * ctx->depth_bytes));
363
364                                         /* this will be needed for RLE decompression */
365                                         ctx->lines_lengths =
366                                                 static_cast<guint16 *>(g_malloc(2 * ctx->channels * ctx->height));
367
368                                         ctx->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
369                                                 FALSE, 8, ctx->width, ctx->height);
370
371                                         if (ctx->lines_lengths == nullptr || ctx->buffer == nullptr ||
372                                                 ctx->pixbuf == nullptr)
373                                         {
374                                                 log_printf("warning: Insufficient memory to load PSD image file\n");
375                                                 free_context(ctx);
376                                                 return FALSE;
377                                         }
378
379                                         /* create separate buffers for each channel */
380                                         ctx->ch_bufs = static_cast<guchar **>(g_malloc(sizeof(guchar*) * ctx->channels));
381                                         for (i = 0; i < ctx->channels; i++) {
382                                                 ctx->ch_bufs[i] =
383                                                         static_cast<guchar *>(g_malloc(ctx->width*ctx->height*ctx->depth_bytes));
384
385                                                 if (ctx->ch_bufs[i] == nullptr) {
386                                                 log_printf("warning: Insufficient memory to load PSD image file\n");
387                                                 free_context(ctx);
388                                                 return FALSE;
389                                                 }
390                                         }
391
392                                         ctx->state = PSD_STATE_COLOR_MODE_BLOCK;
393                                         reset_context_buffer(ctx);
394                                 }
395                                 break;
396                         case PSD_STATE_COLOR_MODE_BLOCK:
397                                 if (skip_block(ctx, &buf, &size)) {
398                                         ctx->state = PSD_STATE_RESOURCES_BLOCK;
399                                         reset_context_buffer(ctx);
400                                 }
401                                 break;
402                         case PSD_STATE_RESOURCES_BLOCK:
403                                 if (skip_block(ctx, &buf, &size)) {
404                                         ctx->state = PSD_STATE_LAYERS_BLOCK;
405                                         reset_context_buffer(ctx);
406                                 }
407                                 break;
408                         case PSD_STATE_LAYERS_BLOCK:
409                                 if (skip_block(ctx, &buf, &size)) {
410                                         ctx->state = PSD_STATE_COMPRESSION;
411                                         reset_context_buffer(ctx);
412                                 }
413                                 break;
414                         case PSD_STATE_COMPRESSION:
415                                 if (feed_buffer(ctx->buffer, &ctx->bytes_read, &buf, &size, 2))
416                                 {
417                                         ctx->compression = static_cast<PsdCompressionType>(read_uint16(ctx->buffer));
418
419                                         if (ctx->compression == PSD_COMPRESSION_RLE) {
420                                                 ctx->state = PSD_STATE_LINES_LENGTHS;
421                                                 reset_context_buffer(ctx);
422                                         } else if (ctx->compression == PSD_COMPRESSION_NONE) {
423                                                 ctx->state = PSD_STATE_CHANNEL_DATA;
424                                                 reset_context_buffer(ctx);
425                                         } else {
426                                                 log_printf("warning: psd - Unsupported compression type\n");
427                                                 return FALSE;
428                                         }
429                                 }
430                                 break;
431                         case PSD_STATE_LINES_LENGTHS:
432                                 if (feed_buffer(
433                                                 reinterpret_cast<guchar*>(ctx->lines_lengths), &ctx->bytes_read, &buf,
434                                                  &size, 2 * ctx->height * ctx->channels))
435                                 {
436                                         /* convert from different endianness */
437                                         for (i = 0; i < ctx->height * ctx->channels; i++) {
438                                                 ctx->lines_lengths[i] = read_uint16(
439                                                         reinterpret_cast<guchar*>(&ctx->lines_lengths[i]));
440                                         }
441                                         ctx->state = PSD_STATE_CHANNEL_DATA;
442                                         reset_context_buffer(ctx);
443                                 }
444                                 break;
445                         case PSD_STATE_CHANNEL_DATA:
446                                 {
447                                         guint line_length = ctx->width * ctx->depth_bytes;
448                                         if (ctx->compression == PSD_COMPRESSION_RLE) {
449                                                 line_length = ctx->lines_lengths[
450                                                         ctx->curr_ch * ctx->height + ctx->curr_row];
451                                         }
452
453                                         if (feed_buffer(ctx->buffer, &ctx->bytes_read, &buf, &size,
454                                                         line_length))
455                                         {
456                                                 if (ctx->compression == PSD_COMPRESSION_RLE) {
457                                                         decompress_line(ctx->buffer, line_length,
458                                                                 ctx->ch_bufs[ctx->curr_ch] + ctx->pos
459                                                         );
460                                                 } else {
461                                                         memcpy(ctx->ch_bufs[ctx->curr_ch] + ctx->pos,
462                                                                 ctx->buffer, line_length);
463                                                 }
464
465                                                 ctx->pos += ctx->width * ctx->depth_bytes;
466                                                 ++ctx->curr_row;
467
468                                                 if (ctx->curr_row >= ctx->height) {
469                                                         ++ctx->curr_ch;
470                                                         ctx->curr_row = 0;
471                                                         ctx->pos = 0;
472                                                         if (ctx->curr_ch >= ctx->channels) {
473                                                                 ctx->state = PSD_STATE_DONE;
474                                                         }
475                                                 }
476
477                                                 reset_context_buffer(ctx);
478                                         }
479                                 }
480                                 break;
481                         case PSD_STATE_DONE:
482                         default:
483                                 size = 0;
484                                 break;
485                 }
486         }
487
488         if (ctx->state == PSD_STATE_DONE && !ctx->finalized) {
489                 /* convert or copy channel buffers to our GdkPixbuf */
490                 guchar* pixels = gdk_pixbuf_get_pixels(ctx->pixbuf);
491                 guint b = ctx->depth_bytes;
492
493                 if (ctx->color_mode == PSD_MODE_RGB) {
494                         for (i = 0; i < ctx->height; i++) {
495                                 for (j = 0; j < ctx->width; j++) {
496                                         pixels[3*j+0] = ctx->ch_bufs[0][ctx->width*i*b + j*b];
497                                         pixels[3*j+1] = ctx->ch_bufs[1][ctx->width*i*b + j*b];
498                                         pixels[3*j+2] = ctx->ch_bufs[2][ctx->width*i*b + j*b];
499                                 }
500                                 pixels += gdk_pixbuf_get_rowstride(ctx->pixbuf);
501                         }
502                 } else if (ctx->color_mode == PSD_MODE_GRAYSCALE ||
503                            ctx->color_mode == PSD_MODE_DUOTONE)
504                 {
505                         for (i = 0; i < ctx->height; i++) {
506                                 for (j = 0; j < ctx->width; j++) {
507                                         pixels[3*j+0] = pixels[3*j+1] = pixels[3*j+2] =
508                                                 ctx->ch_bufs[0][ctx->width*i*b + j*b];
509                                 }
510                                 pixels += gdk_pixbuf_get_rowstride(ctx->pixbuf);
511                         }
512                 } else if (ctx->color_mode == PSD_MODE_CMYK) {
513                         /* unfortunately, this doesn't work 100% correctly...
514                            CMYK-RGB conversion distorts colors significantly  */
515
516                         guchar* pixels = gdk_pixbuf_get_pixels(ctx->pixbuf);
517                         for (i = 0; i < ctx->height; i++) {
518                                 for (j = 0; j < ctx->width; j++) {
519                                         double c = 1.0 -
520                                                 static_cast<double>(ctx->ch_bufs[0][ctx->width*i + j]) / 255.0;
521                                         double m = 1.0 -
522                                                 static_cast<double>(ctx->ch_bufs[1][ctx->width*i + j]) / 255.0;
523                                         double y = 1.0 -
524                                                 static_cast<double>(ctx->ch_bufs[2][ctx->width*i + j]) / 255.0;
525                                         double k = 1.0 -
526                                                 static_cast<double>(ctx->ch_bufs[3][ctx->width*i + j]) / 255.0;
527
528                                         pixels[3*j+0] = (1.0 - (c * (1.0 - k) + k)) * 255.0;
529                                         pixels[3*j+1] = (1.0 - (m * (1.0 - k) + k)) * 255.0;
530                                         pixels[3*j+2] = (1.0 - (y * (1.0 - k) + k)) * 255.0;
531                                 }
532                                 pixels += gdk_pixbuf_get_rowstride(ctx->pixbuf);
533                         }
534                 }
535                 ctx->finalized = TRUE;
536                 pixbuf = ctx->pixbuf;
537                 area_updated_cb(nullptr, 0, 0, ctx->width, ctx->height, data);
538                 free_context(ctx);
539
540                 chunk_size = count;
541                 return TRUE;
542         }
543
544         free_context(ctx);
545         return FALSE;
546 }
547
548 /* ------- Geeqie ------------ */
549
550 void ImageLoaderPSD::init(AreaUpdatedCb area_updated_cb, SizePreparedCb, AreaPreparedCb, gpointer data)
551 {
552         this->area_updated_cb = area_updated_cb;
553         this->data = data;
554 }
555
556 GdkPixbuf *ImageLoaderPSD::get_pixbuf()
557 {
558         return pixbuf;
559 }
560
561 gchar *ImageLoaderPSD::get_format_name()
562 {
563         return g_strdup("psd");
564 }
565
566 gchar **ImageLoaderPSD::get_format_mime_types()
567 {
568         static const gchar *mime[] = {"application/psd", nullptr};
569         return g_strdupv(const_cast<gchar **>(mime));
570 }
571
572 ImageLoaderPSD::~ImageLoaderPSD()
573 {
574         if (pixbuf) g_object_unref(pixbuf);
575 }
576
577 } // namespace
578
579 std::unique_ptr<ImageLoaderBackend> get_image_loader_backend_psd()
580 {
581         return std::make_unique<ImageLoaderPSD>();
582 }
583 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */