Fix warnings in exiv2
[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 enum {
96         PSD_HEADER_SIZE = 26
97 };
98
99 enum PsdColorMode
100 {
101         PSD_MODE_MONO = 0,
102         PSD_MODE_GRAYSCALE = 1,
103         PSD_MODE_INDEXED = 2,
104         PSD_MODE_RGB = 3,
105         PSD_MODE_CMYK = 4,
106         PSD_MODE_MULTICHANNEL = 7,
107         PSD_MODE_DUOTONE = 8,
108         PSD_MODE_LAB = 9,
109 };
110
111 enum PsdCompressionType
112 {
113         PSD_COMPRESSION_NONE = 0,
114         PSD_COMPRESSION_RLE = 1
115 };
116
117 enum PsdReadState
118 {
119         PSD_STATE_HEADER,
120         PSD_STATE_COLOR_MODE_BLOCK,
121         PSD_STATE_RESOURCES_BLOCK,
122         PSD_STATE_LAYERS_BLOCK,
123         PSD_STATE_COMPRESSION,
124         PSD_STATE_LINES_LENGTHS,
125         PSD_STATE_CHANNEL_DATA,
126         PSD_STATE_DONE
127 };
128
129 struct PsdContext
130 {
131         PsdReadState       state;
132
133         GdkPixbuf*         pixbuf;
134         gpointer           user_data;
135
136         guchar*            buffer;
137         guint              bytes_read;
138         guint32            bytes_to_skip;
139         gboolean           bytes_to_skip_known;
140
141         guint32            width;
142         guint32            height;
143         guint16            channels;
144         guint16            depth;
145         guint16            depth_bytes;
146         PsdColorMode       color_mode;
147         PsdCompressionType compression;
148
149         guchar**           ch_bufs;       /* channels buffers */
150         guint              curr_ch;       /* current channel */
151         guint              curr_row;
152         guint              pos;
153         guint16*           lines_lengths;
154         gboolean           finalized;
155 };
156
157
158 guint16 read_uint16 (const guchar* buf)
159 {
160         return (buf[0] << 8) | buf[1];
161 }
162
163 guint32 read_uint32 (const guchar* buf)
164 {
165         return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
166 }
167
168
169 /*
170  * Parse Psdheader from buffer
171  *
172  * str is expected to be at least PSD_HEADER_SIZE long
173  */
174 PsdHeader psd_parse_header (guchar* str)
175 {
176         PsdHeader hd;
177
178         memcpy(hd.signature, str, 4);
179         hd.version = read_uint16(str + 4);
180         hd.channels = read_uint16(str + 12);
181         hd.rows = read_uint32(str + 14);
182         hd.columns = read_uint32(str + 18);
183         hd.depth = read_uint16(str + 22);
184         hd.color_mode = read_uint16(str + 24);
185
186         return hd;
187 }
188
189 /*
190  * Attempts to read bytes_needed bytes from data and stores them in buffer.
191  *
192  * Returns true if there were enough bytes and false otherwise
193  * (which means we need to call feed_buffer again)
194  */
195 gboolean
196 feed_buffer (guchar*        buffer,
197              guint*         bytes_read,
198              const guchar** data,
199              guint*         size,
200              guint          bytes_needed)
201 {
202         guint how_many = bytes_needed - *bytes_read;
203         if (how_many > *size) {
204                 how_many = *size;
205         }
206         memcpy(buffer + *bytes_read, *data, how_many);
207         *bytes_read += how_many;
208         *data += how_many;
209         *size -= how_many;
210         return (*bytes_read == bytes_needed);
211 }
212
213 /*
214  * Attempts to read size of the block and then skip this block.
215  *
216  * Returns true when finishes consuming block data, otherwise false
217  * (false means we need to call skip_block again)
218  */
219 gboolean skip_block (PsdContext* context, const guchar** data, guint* size)
220 {
221         if (!context->bytes_to_skip_known) {
222                 context->bytes_read = 0;
223                 if (feed_buffer(context->buffer, &context->bytes_read, data, size, 4)) {
224                         context->bytes_to_skip = read_uint32(context->buffer);
225                         context->bytes_to_skip_known = TRUE;
226                 } else {
227                         return FALSE;
228                 }
229         }
230
231         if (*size < context->bytes_to_skip)
232                 {
233                 *data += *size;
234                 context->bytes_to_skip -= *size;
235                 *size = 0;
236                 return FALSE;
237                 }
238
239         *size -= context->bytes_to_skip;
240         *data += context->bytes_to_skip;
241         return TRUE;
242 }
243
244 /*
245  * Decodes RLE-compressed data
246  */
247 void decompress_line(const guchar* src, guint line_length, guchar* dest)
248 {
249         guint16 bytes_read = 0;
250         int k;
251         while (bytes_read < line_length)
252                 {
253                 gchar byte = src[bytes_read];
254                 ++bytes_read;
255
256                 if (byte == -128)
257                         {
258                         continue;
259                         }
260
261                 if (byte > -1)
262                         {
263                         gint count = byte + 1;
264
265                         /* copy next count bytes */
266                         for (k = 0; k < count; ++k)
267                                 {
268                                 *dest = src[bytes_read];
269                                 ++dest;
270                                 ++bytes_read;
271                                 }
272                         }
273                 else
274                         {
275                         gint count = -byte + 1;
276
277                         /* copy next byte count times */
278                         guchar next_byte = src[bytes_read];
279                         ++bytes_read;
280                         for (k = 0; k < count; ++k)
281                                 {
282                                 *dest = next_byte;
283                                 ++dest;
284                                 }
285                         }
286                 }
287 }
288
289 void reset_context_buffer(PsdContext* ctx)
290 {
291         ctx->bytes_read = 0;
292         ctx->bytes_to_skip = 0;
293         ctx->bytes_to_skip_known = FALSE;
294 }
295
296 void free_context(PsdContext *ctx)
297 {
298         g_free(ctx->buffer);
299         g_free(ctx->lines_lengths);
300         if (ctx->ch_bufs) {
301                 int i;
302                 for (i = 0; i < ctx->channels; i++) {
303                         g_free(ctx->ch_bufs[i]);
304                 }
305         }
306         g_free(ctx);
307 }
308
309 gboolean ImageLoaderPSD::write(const guchar *buf, gsize &chunk_size, gsize count, GError **)
310 {
311         auto ctx = g_new0(PsdContext, 1);
312         guint i;
313         guint32 j;
314         guint size = count;
315
316         ctx->state = PSD_STATE_HEADER;
317
318         /* we'll allocate larger buffer once we know image size */
319         ctx->buffer = static_cast<guchar *>(g_malloc(PSD_HEADER_SIZE));
320         reset_context_buffer(ctx);
321
322         ctx->ch_bufs = nullptr;
323         ctx->curr_ch = 0;
324         ctx->curr_row = 0;
325         ctx->pos = 0;
326         ctx->lines_lengths = nullptr;
327         ctx->finalized = FALSE;
328
329         while (size > 0) {
330                 switch (ctx->state) {
331                         case PSD_STATE_HEADER:
332                                 if (feed_buffer(
333                                                 ctx->buffer, &ctx->bytes_read,
334                                                 &buf, &size, PSD_HEADER_SIZE))
335                                 {
336                                         PsdHeader hd = psd_parse_header(ctx->buffer);
337
338                                         ctx->width = hd.columns;
339                                         ctx->height = hd.rows;
340                                         ctx->channels = hd.channels;
341                                         ctx->depth = hd.depth;
342                                         ctx->depth_bytes = (ctx->depth/8 > 0 ? ctx->depth/8 : 1);
343                                         ctx->color_mode = static_cast<PsdColorMode>(hd.color_mode);
344
345                                         if (ctx->color_mode != PSD_MODE_RGB
346                                             && ctx->color_mode != PSD_MODE_GRAYSCALE
347                                             && ctx->color_mode != PSD_MODE_CMYK
348                                             && ctx->color_mode != PSD_MODE_DUOTONE
349                                         ) {
350                                                 log_printf("warning: psd - Unsupported color mode\n");
351                                                 free_context(ctx);
352                                                 return FALSE;
353                                         }
354
355                                         if (ctx->depth != 8 && ctx->depth != 16) {
356                                                 log_printf("warning: psd - Unsupported color depth\n");
357                                                 free_context(ctx);
358                                                 return FALSE;
359                                         }
360
361                                         /* we need buffer that can contain one channel data for one
362                                            row in RLE compressed format. 2*width should be enough */
363                                         g_free(ctx->buffer);
364                                         ctx->buffer = static_cast<guchar *>(g_malloc(ctx->width * 2 * ctx->depth_bytes));
365
366                                         /* this will be needed for RLE decompression */
367                                         ctx->lines_lengths =
368                                                 static_cast<guint16 *>(g_malloc(2 * ctx->channels * ctx->height));
369
370                                         ctx->pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB,
371                                                 FALSE, 8, ctx->width, ctx->height);
372
373                                         if (ctx->lines_lengths == nullptr || ctx->buffer == nullptr ||
374                                                 ctx->pixbuf == nullptr)
375                                         {
376                                                 log_printf("warning: Insufficient memory to load PSD image file\n");
377                                                 free_context(ctx);
378                                                 return FALSE;
379                                         }
380
381                                         /* create separate buffers for each channel */
382                                         ctx->ch_bufs = static_cast<guchar **>(g_malloc(sizeof(guchar*) * ctx->channels));
383                                         for (i = 0; i < ctx->channels; i++) {
384                                                 ctx->ch_bufs[i] =
385                                                         static_cast<guchar *>(g_malloc(ctx->width*ctx->height*ctx->depth_bytes));
386
387                                                 if (ctx->ch_bufs[i] == nullptr) {
388                                                 log_printf("warning: Insufficient memory to load PSD image file\n");
389                                                 free_context(ctx);
390                                                 return FALSE;
391                                                 }
392                                         }
393
394                                         ctx->state = PSD_STATE_COLOR_MODE_BLOCK;
395                                         reset_context_buffer(ctx);
396                                 }
397                                 break;
398                         case PSD_STATE_COLOR_MODE_BLOCK:
399                                 if (skip_block(ctx, &buf, &size)) {
400                                         ctx->state = PSD_STATE_RESOURCES_BLOCK;
401                                         reset_context_buffer(ctx);
402                                 }
403                                 break;
404                         case PSD_STATE_RESOURCES_BLOCK:
405                                 if (skip_block(ctx, &buf, &size)) {
406                                         ctx->state = PSD_STATE_LAYERS_BLOCK;
407                                         reset_context_buffer(ctx);
408                                 }
409                                 break;
410                         case PSD_STATE_LAYERS_BLOCK:
411                                 if (skip_block(ctx, &buf, &size)) {
412                                         ctx->state = PSD_STATE_COMPRESSION;
413                                         reset_context_buffer(ctx);
414                                 }
415                                 break;
416                         case PSD_STATE_COMPRESSION:
417                                 if (feed_buffer(ctx->buffer, &ctx->bytes_read, &buf, &size, 2))
418                                 {
419                                         ctx->compression = static_cast<PsdCompressionType>(read_uint16(ctx->buffer));
420
421                                         if (ctx->compression == PSD_COMPRESSION_RLE) {
422                                                 ctx->state = PSD_STATE_LINES_LENGTHS;
423                                                 reset_context_buffer(ctx);
424                                         } else if (ctx->compression == PSD_COMPRESSION_NONE) {
425                                                 ctx->state = PSD_STATE_CHANNEL_DATA;
426                                                 reset_context_buffer(ctx);
427                                         } else {
428                                                 log_printf("warning: psd - Unsupported compression type\n");
429                                                 return FALSE;
430                                         }
431                                 }
432                                 break;
433                         case PSD_STATE_LINES_LENGTHS:
434                                 if (feed_buffer(
435                                                 reinterpret_cast<guchar*>(ctx->lines_lengths), &ctx->bytes_read, &buf,
436                                                  &size, 2 * ctx->height * ctx->channels))
437                                 {
438                                         /* convert from different endianness */
439                                         for (i = 0; i < ctx->height * ctx->channels; i++) {
440                                                 ctx->lines_lengths[i] = read_uint16(
441                                                         reinterpret_cast<guchar*>(&ctx->lines_lengths[i]));
442                                         }
443                                         ctx->state = PSD_STATE_CHANNEL_DATA;
444                                         reset_context_buffer(ctx);
445                                 }
446                                 break;
447                         case PSD_STATE_CHANNEL_DATA:
448                                 {
449                                         guint line_length = ctx->width * ctx->depth_bytes;
450                                         if (ctx->compression == PSD_COMPRESSION_RLE) {
451                                                 line_length = ctx->lines_lengths[
452                                                         ctx->curr_ch * ctx->height + ctx->curr_row];
453                                         }
454
455                                         if (feed_buffer(ctx->buffer, &ctx->bytes_read, &buf, &size,
456                                                         line_length))
457                                         {
458                                                 if (ctx->compression == PSD_COMPRESSION_RLE) {
459                                                         decompress_line(ctx->buffer, line_length,
460                                                                 ctx->ch_bufs[ctx->curr_ch] + ctx->pos
461                                                         );
462                                                 } else {
463                                                         memcpy(ctx->ch_bufs[ctx->curr_ch] + ctx->pos,
464                                                                 ctx->buffer, line_length);
465                                                 }
466
467                                                 ctx->pos += ctx->width * ctx->depth_bytes;
468                                                 ++ctx->curr_row;
469
470                                                 if (ctx->curr_row >= ctx->height) {
471                                                         ++ctx->curr_ch;
472                                                         ctx->curr_row = 0;
473                                                         ctx->pos = 0;
474                                                         if (ctx->curr_ch >= ctx->channels) {
475                                                                 ctx->state = PSD_STATE_DONE;
476                                                         }
477                                                 }
478
479                                                 reset_context_buffer(ctx);
480                                         }
481                                 }
482                                 break;
483                         case PSD_STATE_DONE:
484                         default:
485                                 size = 0;
486                                 break;
487                 }
488         }
489
490         if (ctx->state == PSD_STATE_DONE && !ctx->finalized) {
491                 /* convert or copy channel buffers to our GdkPixbuf */
492                 guchar* pixels = gdk_pixbuf_get_pixels(ctx->pixbuf);
493                 guint b = ctx->depth_bytes;
494
495                 if (ctx->color_mode == PSD_MODE_RGB) {
496                         for (i = 0; i < ctx->height; i++) {
497                                 for (j = 0; j < ctx->width; j++) {
498                                         pixels[3*j+0] = ctx->ch_bufs[0][ctx->width*i*b + j*b];
499                                         pixels[3*j+1] = ctx->ch_bufs[1][ctx->width*i*b + j*b];
500                                         pixels[3*j+2] = ctx->ch_bufs[2][ctx->width*i*b + j*b];
501                                 }
502                                 pixels += gdk_pixbuf_get_rowstride(ctx->pixbuf);
503                         }
504                 } else if (ctx->color_mode == PSD_MODE_GRAYSCALE ||
505                            ctx->color_mode == PSD_MODE_DUOTONE)
506                 {
507                         for (i = 0; i < ctx->height; i++) {
508                                 for (j = 0; j < ctx->width; j++) {
509                                         pixels[3*j+0] = pixels[3*j+1] = pixels[3*j+2] =
510                                                 ctx->ch_bufs[0][ctx->width*i*b + j*b];
511                                 }
512                                 pixels += gdk_pixbuf_get_rowstride(ctx->pixbuf);
513                         }
514                 } else if (ctx->color_mode == PSD_MODE_CMYK) {
515                         /* unfortunately, this doesn't work 100% correctly...
516                            CMYK-RGB conversion distorts colors significantly  */
517
518                         guchar* pixels = gdk_pixbuf_get_pixels(ctx->pixbuf);
519                         for (i = 0; i < ctx->height; i++) {
520                                 for (j = 0; j < ctx->width; j++) {
521                                         double c = 1.0 -
522                                                 static_cast<double>(ctx->ch_bufs[0][ctx->width*i + j]) / 255.0;
523                                         double m = 1.0 -
524                                                 static_cast<double>(ctx->ch_bufs[1][ctx->width*i + j]) / 255.0;
525                                         double y = 1.0 -
526                                                 static_cast<double>(ctx->ch_bufs[2][ctx->width*i + j]) / 255.0;
527                                         double k = 1.0 -
528                                                 static_cast<double>(ctx->ch_bufs[3][ctx->width*i + j]) / 255.0;
529
530                                         pixels[3*j+0] = (1.0 - (c * (1.0 - k) + k)) * 255.0;
531                                         pixels[3*j+1] = (1.0 - (m * (1.0 - k) + k)) * 255.0;
532                                         pixels[3*j+2] = (1.0 - (y * (1.0 - k) + k)) * 255.0;
533                                 }
534                                 pixels += gdk_pixbuf_get_rowstride(ctx->pixbuf);
535                         }
536                 }
537                 ctx->finalized = TRUE;
538                 pixbuf = ctx->pixbuf;
539                 area_updated_cb(nullptr, 0, 0, ctx->width, ctx->height, data);
540                 free_context(ctx);
541
542                 chunk_size = count;
543                 return TRUE;
544         }
545
546         free_context(ctx);
547         return FALSE;
548 }
549
550 /* ------- Geeqie ------------ */
551
552 void ImageLoaderPSD::init(AreaUpdatedCb area_updated_cb, SizePreparedCb, AreaPreparedCb, gpointer data)
553 {
554         this->area_updated_cb = area_updated_cb;
555         this->data = data;
556 }
557
558 GdkPixbuf *ImageLoaderPSD::get_pixbuf()
559 {
560         return pixbuf;
561 }
562
563 gchar *ImageLoaderPSD::get_format_name()
564 {
565         return g_strdup("psd");
566 }
567
568 gchar **ImageLoaderPSD::get_format_mime_types()
569 {
570         static const gchar *mime[] = {"application/psd", nullptr};
571         return g_strdupv(const_cast<gchar **>(mime));
572 }
573
574 ImageLoaderPSD::~ImageLoaderPSD()
575 {
576         if (pixbuf) g_object_unref(pixbuf);
577 }
578
579 } // namespace
580
581 std::unique_ptr<ImageLoaderBackend> get_image_loader_backend_psd()
582 {
583         return std::make_unique<ImageLoaderPSD>();
584 }
585 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */