Coverage Report

Created: 2025-11-16 06:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdk-pixbuf/gdk-pixbuf/io-qtif.c
Line
Count
Source
1
/* -*- mode: C; c-file-style: "linux" -*- */
2
/* GdkPixbuf library - QTIF image loader
3
 *
4
 * This module extracts image data from QTIF format and uses
5
 * other GDK pixbuf modules to decode the image data.
6
 *
7
 * Copyright (C) 2008 Kevin Peng
8
 *
9
 * Authors: Kevin Peng <kevin@zycomtech.com>
10
 *
11
 * This library is free software; you can redistribute it and/or
12
 * modify it under the terms of the GNU Lesser General Public
13
 * License as published by the Free Software Foundation; either
14
 * version 2 of the License, or (at your option) any later version.
15
 *
16
 * This library is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
 * Lesser General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Lesser General Public
22
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
26
#include "config.h"
27
#include <errno.h>
28
#include <libintl.h>
29
#include <stdio.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <setjmp.h>
33
#include <glib/gi18n-lib.h>
34
#include "gdk-pixbuf.h"
35
36
/***
37
 * Definitions
38
 */
39
/* Read buffer size */
40
0
#define READ_BUFFER_SIZE    8192
41
42
/* Only allow atom of size up to 10MB. */
43
0
#define ATOM_SIZE_MAX       100000000
44
45
/* Aborts after going to through this many atoms. */
46
0
#define QTIF_ATOM_COUNT_MAX 10u
47
48
/* QTIF static image data tag "idat". */
49
0
#define QTIF_TAG_IDATA      0x69646174u
50
51
52
/***
53
 * Types
54
 */
55
/* QTIF State */
56
typedef enum {
57
    STATE_READY,
58
    STATE_DATA,
59
    STATE_OTHER
60
} QTIFState;
61
62
/* QTIF Atom Header */
63
typedef struct {
64
    guint32 length;
65
    guint32 tag;
66
} QtHeader;
67
68
/* QTIF loader context */
69
typedef struct {
70
    GdkPixbufLoader *loader;
71
    gpointer        user_data;
72
    QTIFState       state;
73
    guint32         run_length;
74
    gint            atom_count;
75
76
    guchar          header_buffer[sizeof(QtHeader)];
77
78
    GdkPixbufModuleSizeFunc     size_func;
79
    GdkPixbufModulePreparedFunc prepared_func;
80
    GdkPixbufModuleUpdatedFunc  updated_func;
81
    gint            cb_prepare_count;
82
    gint            cb_update_count;
83
} QTIFContext;
84
85
/***
86
 * Local function prototypes
87
 */
88
static GdkPixbuf *gdk_pixbuf__qtif_image_load (FILE *f, GError **error);
89
static gpointer gdk_pixbuf__qtif_image_begin_load (GdkPixbufModuleSizeFunc size_func,
90
                                                   GdkPixbufModulePreparedFunc prepared_func,
91
                                                   GdkPixbufModuleUpdatedFunc updated_func,
92
                                                   gpointer user_data,
93
                                                   GError **error);
94
static gboolean gdk_pixbuf__qtif_image_stop_load (gpointer context, GError **error);
95
static gboolean gdk_pixbuf__qtif_image_load_increment(gpointer context,
96
                                                      const guchar *buf, guint size,
97
                                                      GError **error);
98
static gboolean gdk_pixbuf__qtif_image_create_loader (QTIFContext *context, GError **error);
99
static gboolean gdk_pixbuf__qtif_image_free_loader (QTIFContext *context, GError **error);
100
101
static void gdk_pixbuf__qtif_cb_size_prepared(GdkPixbufLoader *loader,
102
                                              gint width,
103
                                              gint height,
104
                                              gpointer user_data);
105
static void gdk_pixbuf__qtif_cb_area_prepared(GdkPixbufLoader *loader, gpointer user_data);
106
static void gdk_pixbuf__qtif_cb_area_updated(GdkPixbufLoader *loader,
107
                                             gint x,
108
                                             gint y,
109
                                             gint width,
110
                                             gint height,
111
                                             gpointer user_data);
112
113
/***
114
 * Function definitions.
115
 */
116
117
/* Load QTIF from a file handler. */
118
static GdkPixbuf *gdk_pixbuf__qtif_image_load (FILE *f, GError **error)
119
0
{
120
0
    guint count;
121
122
0
    if(f == NULL)
123
0
    {
124
0
        g_set_error_literal (error, GDK_PIXBUF_ERROR,
125
0
                             GDK_PIXBUF_ERROR_BAD_OPTION,
126
0
                             _("Input file descriptor is NULL."));
127
0
        return NULL;
128
0
    }
129
130
0
    for(count = QTIF_ATOM_COUNT_MAX; count != 0u; count--)
131
0
    {
132
0
        QtHeader hdr;
133
0
        size_t rd;
134
135
        /* Read QtHeader. */
136
0
        rd = fread(&hdr, 1, sizeof(QtHeader), f);
137
0
        if(rd != sizeof(QtHeader))
138
0
        {
139
0
            g_set_error_literal(error, GDK_PIXBUF_ERROR,
140
0
                                GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
141
0
                                _("Failed to read QTIF header"));
142
0
            return NULL;
143
0
        }
144
145
0
        hdr.length = GUINT32_FROM_BE(hdr.length) - sizeof(QtHeader);
146
0
        if(hdr.length > ATOM_SIZE_MAX)
147
0
        {
148
0
            g_set_error(error, GDK_PIXBUF_ERROR,
149
0
                        GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
150
0
                        ngettext (  "QTIF atom size too large (%d byte)",
151
0
                                    "QTIF atom size too large (%d bytes)",
152
0
                                    hdr.length),
153
0
                        hdr.length);
154
0
            return NULL;
155
0
        }
156
157
0
        switch(GUINT32_FROM_BE(hdr.tag))
158
0
        {
159
0
        case QTIF_TAG_IDATA: /* "idat" data atom. */
160
0
            {
161
                /* Load image using GdkPixbufLoader. */
162
0
                guchar *buf;
163
0
                GdkPixbufLoader *loader;
164
0
                GdkPixbuf *pixbuf = NULL;
165
0
                GError *tmp = NULL;
166
167
                /* Allocate read buffer. */
168
0
                buf = g_try_malloc(READ_BUFFER_SIZE);
169
0
                if(buf == NULL)
170
0
                {
171
0
                    g_set_error(error, GDK_PIXBUF_ERROR,
172
0
                                GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
173
0
                                ngettext ( "Failed to allocate %d byte for file read buffer",
174
0
                                           "Failed to allocate %d bytes for file read buffer",
175
0
                                           READ_BUFFER_SIZE
176
0
                                ),
177
0
                                READ_BUFFER_SIZE);
178
0
                    return NULL;
179
0
                }
180
181
                /* Create GdkPixbufLoader. */
182
0
                loader = gdk_pixbuf_loader_new();
183
0
                if(loader == NULL)
184
0
                {
185
0
                    g_set_error(error, GDK_PIXBUF_ERROR,
186
0
                                GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
187
0
                                ngettext (  "QTIF atom size too large (%d byte)",
188
0
                                            "QTIF atom size too large (%d bytes)",
189
0
                                            hdr.length),
190
0
                                hdr.length);
191
0
                    goto clean_up;
192
0
                }
193
194
                /* Read atom data. */
195
0
                while(hdr.length != 0u)
196
0
                {
197
0
                    if(fread(buf, 1, rd, f) != rd)
198
0
                    {
199
0
                        g_set_error(error, GDK_PIXBUF_ERROR,
200
0
                                    GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
201
0
                                    _("File error when reading QTIF atom: %s"), g_strerror(errno));
202
0
                        break;
203
0
                    }
204
205
0
                    if(!gdk_pixbuf_loader_write(loader, buf, rd, &tmp))
206
0
                    {
207
0
                        g_propagate_error (error, tmp);
208
0
                        break;
209
0
                    }
210
0
                    hdr.length -= rd;
211
0
                }
212
213
0
clean_up:
214
                /* Release loader */
215
0
                if(loader != NULL)
216
0
                {
217
0
                    gdk_pixbuf_loader_close(loader, NULL);
218
0
                    pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
219
0
                    if(pixbuf != NULL)
220
0
                    {
221
0
                        g_object_ref(pixbuf);
222
0
                    }
223
0
                    g_object_unref(loader);
224
0
                }
225
0
                if(buf != NULL)
226
0
                {
227
0
                    g_free(buf);
228
0
                }
229
0
                return pixbuf;
230
0
            }
231
232
0
        default:
233
            /* Skip any other types of atom. */
234
0
            if(!fseek(f, hdr.length, SEEK_CUR))
235
0
            {
236
0
                g_set_error(error, GDK_PIXBUF_ERROR,
237
0
                            GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
238
0
                            ngettext (  "Failed to skip the next %d byte with seek().",
239
0
                                        "Failed to skip the next %d bytes with seek().",
240
0
                                        hdr.length),
241
0
                            hdr.length);
242
0
                return NULL;
243
0
            }
244
0
            break;
245
0
        }
246
0
    }
247
0
    return NULL;
248
0
}
249
250
/* Incremental load begin. */
251
static gpointer gdk_pixbuf__qtif_image_begin_load (GdkPixbufModuleSizeFunc size_func,
252
                                                   GdkPixbufModulePreparedFunc prepared_func,
253
                                                   GdkPixbufModuleUpdatedFunc updated_func,
254
                                                   gpointer user_data,
255
                                                   GError **error)
256
0
{
257
0
    QTIFContext *context;
258
259
0
    g_assert (size_func != NULL);
260
0
    g_assert (prepared_func != NULL);
261
0
    g_assert (updated_func != NULL);
262
263
    /* Create context struct. */
264
0
    context = g_new0(QTIFContext, 1);
265
0
    if(context == NULL)
266
0
    {
267
0
        g_set_error_literal (error, GDK_PIXBUF_ERROR,
268
0
                             GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
269
0
                             _("Failed to allocate QTIF context structure."));
270
0
        return NULL;
271
0
    }
272
273
    /* Fill context parameters. */
274
0
    context->loader = NULL;
275
0
    context->user_data = user_data;
276
0
    context->state = STATE_READY;
277
0
    context->run_length = 0u;
278
0
    context->atom_count = QTIF_ATOM_COUNT_MAX;
279
0
    context->size_func = size_func;
280
0
    context->prepared_func = prepared_func;
281
0
    context->updated_func = updated_func;
282
283
0
    return context;
284
0
}
285
286
/* Incremental load clean up. */
287
static gboolean gdk_pixbuf__qtif_image_stop_load (gpointer data, GError **error)
288
0
{
289
0
    QTIFContext *context = (QTIFContext *)data;
290
0
    gboolean ret = TRUE;
291
292
0
    if(context->loader != NULL)
293
0
    {
294
0
        GError *tmp = NULL;
295
296
0
        ret = gdk_pixbuf__qtif_image_free_loader(context, &tmp);
297
0
        if(!ret)
298
0
        {
299
0
            g_propagate_error (error, tmp);
300
0
        }
301
0
    }
302
0
    g_free(context);
303
304
0
    return ret;
305
0
}
306
307
/* Create a new GdkPixbufLoader and connect to its signals. */
308
static gboolean gdk_pixbuf__qtif_image_create_loader (QTIFContext *context, GError **error)
309
0
{
310
0
    GError *tmp = NULL;
311
312
0
    if(context == NULL)
313
0
    {
314
0
        return FALSE;
315
0
    }
316
317
    /* Free existing loader. */
318
0
    if(context->loader != NULL)
319
0
    {
320
0
        gdk_pixbuf__qtif_image_free_loader(context, &tmp);
321
0
    }
322
323
    /* Create GdkPixbufLoader object. */
324
0
    context->loader = gdk_pixbuf_loader_new();
325
0
    if(context->loader == NULL)
326
0
    {
327
0
        g_set_error_literal (error, GDK_PIXBUF_ERROR,
328
0
                             GDK_PIXBUF_ERROR_FAILED,
329
0
                             _("Failed to create GdkPixbufLoader object."));
330
0
        return FALSE;
331
0
    }
332
333
    /* Connect signals. */
334
0
    context->cb_prepare_count = 0;
335
0
    context->cb_update_count = 0;
336
0
    g_signal_connect(context->loader, "size-prepared",
337
0
         G_CALLBACK(gdk_pixbuf__qtif_cb_size_prepared),
338
0
         context);
339
0
    g_signal_connect(context->loader, "area-prepared",
340
0
         G_CALLBACK(gdk_pixbuf__qtif_cb_area_prepared),
341
0
         context);
342
0
    g_signal_connect(context->loader, "area-updated",
343
0
         G_CALLBACK(gdk_pixbuf__qtif_cb_area_updated),
344
0
         context);
345
0
    return TRUE;
346
0
}
347
348
/* Free the GdkPixbufLoader and perform callback if haven't done so. */
349
static gboolean gdk_pixbuf__qtif_image_free_loader (QTIFContext *context, GError **error)
350
0
{
351
0
    GdkPixbuf *pixbuf;
352
0
    GError *tmp = NULL;
353
0
    gboolean ret;
354
355
0
    if((context == NULL) || (context->loader == NULL))
356
0
    {
357
0
        return FALSE;
358
0
    }
359
360
    /* Close GdkPixbufLoader. */
361
0
    ret = gdk_pixbuf_loader_close(context->loader, &tmp);
362
0
    if(!ret)
363
0
    {
364
0
        g_propagate_error (error, tmp);
365
0
    }
366
367
368
    /* Get GdkPixbuf from GdkPixbufLoader. */
369
0
    pixbuf = gdk_pixbuf_loader_get_pixbuf(context->loader);
370
0
    if(pixbuf != NULL)
371
0
    {
372
0
        g_object_ref(pixbuf);
373
0
    }
374
375
    /* Free GdkPixbufLoader. */
376
0
    g_object_ref(context->loader);
377
0
    context->loader = NULL;
378
379
0
    if(pixbuf != NULL)
380
0
    {
381
        /* Callback functions should be called for at least once. */
382
0
        if(context->cb_prepare_count == 0)
383
0
        {
384
0
            (context->prepared_func)(pixbuf, NULL, context->user_data);
385
0
        }
386
387
0
        if(context->cb_update_count == 0)
388
0
        {
389
0
            gint width;
390
0
            gint height;
391
392
0
            width = gdk_pixbuf_get_width(pixbuf);
393
0
            height = gdk_pixbuf_get_height(pixbuf);
394
0
            (context->updated_func)(pixbuf, 0, 0, width, height, context->user_data);
395
0
        }
396
397
        /* Free GdkPixbuf (callback function should ref it). */
398
0
        g_object_ref(pixbuf);
399
0
    }
400
401
0
    return ret;
402
0
}
403
404
405
/* Incrementally load the next chunk of data. */
406
static gboolean gdk_pixbuf__qtif_image_load_increment (gpointer data,
407
                                                       const guchar *buf, guint size,
408
                                                       GError **error)
409
0
{
410
0
    QTIFContext *context = (QTIFContext *)data;
411
0
    GError *tmp = NULL;
412
0
    gboolean ret = TRUE; /* Return TRUE for insufficient data. */
413
414
0
    while(ret && (size != 0u))
415
0
    {
416
0
        switch(context->state)
417
0
        {
418
0
        case STATE_READY:
419
            /* Abort if we have seen too many atoms. */
420
0
            if(context->atom_count == 0u)
421
0
            {
422
0
                g_set_error_literal (error, GDK_PIXBUF_ERROR,
423
0
                                     GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
424
0
                                     _("Failed to find an image data atom."));
425
0
                return FALSE;
426
0
            }
427
0
            context->atom_count--;
428
429
            /* Copy to header buffer in context, in case supplied data is not enough. */
430
0
            while (context->run_length < sizeof(QtHeader) && size > 0u)
431
0
            {
432
0
                context->header_buffer[context->run_length] = *buf;
433
0
                context->run_length++;
434
0
                buf++;
435
0
                size--;
436
0
            }
437
438
            /* Parse buffer as QT header. */
439
0
            if(context->run_length == sizeof(QtHeader))
440
0
            {
441
0
                QtHeader *hdr = (QtHeader *)context->header_buffer;
442
0
                context->run_length = GUINT32_FROM_BE(hdr->length) - sizeof(QtHeader);
443
444
                /* Atom max size check. */
445
0
                if(context->run_length > ATOM_SIZE_MAX)
446
0
                {
447
0
                    g_set_error(error, GDK_PIXBUF_ERROR,
448
0
                                       GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
449
0
                                       ngettext (  "QTIF atom size too large (%d byte)",
450
0
                                                   "QTIF atom size too large (%d bytes)",
451
0
                                                    hdr->length),
452
0
                                       hdr->length);
453
0
                    return FALSE;
454
0
                }
455
456
                /* Set state according to atom type. */
457
0
                if(GUINT32_FROM_BE(hdr->tag) == QTIF_TAG_IDATA)
458
0
                {
459
0
                    GError *tmp = NULL;
460
461
0
                    context->state = STATE_DATA;
462
463
                    /* Create GdkPixbufLoader for this image data. */
464
0
                    ret = gdk_pixbuf__qtif_image_create_loader(context, &tmp);
465
0
                    if(!ret)
466
0
                    {
467
0
                        g_propagate_error (error, tmp);
468
0
                    }
469
0
                }
470
0
                else
471
0
                {
472
0
                    context->state = STATE_OTHER;
473
0
                }
474
0
            }
475
0
            break;
476
477
0
        default: /* Both STATE_DATA and STATE_OTHER will come here. */
478
            /* Check for atom boundary. */
479
0
            if(context->run_length > size)
480
0
            {
481
                /* Supply image data to GdkPixbufLoader if in STATE_DATA. */
482
0
                if(context->state == STATE_DATA)
483
0
                {
484
0
                    tmp = NULL;
485
0
                    ret = gdk_pixbuf_loader_write(context->loader, buf, size, &tmp);
486
0
                    if(!ret && (error != NULL) && (*error == NULL))
487
0
                    {
488
0
                        g_propagate_error (error, tmp);
489
0
                    }
490
0
                }
491
0
                context->run_length -= size;
492
0
                size = 0u;
493
0
            }
494
0
            else
495
0
            {
496
                /* Supply image data to GdkPixbufLoader if in STATE_DATA. */
497
0
                if(context->state == STATE_DATA)
498
0
                {
499
0
                    gboolean r;
500
501
                    /* Here we should have concluded a complete image atom. */
502
0
                    tmp = NULL;
503
0
                    ret = gdk_pixbuf_loader_write(context->loader, buf, context->run_length, &tmp);
504
0
                    if(!ret && (error != NULL) && (*error == NULL))
505
0
                    {
506
0
                        g_propagate_error (error, tmp);
507
0
                    }
508
509
                    /* Free GdkPixbufLoader and handle callback. */
510
0
                    tmp = NULL;
511
0
                    r = gdk_pixbuf__qtif_image_free_loader(context, &tmp);
512
0
                    if(!r)
513
0
                    {
514
0
                        if((error != NULL) && (*error == NULL))
515
0
                        {
516
0
                            g_propagate_error (error, tmp);
517
0
                        }
518
0
                        ret = FALSE;
519
0
                    }
520
0
                }
521
0
                buf = &buf[context->run_length];
522
0
                size -= context->run_length;
523
0
                context->run_length = 0u;
524
0
                context->state = STATE_READY;
525
0
            }
526
0
            break;
527
0
        }
528
0
    }
529
530
0
    return ret;
531
0
}
532
533
/* Event handlers */
534
static void gdk_pixbuf__qtif_cb_size_prepared(GdkPixbufLoader *loader,
535
                                              gint width,
536
                                              gint height,
537
                                              gpointer user_data)
538
0
{
539
0
    QTIFContext *context = (QTIFContext *)user_data;
540
0
    (context->size_func)(&width, &height, context->user_data);
541
0
    context->cb_prepare_count++;
542
0
}
543
544
static void gdk_pixbuf__qtif_cb_area_prepared(GdkPixbufLoader *loader, gpointer user_data)
545
0
{
546
0
    QTIFContext *context = (QTIFContext *)user_data;
547
0
    GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(context->loader);
548
0
    (context->prepared_func)(pixbuf, NULL, context->user_data);
549
0
    context->cb_update_count++;
550
0
}
551
552
static void gdk_pixbuf__qtif_cb_area_updated(GdkPixbufLoader *loader,
553
                                             gint x,
554
                                             gint y,
555
                                             gint width,
556
                                             gint height,
557
                                             gpointer user_data)
558
0
{
559
0
    QTIFContext *context = (QTIFContext *)user_data;
560
0
    GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(context->loader);
561
0
    (context->updated_func)(pixbuf, x, y, width, height, context->user_data);
562
0
}
563
564
565
#ifndef INCLUDE_qtif
566
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
567
#else
568
#define MODULE_ENTRY(function) void _gdk_pixbuf__qtif_ ## function
569
#endif
570
571
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
572
4
{
573
4
    module->load = gdk_pixbuf__qtif_image_load;
574
4
    module->begin_load = gdk_pixbuf__qtif_image_begin_load;
575
4
    module->stop_load = gdk_pixbuf__qtif_image_stop_load;
576
4
    module->load_increment = gdk_pixbuf__qtif_image_load_increment;
577
4
}
578
579
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
580
4
{
581
4
    static const GdkPixbufModulePattern signature[] = {
582
4
        { "abcdidsc", "xxxx    ", 100 },
583
4
        { "abcdidat", "xxxx    ", 100 },
584
4
        { NULL, NULL, 0 }
585
4
    };
586
4
    static const gchar *mime_types[] = {
587
4
        "image/x-quicktime",
588
4
        "image/qtif",
589
4
        NULL
590
4
    };
591
4
    static const gchar *extensions[] = {
592
4
        "qtif",
593
4
        "qif",
594
4
        NULL
595
4
    };
596
597
4
    info->name = "qtif";
598
4
    info->signature = (GdkPixbufModulePattern *) signature;
599
4
    info->description = NC_("image format", "QuickTime");
600
4
    info->mime_types = (gchar **) mime_types;
601
4
    info->extensions = (gchar **) extensions;
602
4
    info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
603
4
    info->license = "LGPL";
604
4
}
605