Coverage Report

Created: 2024-06-18 06:05

/src/leptonica/src/fpix1.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 fpix1.c
29
 * <pre>
30
 *
31
 *    ---------------------------------------------------
32
 *    This file has these FPix, FPixa and DPix utilities:
33
 *         - creation and destruction
34
 *         - accessors
35
 *         - serialization and deserialization
36
 *    ---------------------------------------------------
37
 *
38
 *    FPix Create/copy/destroy
39
 *          FPIX          *fpixCreate()
40
 *          FPIX          *fpixCreateTemplate()
41
 *          FPIX          *fpixClone()
42
 *          FPIX          *fpixCopy()
43
 *          void           fpixDestroy()
44
 *
45
 *    FPix accessors
46
 *          l_int32        fpixGetDimensions()
47
 *          l_int32        fpixSetDimensions()
48
 *          l_int32        fpixGetWpl()
49
 *          l_int32        fpixSetWpl()
50
 *          l_int32        fpixGetResolution()
51
 *          l_int32        fpixSetResolution()
52
 *          l_int32        fpixCopyResolution()
53
 *          l_float32     *fpixGetData()
54
 *          l_int32        fpixSetData()
55
 *          l_int32        fpixGetPixel()
56
 *          l_int32        fpixSetPixel()
57
 *
58
 *    FPixa Create/copy/destroy
59
 *          FPIXA         *fpixaCreate()
60
 *          FPIXA         *fpixaCopy()
61
 *          void           fpixaDestroy()
62
 *
63
 *    FPixa addition
64
 *          l_int32        fpixaAddFPix()
65
 *          static l_int32 fpixaExtendArray()
66
 *          static l_int32 fpixaExtendArrayToSize()
67
 *
68
 *    FPixa accessors
69
 *          l_int32        fpixaGetCount()
70
 *          FPIX          *fpixaGetFPix()
71
 *          l_int32        fpixaGetFPixDimensions()
72
 *          l_float32     *fpixaGetData()
73
 *          l_int32        fpixaGetPixel()
74
 *          l_int32        fpixaSetPixel()
75
 *
76
 *    DPix Create/copy/destroy
77
 *          DPIX          *dpixCreate()
78
 *          DPIX          *dpixCreateTemplate()
79
 *          DPIX          *dpixClone()
80
 *          DPIX          *dpixCopy()
81
 *          void           dpixDestroy()
82
 *
83
 *    DPix accessors
84
 *          l_int32        dpixGetDimensions()
85
 *          l_int32        dpixSetDimensions()
86
 *          l_int32        dpixGetWpl()
87
 *          l_int32        dpixSetWpl()
88
 *          l_int32        dpixGetResolution()
89
 *          l_int32        dpixSetResolution()
90
 *          l_int32        dpixCopyResolution()
91
 *          l_float64     *dpixGetData()
92
 *          l_int32        dpixSetData()
93
 *          l_int32        dpixGetPixel()
94
 *          l_int32        dpixSetPixel()
95
 *
96
 *    FPix serialized I/O
97
 *          FPIX          *fpixRead()
98
 *          FPIX          *fpixReadStream()
99
 *          FPIX          *fpixReadMem()
100
 *          l_int32        fpixWrite()
101
 *          l_int32        fpixWriteStream()
102
 *          l_int32        fpixWriteMem()
103
 *          FPIX          *fpixEndianByteSwap()
104
 *
105
 *    DPix serialized I/O
106
 *          DPIX          *dpixRead()
107
 *          DPIX          *dpixReadStream()
108
 *          DPIX          *dpixReadMem()
109
 *          l_int32        dpixWrite()
110
 *          l_int32        dpixWriteStream()
111
 *          l_int32        dpixWriteMem()
112
 *          DPIX          *dpixEndianByteSwap()
113
 *
114
 *    Print FPix (subsampled, for debugging)
115
 *          l_int32        fpixPrintStream()
116
 * </pre>
117
 */
118
119
#ifdef HAVE_CONFIG_H
120
#include <config_auto.h>
121
#endif  /* HAVE_CONFIG_H */
122
123
#include <string.h>
124
#include "allheaders.h"
125
#include "pix_internal.h"
126
127
    /* Bounds on array sizes */
128
static const size_t  MaxPtrArraySize = 100000;
129
static const size_t  InitialPtrArraySize = 20;      /*!< n'importe quoi */
130
131
    /* Static functions */
132
static l_int32 fpixaExtendArray(FPIXA *fpixa);
133
static l_int32 fpixaExtendArrayToSize(FPIXA *fpixa, l_int32 size);
134
135
/*--------------------------------------------------------------------*
136
 *                     FPix Create/copy/destroy                       *
137
 *--------------------------------------------------------------------*/
138
/*!
139
 * \brief   fpixCreate()
140
 *
141
 * \param[in]       width, height
142
 * \return  fpixd   with data allocated and initialized to 0, or NULL on error
143
 *
144
 * <pre>
145
 * Notes:
146
 *      (1) Makes a FPix of specified size, with the data array
147
 *          allocated and initialized to 0.
148
 *      (2) The number of pixels must be less than 2^29.
149
 * </pre>
150
 */
151
FPIX *
152
fpixCreate(l_int32  width,
153
           l_int32  height)
154
0
{
155
0
l_float32  *data;
156
0
l_uint64    npix64;
157
0
FPIX       *fpixd;
158
159
0
    if (width <= 0)
160
0
        return (FPIX *)ERROR_PTR("width must be > 0", __func__, NULL);
161
0
    if (height <= 0)
162
0
        return (FPIX *)ERROR_PTR("height must be > 0", __func__, NULL);
163
164
        /* Avoid overflow in malloc arg, malicious or otherwise */
165
0
    npix64 = (l_uint64)width * (l_uint64)height;   /* # of 4-byte pixels */
166
0
    if (npix64 >= (1LL << 29)) {
167
0
        L_ERROR("requested w = %d, h = %d\n", __func__, width, height);
168
0
        return (FPIX *)ERROR_PTR("requested bytes >= 2^31", __func__, NULL);
169
0
    }
170
171
0
    fpixd = (FPIX *)LEPT_CALLOC(1, sizeof(FPIX));
172
0
    fpixSetDimensions(fpixd, width, height);
173
0
    fpixSetWpl(fpixd, width);  /* 4-byte words */
174
0
    fpixd->refcount = 1;
175
176
0
    data = (l_float32 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float32));
177
0
    if (!data) {
178
0
        fpixDestroy(&fpixd);
179
0
        return (FPIX *)ERROR_PTR("calloc fail for data", __func__, NULL);
180
0
    }
181
0
    fpixSetData(fpixd, data);
182
0
    return fpixd;
183
0
}
184
185
186
/*!
187
 * \brief   fpixCreateTemplate()
188
 *
189
 * \param[in]    fpixs
190
 * \return  fpixd, or NULL on error
191
 *
192
 * <pre>
193
 * Notes:
194
 *      (1) Makes a FPix of the same size as the input FPix, with the
195
 *          data array allocated and initialized to 0.
196
 *      (2) Copies the resolution.
197
 * </pre>
198
 */
199
FPIX *
200
fpixCreateTemplate(FPIX  *fpixs)
201
0
{
202
0
l_int32  w, h;
203
0
FPIX    *fpixd;
204
205
0
    if (!fpixs)
206
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
207
208
0
    fpixGetDimensions(fpixs, &w, &h);
209
0
    if ((fpixd = fpixCreate(w, h)) == NULL)
210
0
        return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL);
211
0
    fpixCopyResolution(fpixd, fpixs);
212
0
    return fpixd;
213
0
}
214
215
216
/*!
217
 * \brief   fpixClone()
218
 *
219
 * \param[in]    fpix
220
 * \return  same fpix ptr, or NULL on error
221
 *
222
 * <pre>
223
 * Notes:
224
 *      (1) See pixClone() for definition and usage.
225
 * </pre>
226
 */
227
FPIX *
228
fpixClone(FPIX  *fpix)
229
0
{
230
0
    if (!fpix)
231
0
        return (FPIX *)ERROR_PTR("fpix not defined", __func__, NULL);
232
0
    ++fpix->refcount;
233
234
0
    return fpix;
235
0
}
236
237
238
/*!
239
 * \brief   fpixCopy()
240
 *
241
 * \param[in]    fpixs
242
 * \return  fpixd, or NULL on error
243
 */
244
FPIX *
245
fpixCopy(FPIX  *fpixs)
246
0
{
247
0
l_int32     w, h, bytes;
248
0
l_float32  *datas, *datad;
249
0
FPIX       *fpixd;
250
251
0
    if (!fpixs)
252
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
253
254
        /* Total bytes in image data */
255
0
    fpixGetDimensions(fpixs, &w, &h);
256
0
    bytes = 4 * w * h;
257
258
0
    if ((fpixd = fpixCreateTemplate(fpixs)) == NULL)
259
0
        return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL);
260
0
    datas = fpixGetData(fpixs);
261
0
    datad = fpixGetData(fpixd);
262
0
    memcpy(datad, datas, bytes);
263
0
    return fpixd;
264
0
}
265
266
267
/*!
268
 * \brief   fpixDestroy()
269
 *
270
 * \param[in,out]   pfpix    will be set to null before returning
271
 * \return  void
272
 *
273
 * <pre>
274
 * Notes:
275
 *      (1) Decrements the ref count and, if 0, destroys the fpix.
276
 *      (2) Always nulls the input ptr.
277
 * </pre>
278
 */
279
void
280
fpixDestroy(FPIX  **pfpix)
281
0
{
282
0
l_float32  *data;
283
0
FPIX       *fpix;
284
285
0
    if (!pfpix) {
286
0
        L_WARNING("ptr address is null!\n", __func__);
287
0
        return;
288
0
    }
289
290
0
    if ((fpix = *pfpix) == NULL)
291
0
        return;
292
293
        /* Decrement the ref count.  If it is 0, destroy the fpix. */
294
0
    if (--fpix->refcount == 0) {
295
0
        if ((data = fpixGetData(fpix)) != NULL)
296
0
            LEPT_FREE(data);
297
0
        LEPT_FREE(fpix);
298
0
    }
299
0
    *pfpix = NULL;
300
0
}
301
302
303
/*--------------------------------------------------------------------*
304
 *                          FPix  Accessors                           *
305
 *--------------------------------------------------------------------*/
306
/*!
307
 * \brief   fpixGetDimensions()
308
 *
309
 * \param[in]    fpix
310
 * \param[out]   pw, ph    [optional] each can be null
311
 * \return  0 if OK, 1 on error
312
 */
313
l_ok
314
fpixGetDimensions(FPIX     *fpix,
315
                  l_int32  *pw,
316
                  l_int32  *ph)
317
0
{
318
0
    if (!pw && !ph)
319
0
        return ERROR_INT("no return val requested", __func__, 1);
320
0
    if (pw) *pw = 0;
321
0
    if (ph) *ph = 0;
322
0
    if (!fpix)
323
0
        return ERROR_INT("fpix not defined", __func__, 1);
324
0
    if (pw) *pw = fpix->w;
325
0
    if (ph) *ph = fpix->h;
326
0
    return 0;
327
0
}
328
329
330
/*!
331
 * \brief   fpixSetDimensions()
332
 *
333
 * \param[in]    fpix
334
 * \param[in]    w, h
335
 * \return  0 if OK, 1 on error
336
 */
337
l_ok
338
fpixSetDimensions(FPIX     *fpix,
339
                  l_int32   w,
340
                  l_int32   h)
341
0
{
342
0
    if (!fpix)
343
0
        return ERROR_INT("fpix not defined", __func__, 1);
344
0
    fpix->w = w;
345
0
    fpix->h = h;
346
0
    return 0;
347
0
}
348
349
350
/*!
351
 * \brief   fpixGetWpl()
352
 *
353
 * \param[in]    fpix
354
 * \return  wpl, or 0 on error
355
 */
356
l_int32
357
fpixGetWpl(FPIX  *fpix)
358
0
{
359
0
    if (!fpix)
360
0
        return ERROR_INT("fpix not defined", __func__, 0);
361
0
    return fpix->wpl;
362
0
}
363
364
365
/*!
366
 * \brief   fpixSetWpl()
367
 *
368
 * \param[in]    fpix
369
 * \param[in]    wpl
370
 * \return  0 if OK, 1 on error
371
 */
372
l_ok
373
fpixSetWpl(FPIX    *fpix,
374
           l_int32  wpl)
375
0
{
376
0
    if (!fpix)
377
0
        return ERROR_INT("fpix not defined", __func__, 1);
378
379
0
    fpix->wpl = wpl;
380
0
    return 0;
381
0
}
382
383
384
/*!
385
 * \brief   fpixGetResolution()
386
 *
387
 * \param[in]    fpix
388
 * \param[out]   pxres, pyres     [optional] x and y resolution
389
 * \return  0 if OK, 1 on error
390
 */
391
l_ok
392
fpixGetResolution(FPIX     *fpix,
393
                  l_int32  *pxres,
394
                  l_int32  *pyres)
395
0
{
396
0
    if (!fpix)
397
0
        return ERROR_INT("fpix not defined", __func__, 1);
398
0
    if (pxres) *pxres = fpix->xres;
399
0
    if (pyres) *pyres = fpix->yres;
400
0
    return 0;
401
0
}
402
403
404
/*!
405
 * \brief   fpixSetResolution()
406
 *
407
 * \param[in]    fpix
408
 * \param[in]    xres, yres     x and y resolution
409
 * \return  0 if OK, 1 on error
410
 */
411
l_ok
412
fpixSetResolution(FPIX    *fpix,
413
                  l_int32  xres,
414
                  l_int32  yres)
415
0
{
416
0
    if (!fpix)
417
0
        return ERROR_INT("fpix not defined", __func__, 1);
418
419
0
    fpix->xres = xres;
420
0
    fpix->yres = yres;
421
0
    return 0;
422
0
}
423
424
425
/*!
426
 * \brief   fpixCopyResolution()
427
 *
428
 * \param[in]    fpixd, fpixs
429
 * \return  0 if OK, 1 on error
430
 */
431
l_ok
432
fpixCopyResolution(FPIX  *fpixd,
433
                   FPIX  *fpixs)
434
0
{
435
0
l_int32  xres, yres;
436
0
    if (!fpixs || !fpixd)
437
0
        return ERROR_INT("fpixs and fpixd not both defined", __func__, 1);
438
439
0
    fpixGetResolution(fpixs, &xres, &yres);
440
0
    fpixSetResolution(fpixd, xres, yres);
441
0
    return 0;
442
0
}
443
444
445
/*!
446
 * \brief   fpixGetData()
447
 *
448
 * \param[in]    fpix
449
 * \return  ptr to fpix data, or NULL on error
450
 */
451
l_float32 *
452
fpixGetData(FPIX  *fpix)
453
0
{
454
0
    if (!fpix)
455
0
        return (l_float32 *)ERROR_PTR("fpix not defined", __func__, NULL);
456
0
    return fpix->data;
457
0
}
458
459
460
/*!
461
 * \brief   fpixSetData()
462
 *
463
 * \param[in]    fpix
464
 * \param[in]    data
465
 * \return  0 if OK, 1 on error
466
 */
467
l_ok
468
fpixSetData(FPIX       *fpix,
469
            l_float32  *data)
470
0
{
471
0
    if (!fpix)
472
0
        return ERROR_INT("fpix not defined", __func__, 1);
473
474
0
    fpix->data = data;
475
0
    return 0;
476
0
}
477
478
479
/*!
480
 * \brief   fpixGetPixel()
481
 *
482
 * \param[in]    fpix
483
 * \param[in]    x,y     pixel coords
484
 * \param[out]   pval    pixel value
485
 * \return  0 if OK; 1 or 2 on error
486
 *
487
 * Notes:
488
 *      (1) If the point is outside the image, this returns an error (2),
489
 *          with 0.0 in %pval.  To avoid spamming output, it fails silently.
490
 */
491
l_ok
492
fpixGetPixel(FPIX       *fpix,
493
             l_int32     x,
494
             l_int32     y,
495
             l_float32  *pval)
496
0
{
497
0
l_int32  w, h;
498
499
0
    if (!pval)
500
0
        return ERROR_INT("pval not defined", __func__, 1);
501
0
    *pval = 0.0;
502
0
    if (!fpix)
503
0
        return ERROR_INT("fpix not defined", __func__, 1);
504
505
0
    fpixGetDimensions(fpix, &w, &h);
506
0
    if (x < 0 || x >= w || y < 0 || y >= h)
507
0
        return 2;
508
509
0
    *pval = *(fpix->data + y * w + x);
510
0
    return 0;
511
0
}
512
513
514
/*!
515
 * \brief   fpixSetPixel()
516
 *
517
 * \param[in]    fpix
518
 * \param[in]    x,y    pixel coords
519
 * \param[in]    val    pixel value
520
 * \return  0 if OK; 1 or 2 on error
521
 *
522
 * Notes:
523
 *      (1) If the point is outside the image, this returns an error (2),
524
 *          with 0.0 in %pval.  To avoid spamming output, it fails silently.
525
 */
526
l_ok
527
fpixSetPixel(FPIX      *fpix,
528
             l_int32    x,
529
             l_int32    y,
530
             l_float32  val)
531
0
{
532
0
l_int32  w, h;
533
534
0
    if (!fpix)
535
0
        return ERROR_INT("fpix not defined", __func__, 1);
536
537
0
    fpixGetDimensions(fpix, &w, &h);
538
0
    if (x < 0 || x >= w || y < 0 || y >= h)
539
0
        return 2;
540
541
0
    *(fpix->data + y * w + x) = val;
542
0
    return 0;
543
0
}
544
545
546
/*--------------------------------------------------------------------*
547
 *                     FPixa Create/copy/destroy                      *
548
 *--------------------------------------------------------------------*/
549
/*!
550
 * \brief   fpixaCreate()
551
 *
552
 * \param[in]    n     initial number of ptrs
553
 * \return  fpixa, or NULL on error
554
 */
555
FPIXA *
556
fpixaCreate(l_int32  n)
557
0
{
558
0
FPIXA  *fpixa;
559
560
0
    if (n <= 0 || n > MaxPtrArraySize)
561
0
        n = InitialPtrArraySize;
562
563
0
    fpixa = (FPIXA *)LEPT_CALLOC(1, sizeof(FPIXA));
564
0
    fpixa->n = 0;
565
0
    fpixa->nalloc = n;
566
0
    fpixa->refcount = 1;
567
0
    fpixa->fpix = (FPIX **)LEPT_CALLOC(n, sizeof(FPIX *));
568
0
    return fpixa;
569
0
}
570
571
572
/*!
573
 * \brief   fpixaCopy()
574
 *
575
 * \param[in]    fpixa
576
 * \param[in]    copyflag     L_COPY, L_CLODE or L_COPY_CLONE
577
 * \return  new fpixa, or NULL on error
578
 *
579
 * <pre>
580
 * Notes:
581
 *      copyflag may be one of
582
 *        ~ L_COPY makes a new fpixa and copies each fpix
583
 *        ~ L_CLONE gives a new ref-counted handle to the input fpixa
584
 *        ~ L_COPY_CLONE makes a new fpixa with clones of all fpix
585
 * </pre>
586
 */
587
FPIXA *
588
fpixaCopy(FPIXA   *fpixa,
589
          l_int32  copyflag)
590
0
{
591
0
l_int32  i;
592
0
FPIX    *fpixc;
593
0
FPIXA   *fpixac;
594
595
0
    if (!fpixa)
596
0
        return (FPIXA *)ERROR_PTR("fpixa not defined", __func__, NULL);
597
598
0
    if (copyflag == L_CLONE) {
599
0
        ++fpixa->refcount;
600
0
        return fpixa;
601
0
    }
602
603
0
    if (copyflag != L_COPY && copyflag != L_COPY_CLONE)
604
0
        return (FPIXA *)ERROR_PTR("invalid copyflag", __func__, NULL);
605
606
0
    if ((fpixac = fpixaCreate(fpixa->n)) == NULL)
607
0
        return (FPIXA *)ERROR_PTR("fpixac not made", __func__, NULL);
608
0
    for (i = 0; i < fpixa->n; i++) {
609
0
        if (copyflag == L_COPY)
610
0
            fpixc = fpixaGetFPix(fpixa, i, L_COPY);
611
0
        else  /* copy-clone */
612
0
            fpixc = fpixaGetFPix(fpixa, i, L_CLONE);
613
0
        fpixaAddFPix(fpixac, fpixc, L_INSERT);
614
0
    }
615
616
0
    return fpixac;
617
0
}
618
619
620
/*!
621
 * \brief   fpixaDestroy()
622
 *
623
 * \param[in,out]   pfpixa    will be set to null before returning
624
 * \return  void
625
 *
626
 * <pre>
627
 * Notes:
628
 *      (1) Decrements the ref count and, if 0, destroys the fpixa.
629
 *      (2) Always nulls the input ptr.
630
 * </pre>
631
 */
632
void
633
fpixaDestroy(FPIXA  **pfpixa)
634
0
{
635
0
l_int32  i;
636
0
FPIXA   *fpixa;
637
638
0
    if (pfpixa == NULL) {
639
0
        L_WARNING("ptr address is NULL!\n", __func__);
640
0
        return;
641
0
    }
642
643
0
    if ((fpixa = *pfpixa) == NULL)
644
0
        return;
645
646
        /* Decrement the refcount.  If it is 0, destroy the pixa. */
647
0
    if (--fpixa->refcount == 0) {
648
0
        for (i = 0; i < fpixa->n; i++)
649
0
            fpixDestroy(&fpixa->fpix[i]);
650
0
        LEPT_FREE(fpixa->fpix);
651
0
        LEPT_FREE(fpixa);
652
0
    }
653
0
    *pfpixa = NULL;
654
0
}
655
656
657
/*--------------------------------------------------------------------*
658
 *                           FPixa addition                           *
659
 *--------------------------------------------------------------------*/
660
/*!
661
 * \brief   fpixaAddFPix()
662
 *
663
 * \param[in]    fpixa
664
 * \param[in]    fpix        to be added
665
 * \param[in]    copyflag    L_INSERT, L_COPY, L_CLONE
666
 * \return  0 if OK; 1 on error
667
 */
668
l_ok
669
fpixaAddFPix(FPIXA   *fpixa,
670
             FPIX    *fpix,
671
             l_int32  copyflag)
672
0
{
673
0
l_int32  n;
674
0
FPIX    *fpixc;
675
676
0
    if (!fpixa)
677
0
        return ERROR_INT("fpixa not defined", __func__, 1);
678
0
    if (!fpix)
679
0
        return ERROR_INT("fpix not defined", __func__, 1);
680
681
0
    if (copyflag == L_INSERT)
682
0
        fpixc = fpix;
683
0
    else if (copyflag == L_COPY)
684
0
        fpixc = fpixCopy(fpix);
685
0
    else if (copyflag == L_CLONE)
686
0
        fpixc = fpixClone(fpix);
687
0
    else
688
0
        return ERROR_INT("invalid copyflag", __func__, 1);
689
0
    if (!fpixc)
690
0
        return ERROR_INT("fpixc not made", __func__, 1);
691
692
0
    n = fpixaGetCount(fpixa);
693
0
    if (n >= fpixa->nalloc) {
694
0
        if (fpixaExtendArray(fpixa)) {
695
0
            if (copyflag != L_INSERT)
696
0
                fpixDestroy(&fpixc);
697
0
            return ERROR_INT("extension failed", __func__, 1);
698
0
        }
699
0
    }
700
0
    fpixa->fpix[n] = fpixc;
701
0
    fpixa->n++;
702
0
    return 0;
703
0
}
704
705
706
/*!
707
 * \brief   fpixaExtendArray()
708
 *
709
 * \param[in]    fpixa
710
 * \return  0 if OK; 1 on error
711
 *
712
 * <pre>
713
 * Notes:
714
 *      (1) Doubles the size of the fpixa ptr array.
715
 *      (2) The max number of fpix ptrs is 100000.
716
 * </pre>
717
 */
718
static l_int32
719
fpixaExtendArray(FPIXA  *fpixa)
720
0
{
721
0
    if (!fpixa)
722
0
        return ERROR_INT("fpixa not defined", __func__, 1);
723
724
0
    return fpixaExtendArrayToSize(fpixa, 2 * fpixa->nalloc);
725
0
}
726
727
728
/*!
729
 * \brief   fpixaExtendArrayToSize()
730
 *
731
 * \param[in]    fpixa
732
 * \param[in]    size      new ptr array size
733
 * \return  0 if OK; 1 on error
734
 *
735
 * <pre>
736
 * Notes:
737
 *      (1) If necessary, reallocs new fpix ptr array to %size.
738
 *      (2) The max number of fpix ptrs is 100K.
739
 * </pre>
740
 */
741
static l_int32
742
fpixaExtendArrayToSize(FPIXA   *fpixa,
743
                       l_int32  size)
744
0
{
745
0
size_t  oldsize, newsize;
746
747
0
    if (!fpixa)
748
0
        return ERROR_INT("fpixa not defined", __func__, 1);
749
0
    if (fpixa->nalloc > MaxPtrArraySize)  /* belt & suspenders */
750
0
        return ERROR_INT("fpixa has too many ptrs", __func__, 1);
751
0
    if (size > MaxPtrArraySize)
752
0
        return ERROR_INT("size > 100K ptrs; too large", __func__, 1);
753
0
    if (size <= fpixa->nalloc) {
754
0
        L_INFO("size too small; no extension\n", __func__);
755
0
        return 0;
756
0
    }
757
758
0
    oldsize = fpixa->nalloc * sizeof(FPIX *);
759
0
    newsize = size * sizeof(FPIX *);
760
0
    if ((fpixa->fpix = (FPIX **)reallocNew((void **)&fpixa->fpix,
761
0
                                           oldsize, newsize)) == NULL)
762
0
        return ERROR_INT("new ptr array not returned", __func__, 1);
763
0
    fpixa->nalloc = size;
764
0
    return 0;
765
0
}
766
767
768
/*--------------------------------------------------------------------*
769
 *                          FPixa accessors                           *
770
 *--------------------------------------------------------------------*/
771
/*!
772
 * \brief   fpixaGetCount()
773
 *
774
 * \param[in]    fpixa
775
 * \return  count, or 0 if no pixa
776
 */
777
l_int32
778
fpixaGetCount(FPIXA  *fpixa)
779
0
{
780
0
    if (!fpixa)
781
0
        return ERROR_INT("fpixa not defined", __func__, 0);
782
783
0
    return fpixa->n;
784
0
}
785
786
787
/*!
788
 * \brief   fpixaGetFPix()
789
 *
790
 * \param[in]    fpixa
791
 * \param[in]    index        to the index-th fpix
792
 * \param[in]    accesstype   L_COPY or L_CLONE
793
 * \return  fpix, or NULL on error
794
 */
795
FPIX *
796
fpixaGetFPix(FPIXA   *fpixa,
797
             l_int32  index,
798
             l_int32  accesstype)
799
0
{
800
0
    if (!fpixa)
801
0
        return (FPIX *)ERROR_PTR("fpixa not defined", __func__, NULL);
802
0
    if (index < 0 || index >= fpixa->n)
803
0
        return (FPIX *)ERROR_PTR("index not valid", __func__, NULL);
804
805
0
    if (accesstype == L_COPY)
806
0
        return fpixCopy(fpixa->fpix[index]);
807
0
    else if (accesstype == L_CLONE)
808
0
        return fpixClone(fpixa->fpix[index]);
809
0
    else
810
0
        return (FPIX *)ERROR_PTR("invalid accesstype", __func__, NULL);
811
0
}
812
813
814
/*!
815
 * \brief   fpixaGetFPixDimensions()
816
 *
817
 * \param[in]    fpixa
818
 * \param[in]    index      to the index-th box
819
 * \param[out]   pw, ph     [optional] each can be null
820
 * \return  0 if OK, 1 on error
821
 */
822
l_ok
823
fpixaGetFPixDimensions(FPIXA    *fpixa,
824
                       l_int32   index,
825
                       l_int32  *pw,
826
                       l_int32  *ph)
827
0
{
828
0
FPIX  *fpix;
829
830
0
    if (!pw && !ph)
831
0
        return ERROR_INT("no return val requested", __func__, 1);
832
0
    if (pw) *pw = 0;
833
0
    if (ph) *ph = 0;
834
0
    if (!fpixa)
835
0
        return ERROR_INT("fpixa not defined", __func__, 1);
836
0
    if (index < 0 || index >= fpixa->n)
837
0
        return ERROR_INT("index not valid", __func__, 1);
838
839
0
    if ((fpix = fpixaGetFPix(fpixa, index, L_CLONE)) == NULL)
840
0
        return ERROR_INT("fpix not found!", __func__, 1);
841
0
    fpixGetDimensions(fpix, pw, ph);
842
0
    fpixDestroy(&fpix);
843
0
    return 0;
844
0
}
845
846
847
/*!
848
 * \brief   fpixaGetData()
849
 *
850
 * \param[in]    fpixa
851
 * \param[in]    index     into fpixa array
852
 * \return  data not a copy, or NULL on error
853
 */
854
l_float32 *
855
fpixaGetData(FPIXA      *fpixa,
856
             l_int32     index)
857
0
{
858
0
l_int32     n;
859
0
l_float32  *data;
860
0
FPIX       *fpix;
861
862
0
    if (!fpixa)
863
0
        return (l_float32 *)ERROR_PTR("fpixa not defined", __func__, NULL);
864
0
    n = fpixaGetCount(fpixa);
865
0
    if (index < 0 || index >= n)
866
0
        return (l_float32 *)ERROR_PTR("invalid index", __func__, NULL);
867
868
0
    fpix = fpixaGetFPix(fpixa, index, L_CLONE);
869
0
    data = fpixGetData(fpix);
870
0
    fpixDestroy(&fpix);
871
0
    return data;
872
0
}
873
874
875
/*!
876
 * \brief   fpixaGetPixel()
877
 *
878
 * \param[in]    fpixa
879
 * \param[in]    index     into fpixa array
880
 * \param[in]    x,y       pixel coords
881
 * \param[out]   pval      pixel value
882
 * \return  0 if OK; 1 on error
883
 */
884
l_ok
885
fpixaGetPixel(FPIXA      *fpixa,
886
              l_int32     index,
887
              l_int32     x,
888
              l_int32     y,
889
              l_float32  *pval)
890
0
{
891
0
l_int32  n, ret;
892
0
FPIX    *fpix;
893
894
0
    if (!pval)
895
0
        return ERROR_INT("pval not defined", __func__, 1);
896
0
    *pval = 0.0;
897
0
    if (!fpixa)
898
0
        return ERROR_INT("fpixa not defined", __func__, 1);
899
0
    n = fpixaGetCount(fpixa);
900
0
    if (index < 0 || index >= n)
901
0
        return ERROR_INT("invalid index into fpixa", __func__, 1);
902
903
0
    fpix = fpixaGetFPix(fpixa, index, L_CLONE);
904
0
    ret = fpixGetPixel(fpix, x, y, pval);
905
0
    fpixDestroy(&fpix);
906
0
    return ret;
907
0
}
908
909
910
/*!
911
 * \brief   fpixaSetPixel()
912
 *
913
 * \param[in]    fpixa
914
 * \param[in]    index    into fpixa array
915
 * \param[in]    x,y      pixel coords
916
 * \param[in]    val      pixel value
917
 * \return  0 if OK; 1 on error
918
 */
919
l_ok
920
fpixaSetPixel(FPIXA     *fpixa,
921
              l_int32    index,
922
              l_int32    x,
923
              l_int32    y,
924
              l_float32  val)
925
0
{
926
0
l_int32  n, ret;
927
0
FPIX    *fpix;
928
929
0
    if (!fpixa)
930
0
        return ERROR_INT("fpixa not defined", __func__, 1);
931
0
    n = fpixaGetCount(fpixa);
932
0
    if (index < 0 || index >= n)
933
0
        return ERROR_INT("invalid index into fpixa", __func__, 1);
934
935
0
    fpix = fpixaGetFPix(fpixa, index, L_CLONE);
936
0
    ret = fpixSetPixel(fpix, x, y, val);
937
0
    fpixDestroy(&fpix);
938
0
    return ret;
939
0
}
940
941
942
/*--------------------------------------------------------------------*
943
 *                     DPix Create/copy/destroy                       *
944
 *--------------------------------------------------------------------*/
945
/*!
946
 * \brief   dpixCreate()
947
 *
948
 * \param[in]     width, height
949
 * \return  dpix  with data allocated and initialized to 0, or NULL on error
950
 *
951
 * <pre>
952
 * Notes:
953
 *      (1) Makes a DPix of specified size, with the data array
954
 *          allocated and initialized to 0.
955
 *      (2) The number of pixels must be less than 2^28.
956
 * </pre>
957
 */
958
DPIX *
959
dpixCreate(l_int32  width,
960
           l_int32  height)
961
0
{
962
0
l_float64  *data;
963
0
l_uint64    npix64;
964
0
DPIX       *dpix;
965
966
0
    if (width <= 0)
967
0
        return (DPIX *)ERROR_PTR("width must be > 0", __func__, NULL);
968
0
    if (height <= 0)
969
0
        return (DPIX *)ERROR_PTR("height must be > 0", __func__, NULL);
970
971
        /* Avoid overflow in malloc arg, malicious or otherwise */
972
0
    npix64 = (l_uint64)width * (l_uint64)height;   /* # of 8 byte pixels */
973
0
    if (npix64 >= (1LL << 28)) {
974
0
        L_ERROR("requested w = %d, h = %d\n", __func__, width, height);
975
0
        return (DPIX *)ERROR_PTR("requested bytes >= 2^31", __func__, NULL);
976
0
    }
977
978
0
    dpix = (DPIX *)LEPT_CALLOC(1, sizeof(DPIX));
979
0
    dpixSetDimensions(dpix, width, height);
980
0
    dpixSetWpl(dpix, width);  /* 8 byte words */
981
0
    dpix->refcount = 1;
982
983
0
    data = (l_float64 *)LEPT_CALLOC((size_t)width * height, sizeof(l_float64));
984
0
    if (!data) {
985
0
        dpixDestroy(&dpix);
986
0
        return (DPIX *)ERROR_PTR("calloc fail for data", __func__, NULL);
987
0
    }
988
0
    dpixSetData(dpix, data);
989
0
    return dpix;
990
0
}
991
992
993
/*!
994
 * \brief   dpixCreateTemplate()
995
 *
996
 * \param[in]    dpixs
997
 * \return  dpixd, or NULL on error
998
 *
999
 * <pre>
1000
 * Notes:
1001
 *      (1) Makes a DPix of the same size as the input DPix, with the
1002
 *          data array allocated and initialized to 0.
1003
 *      (2) Copies the resolution.
1004
 * </pre>
1005
 */
1006
DPIX *
1007
dpixCreateTemplate(DPIX  *dpixs)
1008
0
{
1009
0
l_int32  w, h;
1010
0
DPIX    *dpixd;
1011
1012
0
    if (!dpixs)
1013
0
        return (DPIX *)ERROR_PTR("dpixs not defined", __func__, NULL);
1014
1015
0
    dpixGetDimensions(dpixs, &w, &h);
1016
0
    dpixd = dpixCreate(w, h);
1017
0
    dpixCopyResolution(dpixd, dpixs);
1018
0
    return dpixd;
1019
0
}
1020
1021
1022
/*!
1023
 * \brief   dpixClone()
1024
 *
1025
 * \param[in]    dpix
1026
 * \return  same dpix ptr, or NULL on error
1027
 *
1028
 * <pre>
1029
 * Notes:
1030
 *      (1) See pixClone() for definition and usage.
1031
 * </pre>
1032
 */
1033
DPIX *
1034
dpixClone(DPIX  *dpix)
1035
0
{
1036
0
    if (!dpix)
1037
0
        return (DPIX *)ERROR_PTR("dpix not defined", __func__, NULL);
1038
0
    ++dpix->refcount;
1039
0
    return dpix;
1040
0
}
1041
1042
1043
/*!
1044
 * \brief   dpixCopy()
1045
 *
1046
 * \param[in]    dpixs
1047
 * \return  dpixd, or NULL on error
1048
 */
1049
DPIX *
1050
dpixCopy(DPIX  *dpixs)
1051
0
{
1052
0
l_int32     w, h, bytes;
1053
0
l_float64  *datas, *datad;
1054
0
DPIX       *dpixd;
1055
1056
0
    if (!dpixs)
1057
0
        return (DPIX *)ERROR_PTR("dpixs not defined", __func__, NULL);
1058
1059
        /* Total bytes in image data */
1060
0
    dpixGetDimensions(dpixs, &w, &h);
1061
0
    bytes = 8 * w * h;
1062
1063
0
    if ((dpixd = dpixCreateTemplate(dpixs)) == NULL)
1064
0
        return (DPIX *)ERROR_PTR("dpixd not made", __func__, NULL);
1065
0
    datas = dpixGetData(dpixs);
1066
0
    datad = dpixGetData(dpixd);
1067
0
    memcpy(datad, datas, bytes);
1068
0
    return dpixd;
1069
0
}
1070
1071
1072
/*!
1073
 * \brief   dpixDestroy()
1074
 *
1075
 * \param[in,out]   pdpix    will be set to null before returning
1076
 * \return  void
1077
 *
1078
 * <pre>
1079
 * Notes:
1080
 *      (1) Decrements the ref count and, if 0, destroys the dpix.
1081
 *      (2) Always nulls the input ptr.
1082
 * </pre>
1083
 */
1084
void
1085
dpixDestroy(DPIX  **pdpix)
1086
0
{
1087
0
l_float64  *data;
1088
0
DPIX       *dpix;
1089
1090
0
    if (!pdpix) {
1091
0
        L_WARNING("ptr address is null!\n", __func__);
1092
0
        return;
1093
0
    }
1094
1095
0
    if ((dpix = *pdpix) == NULL)
1096
0
        return;
1097
1098
        /* Decrement the ref count.  If it is 0, destroy the dpix. */
1099
0
    if (--dpix->refcount == 0) {
1100
0
        if ((data = dpixGetData(dpix)) != NULL)
1101
0
            LEPT_FREE(data);
1102
0
        LEPT_FREE(dpix);
1103
0
    }
1104
0
    *pdpix = NULL;
1105
0
}
1106
1107
1108
/*--------------------------------------------------------------------*
1109
 *                          DPix  Accessors                           *
1110
 *--------------------------------------------------------------------*/
1111
/*!
1112
 * \brief   dpixGetDimensions()
1113
 *
1114
 * \param[in]    dpix
1115
 * \param[out]   pw, ph     [optional] each can be null
1116
 * \return  0 if OK, 1 on error
1117
 */
1118
l_ok
1119
dpixGetDimensions(DPIX     *dpix,
1120
                  l_int32  *pw,
1121
                  l_int32  *ph)
1122
0
{
1123
0
    if (!pw && !ph)
1124
0
        return ERROR_INT("no return val requested", __func__, 1);
1125
0
    if (pw) *pw = 0;
1126
0
    if (ph) *ph = 0;
1127
0
    if (!dpix)
1128
0
        return ERROR_INT("dpix not defined", __func__, 1);
1129
0
    if (pw) *pw = dpix->w;
1130
0
    if (ph) *ph = dpix->h;
1131
0
    return 0;
1132
0
}
1133
1134
1135
/*!
1136
 * \brief   dpixSetDimensions()
1137
 *
1138
 * \param[in]    dpix
1139
 * \param[in]    w, h
1140
 * \return  0 if OK, 1 on error
1141
 */
1142
l_ok
1143
dpixSetDimensions(DPIX     *dpix,
1144
                  l_int32   w,
1145
                  l_int32   h)
1146
0
{
1147
0
    if (!dpix)
1148
0
        return ERROR_INT("dpix not defined", __func__, 1);
1149
0
    dpix->w = w;
1150
0
    dpix->h = h;
1151
0
    return 0;
1152
0
}
1153
1154
1155
/*!
1156
 * \brief   dpixGetWpl()
1157
 *
1158
 * \param[in]    dpix
1159
 * \return  wpl, or 0 on error
1160
 */
1161
l_int32
1162
dpixGetWpl(DPIX  *dpix)
1163
0
{
1164
0
    if (!dpix)
1165
0
        return ERROR_INT("dpix not defined", __func__, 0);
1166
0
    return dpix->wpl;
1167
0
}
1168
1169
1170
/*!
1171
 * \brief   dpixSetWpl()
1172
 *
1173
 * \param[in]    dpix
1174
 * \param[in]    wpl
1175
 * \return  0 if OK, 1 on error
1176
 */
1177
l_ok
1178
dpixSetWpl(DPIX    *dpix,
1179
           l_int32  wpl)
1180
0
{
1181
0
    if (!dpix)
1182
0
        return ERROR_INT("dpix not defined", __func__, 1);
1183
1184
0
    dpix->wpl = wpl;
1185
0
    return 0;
1186
0
}
1187
1188
1189
/*!
1190
 * \brief   dpixGetResolution()
1191
 *
1192
 * \param[in]    dpix
1193
 * \param[out]   pxres, pyres    [optional] x and y resolution
1194
 * \return  0 if OK, 1 on error
1195
 */
1196
l_ok
1197
dpixGetResolution(DPIX     *dpix,
1198
                  l_int32  *pxres,
1199
                  l_int32  *pyres)
1200
0
{
1201
0
    if (!dpix)
1202
0
        return ERROR_INT("dpix not defined", __func__, 1);
1203
0
    if (pxres) *pxres = dpix->xres;
1204
0
    if (pyres) *pyres = dpix->yres;
1205
0
    return 0;
1206
0
}
1207
1208
1209
/*!
1210
 * \brief   dpixSetResolution()
1211
 *
1212
 * \param[in]    dpix
1213
 * \param[in]    xres, yres     x and y resolution
1214
 * \return  0 if OK, 1 on error
1215
 */
1216
l_ok
1217
dpixSetResolution(DPIX    *dpix,
1218
                  l_int32  xres,
1219
                  l_int32  yres)
1220
0
{
1221
0
    if (!dpix)
1222
0
        return ERROR_INT("dpix not defined", __func__, 1);
1223
1224
0
    dpix->xres = xres;
1225
0
    dpix->yres = yres;
1226
0
    return 0;
1227
0
}
1228
1229
1230
/*!
1231
 * \brief   dpixCopyResolution()
1232
 *
1233
 * \param[in]    dpixd, dpixs
1234
 * \return  0 if OK, 1 on error
1235
 */
1236
l_ok
1237
dpixCopyResolution(DPIX  *dpixd,
1238
                   DPIX  *dpixs)
1239
0
{
1240
0
l_int32  xres, yres;
1241
0
    if (!dpixs || !dpixd)
1242
0
        return ERROR_INT("dpixs and dpixd not both defined", __func__, 1);
1243
1244
0
    dpixGetResolution(dpixs, &xres, &yres);
1245
0
    dpixSetResolution(dpixd, xres, yres);
1246
0
    return 0;
1247
0
}
1248
1249
1250
/*!
1251
 * \brief   dpixGetData()
1252
 *
1253
 * \param[in]    dpix
1254
 * \return  ptr to dpix data, or NULL on error
1255
 */
1256
l_float64 *
1257
dpixGetData(DPIX  *dpix)
1258
0
{
1259
0
    if (!dpix)
1260
0
        return (l_float64 *)ERROR_PTR("dpix not defined", __func__, NULL);
1261
0
    return dpix->data;
1262
0
}
1263
1264
1265
/*!
1266
 * \brief   dpixSetData()
1267
 *
1268
 * \param[in]    dpix
1269
 * \param[in]    data
1270
 * \return  0 if OK, 1 on error
1271
 */
1272
l_ok
1273
dpixSetData(DPIX       *dpix,
1274
            l_float64  *data)
1275
0
{
1276
0
    if (!dpix)
1277
0
        return ERROR_INT("dpix not defined", __func__, 1);
1278
1279
0
    dpix->data = data;
1280
0
    return 0;
1281
0
}
1282
1283
1284
/*!
1285
 * \brief   dpixGetPixel()
1286
 *
1287
 * \param[in]    dpix
1288
 * \param[in]    x,y     pixel coords
1289
 * \param[out]   pval    pixel value
1290
 * \return  0 if OK; 1 or 2 on error
1291
 *
1292
 * Notes:
1293
 *      (1) If the point is outside the image, this returns an error (2),
1294
 *          with 0.0 in %pval.  To avoid spamming output, it fails silently.
1295
 */
1296
l_ok
1297
dpixGetPixel(DPIX       *dpix,
1298
             l_int32     x,
1299
             l_int32     y,
1300
             l_float64  *pval)
1301
0
{
1302
0
l_int32  w, h;
1303
1304
0
    if (!pval)
1305
0
        return ERROR_INT("pval not defined", __func__, 1);
1306
0
    *pval = 0.0;
1307
0
    if (!dpix)
1308
0
        return ERROR_INT("dpix not defined", __func__, 1);
1309
1310
0
    dpixGetDimensions(dpix, &w, &h);
1311
0
    if (x < 0 || x >= w || y < 0 || y >= h)
1312
0
        return 2;
1313
1314
0
    *pval = *(dpix->data + y * w + x);
1315
0
    return 0;
1316
0
}
1317
1318
1319
/*!
1320
 * \brief   dpixSetPixel()
1321
 *
1322
 * \param[in]    dpix
1323
 * \param[in]    x,y    pixel coords
1324
 * \param[in]    val    pixel value
1325
 * \return  0 if OK; 1 or 2 on error
1326
 *
1327
 * Notes:
1328
 *      (1) If the point is outside the image, this returns an error (2),
1329
 *          with 0.0 in %pval.  To avoid spamming output, it fails silently.
1330
 */
1331
l_ok
1332
dpixSetPixel(DPIX      *dpix,
1333
             l_int32    x,
1334
             l_int32    y,
1335
             l_float64  val)
1336
0
{
1337
0
l_int32  w, h;
1338
1339
0
    if (!dpix)
1340
0
        return ERROR_INT("dpix not defined", __func__, 1);
1341
1342
0
    dpixGetDimensions(dpix, &w, &h);
1343
0
    if (x < 0 || x >= w || y < 0 || y >= h)
1344
0
        return 2;
1345
1346
0
    *(dpix->data + y * w + x) = val;
1347
0
    return 0;
1348
0
}
1349
1350
1351
/*--------------------------------------------------------------------*
1352
 *                       FPix serialized I/O                          *
1353
 *--------------------------------------------------------------------*/
1354
/*!
1355
 * \brief   fpixRead()
1356
 *
1357
 * \param[in]    filename
1358
 * \return  fpix, or NULL on error
1359
 */
1360
FPIX *
1361
fpixRead(const char  *filename)
1362
0
{
1363
0
FILE  *fp;
1364
0
FPIX  *fpix;
1365
1366
0
    if (!filename)
1367
0
        return (FPIX *)ERROR_PTR("filename not defined", __func__, NULL);
1368
1369
0
    if ((fp = fopenReadStream(filename)) == NULL)
1370
0
        return (FPIX *)ERROR_PTR_1("stream not opened",
1371
0
                                   filename, __func__, NULL);
1372
0
    fpix = fpixReadStream(fp);
1373
0
    fclose(fp);
1374
0
    if (!fpix)
1375
0
        return (FPIX *)ERROR_PTR_1("fpix not read", filename, __func__, NULL);
1376
0
    return fpix;
1377
0
}
1378
1379
1380
/*!
1381
 * \brief   fpixReadStream()
1382
 *
1383
 * \param[in]    fp     file stream
1384
 * \return  fpix, or NULL on error
1385
 */
1386
FPIX *
1387
fpixReadStream(FILE  *fp)
1388
0
{
1389
0
char        buf[256];
1390
0
l_int32     w, h, nbytes, xres, yres, version;
1391
0
l_float32  *data;
1392
0
FPIX       *fpix;
1393
1394
0
    if (!fp)
1395
0
        return (FPIX *)ERROR_PTR("stream not defined", __func__, NULL);
1396
1397
0
    if (fscanf(fp, "\nFPix Version %d\n", &version) != 1)
1398
0
        return (FPIX *)ERROR_PTR("not a fpix file", __func__, NULL);
1399
0
    if (version != FPIX_VERSION_NUMBER)
1400
0
        return (FPIX *)ERROR_PTR("invalid fpix version", __func__, NULL);
1401
0
    if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3)
1402
0
        return (FPIX *)ERROR_PTR("read fail for data size", __func__, NULL);
1403
1404
        /* Use fgets() and sscanf(); not fscanf(), for the last
1405
         * bit of header data before the float data.  The reason is
1406
         * that fscanf throws away white space, and if the float data
1407
         * happens to begin with ascii character(s) that are white
1408
         * space, it will swallow them and all will be lost!  */
1409
0
    if (fgets(buf, sizeof(buf), fp) == NULL)
1410
0
        return (FPIX *)ERROR_PTR("fgets read fail", __func__, NULL);
1411
0
    if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2)
1412
0
        return (FPIX *)ERROR_PTR("read fail for xres, yres", __func__, NULL);
1413
1414
0
    if ((fpix = fpixCreate(w, h)) == NULL)
1415
0
        return (FPIX *)ERROR_PTR("fpix not made", __func__, NULL);
1416
0
    fpixSetResolution(fpix, xres, yres);
1417
0
    data = fpixGetData(fpix);
1418
0
    if (fread(data, 1, nbytes, fp) != nbytes) {
1419
0
        fpixDestroy(&fpix);
1420
0
        return (FPIX *)ERROR_PTR("read error for nbytes", __func__, NULL);
1421
0
    }
1422
0
    fgetc(fp);  /* ending nl */
1423
1424
        /* Convert to little-endian if necessary */
1425
0
    fpixEndianByteSwap(fpix, fpix);
1426
0
    return fpix;
1427
0
}
1428
1429
1430
/*!
1431
 * \brief   fpixReadMem()
1432
 *
1433
 * \param[in]    data    of serialized fpix
1434
 * \param[in]    size    of data in bytes
1435
 * \return  fpix, or NULL on error
1436
 */
1437
FPIX *
1438
fpixReadMem(const l_uint8  *data,
1439
            size_t          size)
1440
0
{
1441
0
FILE  *fp;
1442
0
FPIX  *fpix;
1443
1444
0
    if (!data)
1445
0
        return (FPIX *)ERROR_PTR("data not defined", __func__, NULL);
1446
0
    if ((fp = fopenReadFromMemory(data, size)) == NULL)
1447
0
        return (FPIX *)ERROR_PTR("stream not opened", __func__, NULL);
1448
1449
0
    fpix = fpixReadStream(fp);
1450
0
    fclose(fp);
1451
0
    if (!fpix) L_ERROR("fpix not read\n", __func__);
1452
0
    return fpix;
1453
0
}
1454
1455
1456
/*!
1457
 * \brief   fpixWrite()
1458
 *
1459
 * \param[in]    filename
1460
 * \param[in]    fpix
1461
 * \return  0 if OK, 1 on error
1462
 */
1463
l_ok
1464
fpixWrite(const char  *filename,
1465
          FPIX        *fpix)
1466
0
{
1467
0
l_int32  ret;
1468
0
FILE    *fp;
1469
1470
0
    if (!filename)
1471
0
        return ERROR_INT("filename not defined", __func__, 1);
1472
0
    if (!fpix)
1473
0
        return ERROR_INT("fpix not defined", __func__, 1);
1474
1475
0
    if ((fp = fopenWriteStream(filename, "wb")) == NULL)
1476
0
        return ERROR_INT_1("stream not opened", filename, __func__, 1);
1477
0
    ret = fpixWriteStream(fp, fpix);
1478
0
    fclose(fp);
1479
0
    if (ret)
1480
0
        return ERROR_INT_1("fpix not written to stream", filename, __func__, 1);
1481
0
    return 0;
1482
0
}
1483
1484
1485
/*!
1486
 * \brief   fpixWriteStream()
1487
 *
1488
 * \param[in]    fp       file stream opened for "wb"
1489
 * \param[in]    fpix
1490
 * \return  0 if OK, 1 on error
1491
 */
1492
l_ok
1493
fpixWriteStream(FILE  *fp,
1494
                FPIX  *fpix)
1495
0
{
1496
0
l_int32     w, h, xres, yres;
1497
0
l_uint32    nbytes;
1498
0
l_float32  *data;
1499
0
FPIX       *fpixt;
1500
1501
0
    if (!fp)
1502
0
        return ERROR_INT("stream not defined", __func__, 1);
1503
0
    if (!fpix)
1504
0
        return ERROR_INT("fpix not defined", __func__, 1);
1505
1506
        /* Convert to little-endian if necessary */
1507
0
    fpixt = fpixEndianByteSwap(NULL, fpix);
1508
1509
0
    fpixGetDimensions(fpixt, &w, &h);
1510
0
    data = fpixGetData(fpixt);
1511
0
    nbytes = sizeof(l_float32) * w * h;
1512
0
    fpixGetResolution(fpixt, &xres, &yres);
1513
0
    fprintf(fp, "\nFPix Version %d\n", FPIX_VERSION_NUMBER);
1514
0
    fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes);
1515
0
    fprintf(fp, "xres = %d, yres = %d\n", xres, yres);
1516
0
    fwrite(data, 1, nbytes, fp);
1517
0
    fprintf(fp, "\n");
1518
1519
0
    fpixDestroy(&fpixt);
1520
0
    return 0;
1521
0
}
1522
1523
1524
/*!
1525
 * \brief   fpixWriteMem()
1526
 *
1527
 * \param[out]   pdata     data of serialized fpix
1528
 * \param[out]   psize     size of returned data
1529
 * \param[in]    fpix
1530
 * \return  0 if OK, 1 on error
1531
 *
1532
 * <pre>
1533
 * Notes:
1534
 *      (1) Serializes a fpix in memory and puts the result in a buffer.
1535
 * </pre>
1536
 */
1537
l_ok
1538
fpixWriteMem(l_uint8  **pdata,
1539
             size_t    *psize,
1540
             FPIX      *fpix)
1541
0
{
1542
0
l_int32  ret;
1543
0
FILE    *fp;
1544
1545
0
    if (pdata) *pdata = NULL;
1546
0
    if (psize) *psize = 0;
1547
0
    if (!pdata)
1548
0
        return ERROR_INT("&data not defined", __func__, 1);
1549
0
    if (!psize)
1550
0
        return ERROR_INT("&size not defined", __func__, 1);
1551
0
    if (!fpix)
1552
0
        return ERROR_INT("fpix not defined", __func__, 1);
1553
1554
0
#if HAVE_FMEMOPEN
1555
0
    if ((fp = open_memstream((char **)pdata, psize)) == NULL)
1556
0
        return ERROR_INT("stream not opened", __func__, 1);
1557
0
    ret = fpixWriteStream(fp, fpix);
1558
0
    fputc('\0', fp);
1559
0
    fclose(fp);
1560
0
    *psize = *psize - 1;
1561
#else
1562
    L_INFO("no fmemopen API --> work-around: write to temp file\n", __func__);
1563
  #ifdef _WIN32
1564
    if ((fp = fopenWriteWinTempfile()) == NULL)
1565
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1566
  #else
1567
    if ((fp = tmpfile()) == NULL)
1568
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1569
  #endif  /* _WIN32 */
1570
    ret = fpixWriteStream(fp, fpix);
1571
    rewind(fp);
1572
    *pdata = l_binaryReadStream(fp, psize);
1573
    fclose(fp);
1574
#endif  /* HAVE_FMEMOPEN */
1575
0
    return ret;
1576
0
}
1577
1578
1579
/*!
1580
 * \brief   fpixEndianByteSwap()
1581
 *
1582
 * \param[in]    fpixd     [optional] can be either NULL, or equal to fpixs
1583
 * \param[in]    fpixs
1584
 * \return  fpixd always
1585
 *
1586
 * <pre>
1587
 * Notes:
1588
 *      (1) On big-endian hardware, this does byte-swapping on each of
1589
 *          the 4-byte floats in the fpix data.  On little-endians,
1590
 *          the data is unchanged.  This is used for serialization
1591
 *          of fpix; the data is serialized in little-endian byte
1592
 *          order because most hardware is little-endian.
1593
 *      (2) The operation can be either in-place or, if fpixd == NULL,
1594
 *          a new fpix is made.  If not in-place, caller must catch
1595
 *          the returned pointer.
1596
 * </pre>
1597
 */
1598
FPIX *
1599
fpixEndianByteSwap(FPIX  *fpixd,
1600
                   FPIX  *fpixs)
1601
0
{
1602
0
    if (!fpixs)
1603
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, fpixd);
1604
0
    if (fpixd && (fpixs != fpixd))
1605
0
        return (FPIX *)ERROR_PTR("fpixd != fpixs", __func__, fpixd);
1606
1607
#ifdef L_BIG_ENDIAN
1608
    {
1609
    l_uint32  *data;
1610
    l_int32    i, j, w, h;
1611
    l_uint32   word;
1612
1613
        fpixGetDimensions(fpixs, &w, &h);
1614
        if (!fpixd)
1615
            fpixd = fpixCopy(fpixs);
1616
1617
        data = (l_uint32 *)fpixGetData(fpixd);
1618
        for (i = 0; i < h; i++) {
1619
            for (j = 0; j < w; j++, data++) {
1620
                word = *data;
1621
                *data = (word >> 24) |
1622
                        ((word >> 8) & 0x0000ff00) |
1623
                        ((word << 8) & 0x00ff0000) |
1624
                        (word << 24);
1625
            }
1626
        }
1627
        return fpixd;
1628
    }
1629
#else   /* L_LITTLE_ENDIAN */
1630
1631
0
    if (fpixd)
1632
0
        return fpixd;  /* no-op */
1633
0
    else
1634
0
        return fpixClone(fpixs);
1635
1636
0
#endif   /* L_BIG_ENDIAN */
1637
0
}
1638
1639
1640
/*--------------------------------------------------------------------*
1641
 *                       DPix serialized I/O                          *
1642
 *--------------------------------------------------------------------*/
1643
/*!
1644
 * \brief   dpixRead()
1645
 *
1646
 * \param[in]    filename
1647
 * \return  dpix, or NULL on error
1648
 */
1649
DPIX *
1650
dpixRead(const char  *filename)
1651
0
{
1652
0
FILE  *fp;
1653
0
DPIX  *dpix;
1654
1655
0
    if (!filename)
1656
0
        return (DPIX *)ERROR_PTR("filename not defined", __func__, NULL);
1657
1658
0
    if ((fp = fopenReadStream(filename)) == NULL)
1659
0
        return (DPIX *)ERROR_PTR_1("stream not opened",
1660
0
                                   filename, __func__, NULL);
1661
0
    dpix = dpixReadStream(fp);
1662
0
    fclose(fp);
1663
0
    if (!dpix)
1664
0
        return (DPIX *)ERROR_PTR_1("dpix not read", filename, __func__, NULL);
1665
0
    return dpix;
1666
0
}
1667
1668
1669
/*!
1670
 * \brief   dpixReadStream()
1671
 *
1672
 * \param[in]    fp      file stream
1673
 * \return  dpix, or NULL on error
1674
 */
1675
DPIX *
1676
dpixReadStream(FILE  *fp)
1677
0
{
1678
0
char        buf[256];
1679
0
l_int32     w, h, nbytes, version, xres, yres;
1680
0
l_float64  *data;
1681
0
DPIX       *dpix;
1682
1683
0
    if (!fp)
1684
0
        return (DPIX *)ERROR_PTR("stream not defined", __func__, NULL);
1685
1686
0
    if (fscanf(fp, "\nDPix Version %d\n", &version) != 1)
1687
0
        return (DPIX *)ERROR_PTR("not a dpix file", __func__, NULL);
1688
0
    if (version != DPIX_VERSION_NUMBER)
1689
0
        return (DPIX *)ERROR_PTR("invalid dpix version", __func__, NULL);
1690
0
    if (fscanf(fp, "w = %d, h = %d, nbytes = %d\n", &w, &h, &nbytes) != 3)
1691
0
        return (DPIX *)ERROR_PTR("read fail for data size", __func__, NULL);
1692
1693
        /* Use fgets() and sscanf(); not fscanf(), for the last
1694
         * bit of header data before the float data.  The reason is
1695
         * that fscanf throws away white space, and if the float data
1696
         * happens to begin with ascii character(s) that are white
1697
         * space, it will swallow them and all will be lost!  */
1698
0
    if (fgets(buf, sizeof(buf), fp) == NULL)
1699
0
        return (DPIX *)ERROR_PTR("fgets read fail", __func__, NULL);
1700
0
    if (sscanf(buf, "xres = %d, yres = %d\n", &xres, &yres) != 2)
1701
0
        return (DPIX *)ERROR_PTR("read fail for xres, yres", __func__, NULL);
1702
1703
0
    if ((dpix = dpixCreate(w, h)) == NULL)
1704
0
        return (DPIX *)ERROR_PTR("dpix not made", __func__, NULL);
1705
0
    dpixSetResolution(dpix, xres, yres);
1706
0
    data = dpixGetData(dpix);
1707
0
    if (fread(data, 1, nbytes, fp) != nbytes) {
1708
0
        dpixDestroy(&dpix);
1709
0
        return (DPIX *)ERROR_PTR("read error for nbytes", __func__, NULL);
1710
0
    }
1711
0
    fgetc(fp);  /* ending nl */
1712
1713
        /* Convert to little-endian if necessary */
1714
0
    dpixEndianByteSwap(dpix, dpix);
1715
0
    return dpix;
1716
0
}
1717
1718
1719
/*!
1720
 * \brief   dpixReadMem()
1721
 *
1722
 * \param[in]    data     of serialized dpix
1723
 * \param[in]    size     of data in bytes
1724
 * \return  dpix, or NULL on error
1725
 */
1726
DPIX *
1727
dpixReadMem(const l_uint8  *data,
1728
            size_t          size)
1729
0
{
1730
0
FILE  *fp;
1731
0
DPIX  *dpix;
1732
1733
0
    if (!data)
1734
0
        return (DPIX *)ERROR_PTR("data not defined", __func__, NULL);
1735
0
    if ((fp = fopenReadFromMemory(data, size)) == NULL)
1736
0
        return (DPIX *)ERROR_PTR("stream not opened", __func__, NULL);
1737
1738
0
    dpix = dpixReadStream(fp);
1739
0
    fclose(fp);
1740
0
    if (!dpix) L_ERROR("dpix not read\n", __func__);
1741
0
    return dpix;
1742
0
}
1743
1744
1745
/*!
1746
 * \brief   dpixWrite()
1747
 *
1748
 * \param[in]    filename
1749
 * \param[in]    dpix
1750
 * \return  0 if OK, 1 on error
1751
 */
1752
l_ok
1753
dpixWrite(const char  *filename,
1754
          DPIX        *dpix)
1755
0
{
1756
0
l_int32  ret;
1757
0
FILE    *fp;
1758
1759
0
    if (!filename)
1760
0
        return ERROR_INT("filename not defined", __func__, 1);
1761
0
    if (!dpix)
1762
0
        return ERROR_INT("dpix not defined", __func__, 1);
1763
1764
0
    if ((fp = fopenWriteStream(filename, "wb")) == NULL)
1765
0
        return ERROR_INT_1("stream not opened", filename, __func__, 1);
1766
0
    ret = dpixWriteStream(fp, dpix);
1767
0
    fclose(fp);
1768
0
    if (ret)
1769
0
        return ERROR_INT_1("dpix not written to stream", filename, __func__, 1);
1770
0
    return 0;
1771
0
}
1772
1773
1774
/*!
1775
 * \brief   dpixWriteStream()
1776
 *
1777
 * \param[in]    fp      file stream opened for "wb"
1778
 * \param[in]    dpix
1779
 * \return  0 if OK, 1 on error
1780
 */
1781
l_ok
1782
dpixWriteStream(FILE  *fp,
1783
                DPIX  *dpix)
1784
0
{
1785
0
l_int32     w, h, xres, yres;
1786
0
l_uint32    nbytes;
1787
0
l_float64  *data;
1788
0
DPIX       *dpixt;
1789
1790
0
    if (!fp)
1791
0
        return ERROR_INT("stream not defined", __func__, 1);
1792
0
    if (!dpix)
1793
0
        return ERROR_INT("dpix not defined", __func__, 1);
1794
1795
        /* Convert to little-endian if necessary */
1796
0
    dpixt = dpixEndianByteSwap(NULL, dpix);
1797
1798
0
    dpixGetDimensions(dpixt, &w, &h);
1799
0
    dpixGetResolution(dpixt, &xres, &yres);
1800
0
    data = dpixGetData(dpixt);
1801
0
    nbytes = sizeof(l_float64) * w * h;
1802
0
    fprintf(fp, "\nDPix Version %d\n", DPIX_VERSION_NUMBER);
1803
0
    fprintf(fp, "w = %d, h = %d, nbytes = %u\n", w, h, nbytes);
1804
0
    fprintf(fp, "xres = %d, yres = %d\n", xres, yres);
1805
0
    fwrite(data, 1, nbytes, fp);
1806
0
    fprintf(fp, "\n");
1807
1808
0
    dpixDestroy(&dpixt);
1809
0
    return 0;
1810
0
}
1811
1812
1813
/*!
1814
 * \brief   dpixWriteMem()
1815
 *
1816
 * \param[out]   pdata     data of serialized dpix
1817
 * \param[out]   psize     size of returned data
1818
 * \param[in]    dpix
1819
 * \return  0 if OK, 1 on error
1820
 *
1821
 * <pre>
1822
 * Notes:
1823
 *      (1) Serializes a dpix in memory and puts the result in a buffer.
1824
 * </pre>
1825
 */
1826
l_ok
1827
dpixWriteMem(l_uint8  **pdata,
1828
             size_t    *psize,
1829
             DPIX      *dpix)
1830
0
{
1831
0
l_int32  ret;
1832
0
FILE    *fp;
1833
1834
0
    if (pdata) *pdata = NULL;
1835
0
    if (psize) *psize = 0;
1836
0
    if (!pdata)
1837
0
        return ERROR_INT("&data not defined", __func__, 1);
1838
0
    if (!psize)
1839
0
        return ERROR_INT("&size not defined", __func__, 1);
1840
0
    if (!dpix)
1841
0
        return ERROR_INT("dpix not defined", __func__, 1);
1842
1843
0
#if HAVE_FMEMOPEN
1844
0
    if ((fp = open_memstream((char **)pdata, psize)) == NULL)
1845
0
        return ERROR_INT("stream not opened", __func__, 1);
1846
0
    ret = dpixWriteStream(fp, dpix);
1847
0
    fputc('\0', fp);
1848
0
    fclose(fp);
1849
0
    *psize = *psize - 1;
1850
#else
1851
    L_INFO("no fmemopen API --> work-around: write to temp file\n", __func__);
1852
  #ifdef _WIN32
1853
    if ((fp = fopenWriteWinTempfile()) == NULL)
1854
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1855
  #else
1856
    if ((fp = tmpfile()) == NULL)
1857
        return ERROR_INT("tmpfile stream not opened", __func__, 1);
1858
  #endif  /* _WIN32 */
1859
    ret = dpixWriteStream(fp, dpix);
1860
    rewind(fp);
1861
    *pdata = l_binaryReadStream(fp, psize);
1862
    fclose(fp);
1863
#endif  /* HAVE_FMEMOPEN */
1864
0
    return ret;
1865
0
}
1866
1867
1868
/*!
1869
 * \brief   dpixEndianByteSwap()
1870
 *
1871
 * \param[in]    dpixd     [optional] can be either NULL, or equal to dpixs
1872
 * \param[in]    dpixs
1873
 * \return  dpixd always
1874
 *
1875
 * <pre>
1876
 * Notes:
1877
 *      (1) On big-endian hardware, this does byte-swapping on each of
1878
 *          the 4-byte words in the dpix data.  On little-endians,
1879
 *          the data is unchanged.  This is used for serialization
1880
 *          of dpix; the data is serialized in little-endian byte
1881
 *          order because most hardware is little-endian.
1882
 *      (2) The operation can be either in-place or, if dpixd == NULL,
1883
 *          a new dpix is made.  If not in-place, caller must catch
1884
 *          the returned pointer.
1885
 * </pre>
1886
 */
1887
DPIX *
1888
dpixEndianByteSwap(DPIX  *dpixd,
1889
                   DPIX  *dpixs)
1890
0
{
1891
0
    if (!dpixs)
1892
0
        return (DPIX *)ERROR_PTR("dpixs not defined", __func__, dpixd);
1893
0
    if (dpixd && (dpixs != dpixd))
1894
0
        return (DPIX *)ERROR_PTR("dpixd != dpixs", __func__, dpixd);
1895
1896
#ifdef L_BIG_ENDIAN
1897
    {
1898
    l_uint32  *data;
1899
    l_int32    i, j, w, h;
1900
    l_uint32   word;
1901
1902
        dpixGetDimensions(dpixs, &w, &h);
1903
        if (!dpixd)
1904
            dpixd = dpixCopy(dpixs);
1905
1906
        data = (l_uint32 *)dpixGetData(dpixd);
1907
        for (i = 0; i < h; i++) {
1908
            for (j = 0; j < 2 * w; j++, data++) {
1909
                word = *data;
1910
                *data = (word >> 24) |
1911
                        ((word >> 8) & 0x0000ff00) |
1912
                        ((word << 8) & 0x00ff0000) |
1913
                        (word << 24);
1914
            }
1915
        }
1916
        return dpixd;
1917
    }
1918
#else   /* L_LITTLE_ENDIAN */
1919
1920
0
    if (dpixd)
1921
0
        return dpixd;  /* no-op */
1922
0
    else
1923
0
        return dpixClone(dpixs);
1924
1925
0
#endif   /* L_BIG_ENDIAN */
1926
0
}
1927
1928
1929
/*--------------------------------------------------------------------*
1930
 *                 Print FPix (subsampled, for debugging)             *
1931
 *--------------------------------------------------------------------*/
1932
/*!
1933
 * \brief   fpixPrintStream()
1934
 *
1935
 * \param[in]    fp       file stream
1936
 * \param[in]    fpix
1937
 * \param[in]    factor   for subsampling
1938
 * \return  0 if OK, 1 on error
1939
 *
1940
 * <pre>
1941
 * Notes:
1942
 *      (1) Subsampled printout of fpix for debugging.
1943
 * </pre>
1944
 */
1945
l_ok
1946
fpixPrintStream(FILE    *fp,
1947
                FPIX    *fpix,
1948
                l_int32  factor)
1949
0
{
1950
0
l_int32    i, j, w, h, count;
1951
0
l_float32  val;
1952
1953
0
    if (!fp)
1954
0
        return ERROR_INT("stream not defined", __func__, 1);
1955
0
    if (!fpix)
1956
0
        return ERROR_INT("fpix not defined", __func__, 1);
1957
0
    if (factor < 1)
1958
0
        return ERROR_INT("sampling factor < 1f", __func__, 1);
1959
1960
0
    fpixGetDimensions(fpix, &w, &h);
1961
0
    fprintf(fp, "\nFPix: w = %d, h = %d\n", w, h);
1962
0
    for (i = 0; i < h; i += factor) {
1963
0
        for (count = 0, j = 0; j < w; j += factor, count++) {
1964
0
            fpixGetPixel(fpix, j, i, &val);
1965
0
            fprintf(fp, "val[%d, %d] = %f   ", i, j, val);
1966
0
            if ((count + 1) % 3 == 0) fprintf(fp, "\n");
1967
0
        }
1968
0
        if (count % 3) fprintf(fp, "\n");
1969
0
     }
1970
0
     fprintf(fp, "\n");
1971
0
     return 0;
1972
0
}