Coverage Report

Created: 2024-06-18 06:05

/src/leptonica/src/spixio.c
Line
Count
Source (jump to first uncovered line)
1
/*====================================================================*
2
 -  Copyright (C) 2001 Leptonica.  All rights reserved.
3
 -
4
 -  Redistribution and use in source and binary forms, with or without
5
 -  modification, are permitted provided that the following conditions
6
 -  are met:
7
 -  1. Redistributions of source code must retain the above copyright
8
 -     notice, this list of conditions and the following disclaimer.
9
 -  2. Redistributions in binary form must reproduce the above
10
 -     copyright notice, this list of conditions and the following
11
 -     disclaimer in the documentation and/or other materials
12
 -     provided with the distribution.
13
 -
14
 -  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15
 -  ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16
 -  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17
 -  A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ANY
18
 -  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19
 -  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
 -  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21
 -  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22
 -  OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
23
 -  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
 -  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
 *====================================================================*/
26
27
/*!
28
 * \file spixio.c
29
 * <pre>
30
 *
31
 *    This does fast serialization of a pix in memory to file,
32
 *    copying the raw data for maximum speed.  The underlying
33
 *    function serializes it to memory, and it is wrapped to be
34
 *    callable from standard pixRead() and pixWrite() file functions.
35
 *
36
 *      Reading spix from file
37
 *           PIX        *pixReadStreamSpix()
38
 *           l_int32     readHeaderSpix()
39
 *           l_int32     freadHeaderSpix()
40
 *           l_int32     sreadHeaderSpix()
41
 *
42
 *      Writing spix to file
43
 *           l_int32     pixWriteStreamSpix()
44
 *
45
 *      Low-level serialization of pix to/from memory (uncompressed)
46
 *           PIX        *pixReadMemSpix()
47
 *           l_int32     pixWriteMemSpix()
48
 *           l_int32     pixSerializeToMemory()
49
 *           PIX        *pixDeserializeFromMemory()
50
 *
51
 *    Note: these functions have not been extensively tested for fuzzing
52
 *    (bad input data that can result in, e.g., memory faults).
53
 *    The spix serialization format is only defined here, in leptonica.
54
 *    The image data is uncompressed and the serialization is not intended
55
 *    to be a secure file format from untrusted sources.
56
 * </pre>
57
 */
58
59
#ifdef HAVE_CONFIG_H
60
#include <config_auto.h>
61
#endif  /* HAVE_CONFIG_H */
62
63
#include <string.h>
64
#include "allheaders.h"
65
66
    /* Image dimension limits */
67
static const l_int32  MaxAllowedWidth = 1000000;
68
static const l_int32  MaxAllowedHeight = 1000000;
69
static const l_int64  MaxAllowedArea = 400000000LL;
70
71
#ifndef  NO_CONSOLE_IO
72
#define  DEBUG_SERIALIZE      0
73
#endif  /* ~NO_CONSOLE_IO */
74
75
76
/*-----------------------------------------------------------------------*
77
 *                          Reading spix from file                       *
78
 *-----------------------------------------------------------------------*/
79
/*!
80
 * \brief   pixReadStreamSpix()
81
 *
82
 * \param[in]    fp     file stream
83
 * \return  pix, or NULL on error.
84
 *
85
 * <pre>
86
 * Notes:
87
 *      (1) If called from pixReadStream(), the stream is positioned
88
 *          at the beginning of the file.
89
 * </pre>
90
 */
91
PIX *
92
pixReadStreamSpix(FILE  *fp)
93
0
{
94
0
size_t    nbytes;
95
0
l_uint8  *data;
96
0
PIX      *pix;
97
98
0
    if (!fp)
99
0
        return (PIX *)ERROR_PTR("stream not defined", __func__, NULL);
100
101
0
    if ((data = l_binaryReadStream(fp, &nbytes)) == NULL)
102
0
        return (PIX *)ERROR_PTR("data not read", __func__, NULL);
103
0
    pix = pixReadMemSpix(data, nbytes);
104
0
    LEPT_FREE(data);
105
0
    if (!pix)
106
0
        return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
107
0
    return pix;
108
0
}
109
110
111
/*!
112
 * \brief   readHeaderSpix()
113
 *
114
 * \param[in]    filename
115
 * \param[out]   pwidth     width
116
 * \param[out]   pheight    height
117
 * \param[out]   pbps       bits/sample
118
 * \param[out]   pspp       samples/pixel
119
 * \param[out]   piscmap    [optional]  input NULL to ignore
120
 * \return  0 if OK, 1 on error
121
 *
122
 * <pre>
123
 * Notes:
124
 *      (1) If there is a colormap, iscmap is returned as 1; else 0.
125
 * </pre>
126
 */
127
l_ok
128
readHeaderSpix(const char *filename,
129
               l_int32    *pwidth,
130
               l_int32    *pheight,
131
               l_int32    *pbps,
132
               l_int32    *pspp,
133
               l_int32    *piscmap)
134
0
{
135
0
l_int32  ret;
136
0
FILE    *fp;
137
138
0
    if (!filename)
139
0
        return ERROR_INT("filename not defined", __func__, 1);
140
0
    if (!pwidth || !pheight || !pbps || !pspp)
141
0
        return ERROR_INT("input ptr(s) not defined", __func__, 1);
142
0
    if ((fp = fopenReadStream(filename)) == NULL)
143
0
        return ERROR_INT_1("image file not found", filename, __func__, 1);
144
0
    ret = freadHeaderSpix(fp, pwidth, pheight, pbps, pspp, piscmap);
145
0
    fclose(fp);
146
0
    return ret;
147
0
}
148
149
150
/*!
151
 * \brief   freadHeaderSpix()
152
 *
153
 * \param[in]    fp        file stream
154
 * \param[out]   pwidth    width
155
 * \param[out]   pheight   height
156
 * \param[out]   pbps      bits/sample
157
 * \param[out]   pspp      samples/pixel
158
 * \param[out]   piscmap   [optional]  input NULL to ignore
159
 * \return  0 if OK, 1 on error
160
 *
161
 * <pre>
162
 * Notes:
163
 *      (1) If there is a colormap, iscmap is returned as 1; else 0.
164
 * </pre>
165
 */
166
l_ok
167
freadHeaderSpix(FILE     *fp,
168
                l_int32  *pwidth,
169
                l_int32  *pheight,
170
                l_int32  *pbps,
171
                l_int32  *pspp,
172
                l_int32  *piscmap)
173
0
{
174
0
l_int32   nbytes, ret;
175
0
l_uint32  data[6];
176
177
0
    if (!fp)
178
0
        return ERROR_INT("stream not defined", __func__, 1);
179
0
    if (!pwidth || !pheight || !pbps || !pspp)
180
0
        return ERROR_INT("input ptr(s) not defined", __func__, 1);
181
182
0
    nbytes = fnbytesInFile(fp);
183
0
    if (nbytes < 32)
184
0
        return ERROR_INT("file too small to be spix", __func__, 1);
185
0
    if (fread(data, 4, 6, fp) != 6)
186
0
        return ERROR_INT("error reading data", __func__, 1);
187
0
    ret = sreadHeaderSpix(data, nbytes, pwidth, pheight, pbps, pspp, piscmap);
188
0
    return ret;
189
0
}
190
191
192
/*!
193
 * \brief   sreadHeaderSpix()
194
 *
195
 * \param[in]    data
196
 * \param[in]    size      of data
197
 * \param[out]   pwidth    width
198
 * \param[out]   pheight   height
199
 * \param[out]   pbps      bits/sample
200
 * \param[out]   pspp      samples/pixel
201
 * \param[out]   piscmap   [optional]  input NULL to ignore
202
 * \return  0 if OK, 1 on error
203
 *
204
 * <pre>
205
 * Notes:
206
 *      (1) If there is a colormap, iscmap is returned as 1; else 0.
207
 * </pre>
208
 */
209
l_ok
210
sreadHeaderSpix(const l_uint32  *data,
211
                size_t           size,
212
                l_int32         *pwidth,
213
                l_int32         *pheight,
214
                l_int32         *pbps,
215
                l_int32         *pspp,
216
                l_int32         *piscmap)
217
0
{
218
0
char    *id;
219
0
l_int32  d, ncolors;
220
221
0
    if (!data)
222
0
        return ERROR_INT("data not defined", __func__, 1);
223
0
    if (!pwidth || !pheight || !pbps || !pspp)
224
0
        return ERROR_INT("input ptr(s) not defined", __func__, 1);
225
0
    *pwidth = *pheight = *pbps = *pspp = 0;
226
0
    if (piscmap)
227
0
      *piscmap = 0;
228
0
    if (size < 28)
229
0
        return ERROR_INT("size too small", __func__, 1);
230
231
        /* Check file id */
232
0
    id = (char *)data;
233
0
    if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x')
234
0
        return ERROR_INT("not a valid spix file", __func__, 1);
235
236
0
    *pwidth = data[1];
237
0
    *pheight = data[2];
238
0
    d = data[3];
239
0
    if (d <= 16) {
240
0
      *pbps = d;
241
0
      *pspp = 1;
242
0
    } else {
243
0
      *pbps = 8;
244
0
      *pspp = d / 8;  /* if the pix is 32 bpp, call it 4 samples */
245
0
    }
246
0
    ncolors = data[5];
247
0
    if (piscmap)
248
0
        *piscmap = (ncolors == 0) ? 0 : 1;
249
250
0
    return 0;
251
0
}
252
253
254
/*-----------------------------------------------------------------------*
255
 *                            Writing spix to file                       *
256
 *-----------------------------------------------------------------------*/
257
/*!
258
 * \brief   pixWriteStreamSpix()
259
 *
260
 * \param[in]    fp     file stream
261
 * \param[in]    pix
262
 * \return  0 if OK; 1 on error
263
 */
264
l_ok
265
pixWriteStreamSpix(FILE  *fp,
266
                   PIX   *pix)
267
0
{
268
0
l_uint8  *data;
269
0
size_t    size;
270
271
0
    if (!fp)
272
0
        return ERROR_INT("stream not defined", __func__, 1);
273
0
    if (!pix)
274
0
        return ERROR_INT("pix not defined", __func__, 1);
275
276
0
    if (pixWriteMemSpix(&data, &size, pix))
277
0
        return ERROR_INT("failure to write pix to memory", __func__, 1);
278
0
    fwrite(data, 1, size, fp);
279
0
    LEPT_FREE(data);
280
0
    return 0;
281
0
}
282
283
284
/*-----------------------------------------------------------------------*
285
 *       Low-level serialization of pix to/from memory (uncompressed)    *
286
 *-----------------------------------------------------------------------*/
287
/*!
288
 * \brief   pixReadMemSpix()
289
 *
290
 * \param[in]    data    const; uncompressed
291
 * \param[in]    size    bytes of data
292
 * \return  pix, or NULL on error
293
 */
294
PIX *
295
pixReadMemSpix(const l_uint8  *data,
296
               size_t          size)
297
1.61k
{
298
1.61k
    return pixDeserializeFromMemory((l_uint32 *)data, size);
299
1.61k
}
300
301
302
/*!
303
 * \brief   pixWriteMemSpix()
304
 *
305
 * \param[out]   pdata    data of serialized, uncompressed pix
306
 * \param[out]   psize    size of returned data
307
 * \param[in]    pix      all depths; colormap OK
308
 * \return  0 if OK, 1 on error
309
 */
310
l_ok
311
pixWriteMemSpix(l_uint8  **pdata,
312
                size_t    *psize,
313
                PIX       *pix)
314
0
{
315
0
    return pixSerializeToMemory(pix, (l_uint32 **)pdata, psize);
316
0
}
317
318
319
/*!
320
 * \brief   pixSerializeToMemory()
321
 *
322
 * \param[in]    pixs     all depths, colormap OK
323
 * \param[out]   pdata    serialized data in memory
324
 * \param[out]   pnbytes  number of bytes in data string
325
 * \return  0 if OK, 1 on error
326
 *
327
 * <pre>
328
 * Notes:
329
 *      (1) This does a fast serialization of the principal elements
330
 *          of the pix, as follows:
331
 *            "spix"    (4 bytes) -- ID for file type
332
 *            w         (4 bytes)
333
 *            h         (4 bytes)
334
 *            d         (4 bytes)
335
 *            wpl       (4 bytes)
336
 *            ncolors   (4 bytes) -- in colormap; 0 if there is no colormap
337
 *            cdata     (4 * ncolors)  -- size of serialized colormap array
338
 *            rdatasize (4 bytes) -- size of serialized raster data
339
 *                                   = 4 * wpl * h
340
 *            rdata     (rdatasize)
341
 * </pre>
342
 */
343
l_ok
344
pixSerializeToMemory(PIX        *pixs,
345
                     l_uint32  **pdata,
346
                     size_t     *pnbytes)
347
0
{
348
0
char      *id;
349
0
l_int32    w, h, d, wpl, rdatasize, ncolors, nbytes, index, valid;
350
0
l_uint8   *cdata;  /* data in colormap array (4 bytes/color table entry) */
351
0
l_uint32  *data;
352
0
l_uint32  *rdata;  /* data in pix raster */
353
0
PIXCMAP   *cmap;
354
355
0
    if (!pdata || !pnbytes)
356
0
        return ERROR_INT("&data and &nbytes not both defined", __func__, 1);
357
0
    *pdata = NULL;
358
0
    *pnbytes = 0;
359
0
    if (!pixs)
360
0
        return ERROR_INT("pixs not defined", __func__, 1);
361
362
0
    pixGetDimensions(pixs, &w, &h, &d);
363
0
    wpl = pixGetWpl(pixs);
364
0
    rdata = pixGetData(pixs);
365
0
    rdatasize = 4 * wpl * h;
366
0
    ncolors = 0;
367
0
    cdata = NULL;
368
0
    if ((cmap = pixGetColormap(pixs)) != NULL) {
369
0
        pixcmapIsValid(cmap, pixs, &valid);
370
0
        if (!valid)
371
0
            return ERROR_INT("colormap not valid", __func__, 1);
372
0
        pixcmapSerializeToMemory(cmap, 4, &ncolors, &cdata);
373
0
    }
374
375
0
    nbytes = 24 + 4 * ncolors + 4 + rdatasize;
376
0
    if ((data = (l_uint32 *)LEPT_CALLOC(nbytes / 4, sizeof(l_uint32)))
377
0
         == NULL) {
378
0
        LEPT_FREE(cdata);
379
0
        return ERROR_INT("data not made", __func__, 1);
380
0
    }
381
0
    *pdata = data;
382
0
    *pnbytes = nbytes;
383
0
    id = (char *)data;
384
0
    id[0] = 's';
385
0
    id[1] = 'p';
386
0
    id[2] = 'i';
387
0
    id[3] = 'x';
388
0
    data[1] = w;
389
0
    data[2] = h;
390
0
    data[3] = d;
391
0
    data[4] = wpl;
392
0
    data[5] = ncolors;
393
0
    if (ncolors > 0)
394
0
        memcpy(data + 6, cdata, 4 * ncolors);
395
0
    index = 6 + ncolors;
396
0
    data[index] = rdatasize;
397
0
    memcpy(data + index + 1, rdata, rdatasize);
398
399
#if  DEBUG_SERIALIZE
400
    lept_stderr("Serialize:   "
401
                "raster size = %d, ncolors in cmap = %d, total bytes = %d\n",
402
                rdatasize, ncolors, nbytes);
403
#endif  /* DEBUG_SERIALIZE */
404
405
0
    LEPT_FREE(cdata);
406
0
    return 0;
407
0
}
408
409
410
/*!
411
 * \brief   pixDeserializeFromMemory()
412
 *
413
 * \param[in]    data     serialized data in memory
414
 * \param[in]    nbytes   number of bytes in data string
415
 * \return  pix, or NULL on error
416
 *
417
 * <pre>
418
 * Notes:
419
 *      (1) See pixSerializeToMemory() for the binary format.
420
 *      (2) Note the image size limits.
421
 * </pre>
422
 */
423
PIX *
424
pixDeserializeFromMemory(const l_uint32  *data,
425
                         size_t           nbytes)
426
1.61k
{
427
1.61k
char      *id;
428
1.61k
l_int32    w, h, d, pixdata_size, memdata_size, imdata_size, ncolors, valid;
429
1.61k
l_uint32  *imdata;  /* data in pix raster */
430
1.61k
PIX       *pix1, *pixd;
431
1.61k
PIXCMAP   *cmap = NULL;
432
433
1.61k
    if (!data)
434
0
        return (PIX *)ERROR_PTR("data not defined", __func__, NULL);
435
1.61k
    if (nbytes < 28 || nbytes > ((1LL << 31) - 1)) {
436
10
        L_ERROR("invalid nbytes = %zu\n", __func__, nbytes);
437
10
        return NULL;
438
10
    }
439
440
1.60k
    id = (char *)data;
441
1.60k
    if (id[0] != 's' || id[1] != 'p' || id[2] != 'i' || id[3] != 'x')
442
56
        return (PIX *)ERROR_PTR("invalid id string", __func__, NULL);
443
1.54k
    w = data[1];
444
1.54k
    h = data[2];
445
1.54k
    d = data[3];
446
1.54k
    ncolors = data[5];
447
448
        /* Sanity checks on the amount of image data */
449
1.54k
    if (w < 1 || w > MaxAllowedWidth)
450
52
        return (PIX *)ERROR_PTR("invalid width", __func__, NULL);
451
1.49k
    if (h < 1 || h > MaxAllowedHeight)
452
57
        return (PIX *)ERROR_PTR("invalid height", __func__, NULL);
453
1.43k
    if (1LL * w * h > MaxAllowedArea)
454
27
        return (PIX *)ERROR_PTR("area too large", __func__, NULL);
455
1.41k
    if (ncolors < 0 || ncolors > 256 || ncolors + 7 >= nbytes/sizeof(l_int32))
456
92
        return (PIX *)ERROR_PTR("invalid ncolors", __func__, NULL);
457
1.32k
    if ((pix1 = pixCreateHeader(w, h, d)) == NULL)  /* just make the header */
458
116
        return (PIX *)ERROR_PTR("failed to make header", __func__, NULL);
459
1.20k
    pixdata_size = 4 * h * pixGetWpl(pix1);
460
1.20k
    memdata_size = nbytes - 24 - 4 * ncolors - 4;
461
1.20k
    imdata_size = data[6 + ncolors];
462
1.20k
    pixDestroy(&pix1);
463
1.20k
    if (pixdata_size != memdata_size || pixdata_size != imdata_size) {
464
125
        L_ERROR("pixdata_size = %d, memdata_size = %d, imdata_size = %d "
465
125
                "not all equal!\n", __func__, pixdata_size, memdata_size,
466
125
                imdata_size);
467
125
        return NULL;
468
125
    }
469
470
1.07k
    if ((pixd = pixCreate(w, h, d)) == NULL)
471
0
        return (PIX *)ERROR_PTR("pix not made", __func__, NULL);
472
1.07k
    if (ncolors > 0) {
473
223
        cmap = pixcmapDeserializeFromMemory((l_uint8 *)(&data[6]), 4, ncolors);
474
223
        if (!cmap) {
475
0
            pixDestroy(&pixd);
476
0
            return (PIX *)ERROR_PTR("cmap not made", __func__, NULL);
477
0
        }
478
223
        if (pixSetColormap(pixd, cmap)) {
479
0
            pixDestroy(&pixd);
480
0
            return (PIX *)ERROR_PTR("cmap is not valid", __func__, NULL);
481
0
        }
482
223
    }
483
484
        /* Read the raster data */
485
1.07k
    imdata = pixGetData(pixd);
486
1.07k
    memcpy(imdata, data + 7 + ncolors, imdata_size);
487
488
        /* Verify that the colormap is valid with the pix */
489
1.07k
    if (ncolors > 0) {
490
223
        pixcmapIsValid(cmap, pixd, &valid);
491
223
        if (!valid) {
492
149
            pixDestroy(&pixd);
493
149
            return (PIX *)ERROR_PTR("cmap is invalid with pix", __func__, NULL);
494
149
        }
495
223
    }
496
497
#if  DEBUG_SERIALIZE
498
    lept_stderr("Deserialize: "
499
                "raster size = %d, ncolors in cmap = %d, total bytes = %zu\n",
500
                imdata_size, ncolors, nbytes);
501
#endif  /* DEBUG_SERIALIZE */
502
503
930
    return pixd;
504
1.07k
}