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