Coverage Report

Created: 2024-06-18 06:06

/src/leptonica/src/fpix2.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 fpix2.c
29
 * <pre>
30
 *
31
 *    ------------------------------------------
32
 *    This file has these FPix utilities:
33
 *       ~ interconversions with pix, fpix, dpix
34
 *       ~ min and max values
35
 *       ~ integer scaling
36
 *       ~ arithmetic operations
37
 *       ~ set all
38
 *       ~ border functions
39
 *       ~ simple rasterop (source --> dest)
40
 *       ~ geometric transforms
41
 *    ------------------------------------------
42
 *
43
 *    Interconversions between Pix, FPix and DPix
44
 *          FPIX          *pixConvertToFPix()
45
 *          DPIX          *pixConvertToDPix()
46
 *          PIX           *fpixConvertToPix()
47
 *          PIX           *fpixDisplayMaxDynamicRange()  [useful for debugging]
48
 *          DPIX          *fpixConvertToDPix()
49
 *          PIX           *dpixConvertToPix()
50
 *          FPIX          *dpixConvertToFPix()
51
 *
52
 *    Min/max value
53
 *          l_int32        fpixGetMin()
54
 *          l_int32        fpixGetMax()
55
 *          l_int32        dpixGetMin()
56
 *          l_int32        dpixGetMax()
57
 *
58
 *    Integer scaling
59
 *          FPIX          *fpixScaleByInteger()
60
 *          DPIX          *dpixScaleByInteger()
61
 *
62
 *    Arithmetic operations
63
 *          FPIX          *fpixLinearCombination()
64
 *          l_int32        fpixAddMultConstant()
65
 *          DPIX          *dpixLinearCombination()
66
 *          l_int32        dpixAddMultConstant()
67
 *
68
 *    Set all
69
 *          l_int32        fpixSetAllArbitrary()
70
 *          l_int32        dpixSetAllArbitrary()
71
 *
72
 *    FPix border functions
73
 *          FPIX          *fpixAddBorder()
74
 *          FPIX          *fpixRemoveBorder()
75
 *          FPIX          *fpixAddMirroredBorder()
76
 *          FPIX          *fpixAddContinuedBorder()
77
 *          FPIX          *fpixAddSlopeBorder()
78
 *
79
 *    FPix simple rasterop
80
 *          l_int32        fpixRasterop()
81
 *
82
 *    FPix rotation by multiples of 90 degrees
83
 *          FPIX          *fpixRotateOrth()
84
 *          FPIX          *fpixRotate180()
85
 *          FPIX          *fpixRotate90()
86
 *          FPIX          *fpixFlipLR()
87
 *          FPIX          *fpixFlipTB()
88
 *
89
 *    FPix affine and projective interpolated transforms
90
 *          FPIX          *fpixAffinePta()
91
 *          FPIX          *fpixAffine()
92
 *          FPIX          *fpixProjectivePta()
93
 *          FPIX          *fpixProjective()
94
 *          l_int32        linearInterpolatePixelFloat()
95
 *
96
 *    Thresholding to 1 bpp Pix
97
 *          PIX           *fpixThresholdToPix()
98
 *
99
 *    Generate function from components
100
 *          FPIX          *pixComponentFunction()
101
 * </pre>
102
 */
103
104
#ifdef HAVE_CONFIG_H
105
#include <config_auto.h>
106
#endif  /* HAVE_CONFIG_H */
107
108
#include <string.h>
109
#include "allheaders.h"
110
111
/*--------------------------------------------------------------------*
112
 *                     FPix  <-->  Pix conversions                    *
113
 *--------------------------------------------------------------------*/
114
/*!
115
 * \brief   pixConvertToFPix()
116
 *
117
 * \param[in]    pixs      1, 2, 4, 8, 16 or 32 bpp
118
 * \param[in]    ncomps    number of components: 3 for RGB, 1 otherwise
119
 * \return  fpix, or NULL on error
120
 *
121
 * <pre>
122
 * Notes:
123
 *      (1) If colormapped, remove to grayscale.
124
 *      (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance.
125
 *          In all other cases the src image is treated as having a single
126
 *          component of pixel values.
127
 * </pre>
128
 */
129
FPIX *
130
pixConvertToFPix(PIX     *pixs,
131
                 l_int32  ncomps)
132
0
{
133
0
l_int32     w, h, d, i, j, val, wplt, wpld;
134
0
l_uint32    uval;
135
0
l_uint32   *datat, *linet;
136
0
l_float32  *datad, *lined;
137
0
PIX        *pixt;
138
0
FPIX       *fpixd;
139
140
0
    if (!pixs)
141
0
        return (FPIX *)ERROR_PTR("pixs not defined", __func__, NULL);
142
143
           /* Convert to a single component */
144
0
    if (pixGetColormap(pixs))
145
0
        pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
146
0
    else if (pixGetDepth(pixs) == 32 && ncomps == 3)
147
0
        pixt = pixConvertRGBToLuminance(pixs);
148
0
    else
149
0
        pixt = pixClone(pixs);
150
0
    pixGetDimensions(pixt, &w, &h, &d);
151
0
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) {
152
0
        pixDestroy(&pixt);
153
0
        return (FPIX *)ERROR_PTR("invalid depth", __func__, NULL);
154
0
    }
155
156
0
    if ((fpixd = fpixCreate(w, h)) == NULL) {
157
0
        pixDestroy(&pixt);
158
0
        return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL);
159
0
    }
160
0
    datat = pixGetData(pixt);
161
0
    wplt = pixGetWpl(pixt);
162
0
    datad = fpixGetData(fpixd);
163
0
    wpld = fpixGetWpl(fpixd);
164
0
    for (i = 0; i < h; i++) {
165
0
        linet = datat + i * wplt;
166
0
        lined = datad + i * wpld;
167
0
        if (d == 1) {
168
0
            for (j = 0; j < w; j++) {
169
0
                val = GET_DATA_BIT(linet, j);
170
0
                lined[j] = (l_float32)val;
171
0
            }
172
0
        } else if (d == 2) {
173
0
            for (j = 0; j < w; j++) {
174
0
                val = GET_DATA_DIBIT(linet, j);
175
0
                lined[j] = (l_float32)val;
176
0
            }
177
0
        } else if (d == 4) {
178
0
            for (j = 0; j < w; j++) {
179
0
                val = GET_DATA_QBIT(linet, j);
180
0
                lined[j] = (l_float32)val;
181
0
            }
182
0
        } else if (d == 8) {
183
0
            for (j = 0; j < w; j++) {
184
0
                val = GET_DATA_BYTE(linet, j);
185
0
                lined[j] = (l_float32)val;
186
0
            }
187
0
        } else if (d == 16) {
188
0
            for (j = 0; j < w; j++) {
189
0
                val = GET_DATA_TWO_BYTES(linet, j);
190
0
                lined[j] = (l_float32)val;
191
0
            }
192
0
        } else {  /* d == 32 */
193
0
            for (j = 0; j < w; j++) {
194
0
                uval = GET_DATA_FOUR_BYTES(linet, j);
195
0
                lined[j] = (l_float32)uval;
196
0
            }
197
0
        }
198
0
    }
199
200
0
    pixDestroy(&pixt);
201
0
    return fpixd;
202
0
}
203
204
205
/*!
206
 * \brief   pixConvertToDPix()
207
 *
208
 * \param[in]    pixs      1, 2, 4, 8, 16 or 32 bpp
209
 * \param[in]    ncomps    number of components: 3 for RGB, 1 otherwise
210
 * \return  dpix, or NULL on error
211
 *
212
 * <pre>
213
 * Notes:
214
 *      (1) If colormapped, remove to grayscale.
215
 *      (2) If 32 bpp and %ncomps == 3, this is RGB; convert to luminance.
216
 *          In all other cases the src image is treated as having a single
217
 *          component of pixel values.
218
 * </pre>
219
 */
220
DPIX *
221
pixConvertToDPix(PIX     *pixs,
222
                 l_int32  ncomps)
223
0
{
224
0
l_int32     w, h, d, i, j, val, wplt, wpld;
225
0
l_uint32    uval;
226
0
l_uint32   *datat, *linet;
227
0
l_float64  *datad, *lined;
228
0
PIX        *pixt;
229
0
DPIX       *dpixd;
230
231
0
    if (!pixs)
232
0
        return (DPIX *)ERROR_PTR("pixs not defined", __func__, NULL);
233
234
           /* Convert to a single component */
235
0
    if (pixGetColormap(pixs))
236
0
        pixt = pixRemoveColormap(pixs, REMOVE_CMAP_TO_GRAYSCALE);
237
0
    else if (pixGetDepth(pixs) == 32 && ncomps == 3)
238
0
        pixt = pixConvertRGBToLuminance(pixs);
239
0
    else
240
0
        pixt = pixClone(pixs);
241
0
    pixGetDimensions(pixt, &w, &h, &d);
242
0
    if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) {
243
0
        pixDestroy(&pixt);
244
0
        return (DPIX *)ERROR_PTR("invalid depth", __func__, NULL);
245
0
    }
246
247
0
    if ((dpixd = dpixCreate(w, h)) == NULL) {
248
0
        pixDestroy(&pixt);
249
0
        return (DPIX *)ERROR_PTR("dpixd not made", __func__, NULL);
250
0
    }
251
0
    datat = pixGetData(pixt);
252
0
    wplt = pixGetWpl(pixt);
253
0
    datad = dpixGetData(dpixd);
254
0
    wpld = dpixGetWpl(dpixd);
255
0
    for (i = 0; i < h; i++) {
256
0
        linet = datat + i * wplt;
257
0
        lined = datad + i * wpld;
258
0
        if (d == 1) {
259
0
            for (j = 0; j < w; j++) {
260
0
                val = GET_DATA_BIT(linet, j);
261
0
                lined[j] = (l_float64)val;
262
0
            }
263
0
        } else if (d == 2) {
264
0
            for (j = 0; j < w; j++) {
265
0
                val = GET_DATA_DIBIT(linet, j);
266
0
                lined[j] = (l_float64)val;
267
0
            }
268
0
        } else if (d == 4) {
269
0
            for (j = 0; j < w; j++) {
270
0
                val = GET_DATA_QBIT(linet, j);
271
0
                lined[j] = (l_float64)val;
272
0
            }
273
0
        } else if (d == 8) {
274
0
            for (j = 0; j < w; j++) {
275
0
                val = GET_DATA_BYTE(linet, j);
276
0
                lined[j] = (l_float64)val;
277
0
            }
278
0
        } else if (d == 16) {
279
0
            for (j = 0; j < w; j++) {
280
0
                val = GET_DATA_TWO_BYTES(linet, j);
281
0
                lined[j] = (l_float64)val;
282
0
            }
283
0
        } else {  /* d == 32 */
284
0
            for (j = 0; j < w; j++) {
285
0
                uval = GET_DATA_FOUR_BYTES(linet, j);
286
0
                lined[j] = (l_float64)uval;
287
0
            }
288
0
        }
289
0
    }
290
291
0
    pixDestroy(&pixt);
292
0
    return dpixd;
293
0
}
294
295
296
/*!
297
 * \brief   fpixConvertToPix()
298
 *
299
 * \param[in]    fpixs
300
 * \param[in]    outdepth     0, 8, 16 or 32 bpp
301
 * \param[in]    negvals      L_CLIP_TO_ZERO, L_TAKE_ABSVAL
302
 * \param[in]    errorflag    1 to output error stats; 0 otherwise
303
 * \return  pixd, or NULL on error
304
 *
305
 * <pre>
306
 * Notes:
307
 *      (1) Use %outdepth = 0 to programmatically determine the
308
 *          output depth.  If no values are greater than 255,
309
 *          it will set outdepth = 8; otherwise to 16 or 32.
310
 *      (2) Because we are converting a float to an unsigned int
311
 *          with a specified dynamic range (8, 16 or 32 bits), errors
312
 *          can occur.  If errorflag == TRUE, output the number
313
 *          of values out of range, both negative and positive.
314
 *      (3) If a pixel value is positive and out of range, clip to
315
 *          the maximum value represented at the outdepth of 8, 16
316
 *          or 32 bits.
317
 * </pre>
318
 */
319
PIX *
320
fpixConvertToPix(FPIX    *fpixs,
321
                 l_int32  outdepth,
322
                 l_int32  negvals,
323
                 l_int32  errorflag)
324
0
{
325
0
l_int32     w, h, i, j, wpls, wpld;
326
0
l_uint32    vald, maxval;
327
0
l_float32   val;
328
0
l_float32  *datas, *lines;
329
0
l_uint32   *datad, *lined;
330
0
PIX        *pixd;
331
332
0
    if (!fpixs)
333
0
        return (PIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
334
0
    if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL)
335
0
        return (PIX *)ERROR_PTR("invalid negvals", __func__, NULL);
336
0
    if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32)
337
0
        return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", __func__, NULL);
338
339
0
    fpixGetDimensions(fpixs, &w, &h);
340
0
    datas = fpixGetData(fpixs);
341
0
    wpls = fpixGetWpl(fpixs);
342
343
        /* Adaptive determination of output depth */
344
0
    if (outdepth == 0) {
345
0
        outdepth = 8;
346
0
        for (i = 0; i < h && outdepth < 32; i++) {
347
0
            lines = datas + i * wpls;
348
0
            for (j = 0; j < w && outdepth < 32; j++) {
349
0
                if (lines[j] > 65535.5)
350
0
                    outdepth = 32;
351
0
                else if (lines[j] > 255.5)
352
0
                    outdepth = 16;
353
0
            }
354
0
        }
355
0
    }
356
0
    if (outdepth == 8)
357
0
        maxval = 0xff;
358
0
    else if (outdepth == 16)
359
0
        maxval = 0xffff;
360
0
    else  /* outdepth == 32 */
361
0
        maxval = 0xffffffff;
362
363
        /* Gather statistics if %errorflag = TRUE */
364
0
    if (errorflag) {
365
0
        l_int32  negs = 0;
366
0
        l_int32  overvals = 0;
367
0
        for (i = 0; i < h; i++) {
368
0
            lines = datas + i * wpls;
369
0
            for (j = 0; j < w; j++) {
370
0
                val = lines[j];
371
0
                if (val < 0.0)
372
0
                    negs++;
373
0
                else if (val > maxval)
374
0
                    overvals++;
375
0
            }
376
0
        }
377
0
        if (negs > 0)
378
0
            L_ERROR("Number of negative values: %d\n", __func__, negs);
379
0
        if (overvals > 0)
380
0
            L_ERROR("Number of too-large values: %d\n", __func__, overvals);
381
0
    }
382
383
        /* Make the pix and convert the data */
384
0
    if ((pixd = pixCreate(w, h, outdepth)) == NULL)
385
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
386
0
    datad = pixGetData(pixd);
387
0
    wpld = pixGetWpl(pixd);
388
0
    for (i = 0; i < h; i++) {
389
0
        lines = datas + i * wpls;
390
0
        lined = datad + i * wpld;
391
0
        for (j = 0; j < w; j++) {
392
0
            val = lines[j];
393
0
            if (val >= 0.0)
394
0
                vald = (l_uint32)(val + 0.5);
395
0
            else if (negvals == L_CLIP_TO_ZERO)  /* and val < 0.0 */
396
0
                vald = 0;
397
0
            else
398
0
                vald = (l_uint32)(-val + 0.5);
399
0
            if (vald > maxval)
400
0
                vald = maxval;
401
402
0
            if (outdepth == 8)
403
0
                SET_DATA_BYTE(lined, j, vald);
404
0
            else if (outdepth == 16)
405
0
                SET_DATA_TWO_BYTES(lined, j, vald);
406
0
            else  /* outdepth == 32 */
407
0
                SET_DATA_FOUR_BYTES(lined, j, vald);
408
0
        }
409
0
    }
410
411
0
    return pixd;
412
0
}
413
414
415
/*!
416
 * \brief   fpixDisplayMaxDynamicRange()
417
 *
418
 * \param[in]    fpixs
419
 * \return  pixd 8 bpp, or NULL on error
420
 */
421
PIX *
422
fpixDisplayMaxDynamicRange(FPIX  *fpixs)
423
0
{
424
0
l_uint8     dval;
425
0
l_int32     i, j, w, h, wpls, wpld;
426
0
l_float32   factor, sval, maxval;
427
0
l_float32  *lines, *datas;
428
0
l_uint32   *lined, *datad;
429
0
PIX        *pixd;
430
431
0
    if (!fpixs)
432
0
        return (PIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
433
434
0
    fpixGetDimensions(fpixs, &w, &h);
435
0
    datas = fpixGetData(fpixs);
436
0
    wpls = fpixGetWpl(fpixs);
437
438
0
    maxval = 0.0;
439
0
    for (i = 0; i < h; i++) {
440
0
        lines = datas + i * wpls;
441
0
        for (j = 0; j < w; j++) {
442
0
            sval = *(lines + j);
443
0
            if (sval > maxval)
444
0
                maxval = sval;
445
0
        }
446
0
    }
447
448
0
    pixd = pixCreate(w, h, 8);
449
0
    if (maxval == 0.0)
450
0
        return pixd;  /* all pixels are 0 */
451
452
0
    datad = pixGetData(pixd);
453
0
    wpld = pixGetWpl(pixd);
454
0
    factor = 255. / maxval;
455
0
    for (i = 0; i < h; i++) {
456
0
        lines = datas + i * wpls;
457
0
        lined = datad + i * wpld;
458
0
        for (j = 0; j < w; j++) {
459
0
            sval = *(lines + j);
460
0
            if (sval < 0.0) sval = 0.0;
461
0
            dval = (l_uint8)(factor * sval + 0.5);
462
0
            SET_DATA_BYTE(lined, j, dval);
463
0
        }
464
0
    }
465
466
0
    return pixd;
467
0
}
468
469
470
/*!
471
 * \brief   fpixConvertToDPix()
472
 *
473
 * \param[in]    fpix
474
 * \return  dpix, or NULL on error
475
 */
476
DPIX *
477
fpixConvertToDPix(FPIX  *fpix)
478
0
{
479
0
l_int32     w, h, i, j, wpls, wpld;
480
0
l_float32   val;
481
0
l_float32  *datas, *lines;
482
0
l_float64  *datad, *lined;
483
0
DPIX       *dpix;
484
485
0
    if (!fpix)
486
0
        return (DPIX *)ERROR_PTR("fpix not defined", __func__, NULL);
487
488
0
    fpixGetDimensions(fpix, &w, &h);
489
0
    if ((dpix = dpixCreate(w, h)) == NULL)
490
0
        return (DPIX *)ERROR_PTR("dpix not made", __func__, NULL);
491
492
0
    datas = fpixGetData(fpix);
493
0
    datad = dpixGetData(dpix);
494
0
    wpls = fpixGetWpl(fpix);
495
0
    wpld = dpixGetWpl(dpix);  /* 8 byte words */
496
0
    for (i = 0; i < h; i++) {
497
0
        lines = datas + i * wpls;
498
0
        lined = datad + i * wpld;
499
0
        for (j = 0; j < w; j++) {
500
0
            val = lines[j];
501
0
            lined[j] = val;
502
0
        }
503
0
    }
504
505
0
    return dpix;
506
0
}
507
508
509
/*!
510
 * \brief   dpixConvertToPix()
511
 *
512
 * \param[in]    dpixs
513
 * \param[in]    outdepth     0, 8, 16 or 32 bpp
514
 * \param[in]    negvals      L_CLIP_TO_ZERO, L_TAKE_ABSVAL
515
 * \param[in]    errorflag    1 to output error stats; 0 otherwise
516
 * \return  pixd, or NULL on error
517
 *
518
 * <pre>
519
 * Notes:
520
 *      (1) Use %outdepth = 0 to programmatically determine the
521
 *          output depth.  If no values are greater than 255,
522
 *          it will set outdepth = 8; otherwise to 16 or 32.
523
 *      (2) Because we are converting a float to an unsigned int
524
 *          with a specified dynamic range (8, 16 or 32 bits), errors
525
 *          can occur.  If errorflag == TRUE, output the number
526
 *          of values out of range, both negative and positive.
527
 *      (3) If a pixel value is positive and out of range, clip to
528
 *          the maximum value represented at the outdepth of 8, 16
529
 *          or 32 bits.
530
 * </pre>
531
 */
532
PIX *
533
dpixConvertToPix(DPIX    *dpixs,
534
                 l_int32  outdepth,
535
                 l_int32  negvals,
536
                 l_int32  errorflag)
537
0
{
538
0
l_int32     w, h, i, j, wpls, wpld, maxval;
539
0
l_uint32    vald;
540
0
l_float64   val;
541
0
l_float64  *datas, *lines;
542
0
l_uint32   *datad, *lined;
543
0
PIX        *pixd;
544
545
0
    if (!dpixs)
546
0
        return (PIX *)ERROR_PTR("dpixs not defined", __func__, NULL);
547
0
    if (negvals != L_CLIP_TO_ZERO && negvals != L_TAKE_ABSVAL)
548
0
        return (PIX *)ERROR_PTR("invalid negvals", __func__, NULL);
549
0
    if (outdepth != 0 && outdepth != 8 && outdepth != 16 && outdepth != 32)
550
0
        return (PIX *)ERROR_PTR("outdepth not in {0,8,16,32}", __func__, NULL);
551
552
0
    dpixGetDimensions(dpixs, &w, &h);
553
0
    datas = dpixGetData(dpixs);
554
0
    wpls = dpixGetWpl(dpixs);
555
556
        /* Adaptive determination of output depth */
557
0
    if (outdepth == 0) {
558
0
        outdepth = 8;
559
0
        for (i = 0; i < h && outdepth < 32; i++) {
560
0
            lines = datas + i * wpls;
561
0
            for (j = 0; j < w && outdepth < 32; j++) {
562
0
                if (lines[j] > 65535.5)
563
0
                    outdepth = 32;
564
0
                else if (lines[j] > 255.5)
565
0
                    outdepth = 16;
566
0
            }
567
0
        }
568
0
    }
569
0
    maxval = 0xff;
570
0
    if (outdepth == 16)
571
0
        maxval = 0xffff;
572
0
    else  /* outdepth == 32 */
573
0
        maxval = 0xffffffff;
574
575
        /* Gather statistics if %errorflag = TRUE */
576
0
    if (errorflag) {
577
0
        l_int32  negs = 0;
578
0
        l_int32  overvals = 0;
579
0
        for (i = 0; i < h; i++) {
580
0
            lines = datas + i * wpls;
581
0
            for (j = 0; j < w; j++) {
582
0
                val = lines[j];
583
0
                if (val < 0.0)
584
0
                    negs++;
585
0
                else if (val > maxval)
586
0
                    overvals++;
587
0
            }
588
0
        }
589
0
        if (negs > 0)
590
0
            L_ERROR("Number of negative values: %d\n", __func__, negs);
591
0
        if (overvals > 0)
592
0
            L_ERROR("Number of too-large values: %d\n", __func__, overvals);
593
0
    }
594
595
        /* Make the pix and convert the data */
596
0
    if ((pixd = pixCreate(w, h, outdepth)) == NULL)
597
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
598
0
    datad = pixGetData(pixd);
599
0
    wpld = pixGetWpl(pixd);
600
0
    for (i = 0; i < h; i++) {
601
0
        lines = datas + i * wpls;
602
0
        lined = datad + i * wpld;
603
0
        for (j = 0; j < w; j++) {
604
0
            val = lines[j];
605
0
            if (val >= 0.0) {
606
0
                vald = (l_uint32)(val + 0.5);
607
0
            } else {  /* val < 0.0 */
608
0
                if (negvals == L_CLIP_TO_ZERO)
609
0
                    vald = 0;
610
0
                else
611
0
                    vald = (l_uint32)(-val + 0.5);
612
0
            }
613
0
            if (vald > maxval)
614
0
                vald = maxval;
615
0
            if (outdepth == 8)
616
0
                SET_DATA_BYTE(lined, j, vald);
617
0
            else if (outdepth == 16)
618
0
                SET_DATA_TWO_BYTES(lined, j, vald);
619
0
            else  /* outdepth == 32 */
620
0
                SET_DATA_FOUR_BYTES(lined, j, vald);
621
0
        }
622
0
    }
623
624
0
    return pixd;
625
0
}
626
627
628
/*!
629
 * \brief   dpixConvertToFPix()
630
 *
631
 * \param[in]    dpix
632
 * \return  fpix, or NULL on error
633
 */
634
FPIX *
635
dpixConvertToFPix(DPIX  *dpix)
636
0
{
637
0
l_int32     w, h, i, j, wpls, wpld;
638
0
l_float64   val;
639
0
l_float32  *datad, *lined;
640
0
l_float64  *datas, *lines;
641
0
FPIX       *fpix;
642
643
0
    if (!dpix)
644
0
        return (FPIX *)ERROR_PTR("dpix not defined", __func__, NULL);
645
646
0
    dpixGetDimensions(dpix, &w, &h);
647
0
    if ((fpix = fpixCreate(w, h)) == NULL)
648
0
        return (FPIX *)ERROR_PTR("fpix not made", __func__, NULL);
649
650
0
    datas = dpixGetData(dpix);
651
0
    datad = fpixGetData(fpix);
652
0
    wpls = dpixGetWpl(dpix);  /* 8 byte words */
653
0
    wpld = fpixGetWpl(fpix);
654
0
    for (i = 0; i < h; i++) {
655
0
        lines = datas + i * wpls;
656
0
        lined = datad + i * wpld;
657
0
        for (j = 0; j < w; j++) {
658
0
            val = lines[j];
659
0
            lined[j] = (l_float32)val;
660
0
        }
661
0
    }
662
663
0
    return fpix;
664
0
}
665
666
667
668
/*--------------------------------------------------------------------*
669
 *                           Min/max value                            *
670
 *--------------------------------------------------------------------*/
671
/*!
672
 * \brief   fpixGetMin()
673
 *
674
 * \param[in]    fpix
675
 * \param[out]   pminval    [optional] min value
676
 * \param[out]   pxminloc   [optional] x location of min
677
 * \param[out]   pyminloc   [optional] y location of min
678
 * \return  0 if OK; 1 on error
679
 */
680
l_ok
681
fpixGetMin(FPIX       *fpix,
682
           l_float32  *pminval,
683
           l_int32    *pxminloc,
684
           l_int32    *pyminloc)
685
0
{
686
0
l_int32     i, j, w, h, wpl, xminloc, yminloc;
687
0
l_float32  *data, *line;
688
0
l_float32   minval;
689
690
0
    if (!pminval && !pxminloc && !pyminloc)
691
0
        return ERROR_INT("no return val requested", __func__, 1);
692
0
    if (pminval) *pminval = 0.0;
693
0
    if (pxminloc) *pxminloc = 0;
694
0
    if (pyminloc) *pyminloc = 0;
695
0
    if (!fpix)
696
0
        return ERROR_INT("fpix not defined", __func__, 1);
697
698
0
    minval = +1.0e20f;
699
0
    xminloc = 0;
700
0
    yminloc = 0;
701
0
    fpixGetDimensions(fpix, &w, &h);
702
0
    data = fpixGetData(fpix);
703
0
    wpl = fpixGetWpl(fpix);
704
0
    for (i = 0; i < h; i++) {
705
0
        line = data + i * wpl;
706
0
        for (j = 0; j < w; j++) {
707
0
            if (line[j] < minval) {
708
0
                minval = line[j];
709
0
                xminloc = j;
710
0
                yminloc = i;
711
0
            }
712
0
        }
713
0
    }
714
715
0
    if (pminval) *pminval = minval;
716
0
    if (pxminloc) *pxminloc = xminloc;
717
0
    if (pyminloc) *pyminloc = yminloc;
718
0
    return 0;
719
0
}
720
721
722
/*!
723
 * \brief   fpixGetMax()
724
 *
725
 * \param[in]    fpix
726
 * \param[out]   pmaxval    [optional] max value
727
 * \param[out]   pxmaxloc   [optional] x location of max
728
 * \param[out]   pymaxloc   [optional] y location of max
729
 * \return  0 if OK; 1 on error
730
 */
731
l_ok
732
fpixGetMax(FPIX       *fpix,
733
           l_float32  *pmaxval,
734
           l_int32    *pxmaxloc,
735
           l_int32    *pymaxloc)
736
0
{
737
0
l_int32     i, j, w, h, wpl, xmaxloc, ymaxloc;
738
0
l_float32  *data, *line;
739
0
l_float32   maxval;
740
741
0
    if (!pmaxval && !pxmaxloc && !pymaxloc)
742
0
        return ERROR_INT("no return val requested", __func__, 1);
743
0
    if (pmaxval) *pmaxval = 0.0;
744
0
    if (pxmaxloc) *pxmaxloc = 0;
745
0
    if (pymaxloc) *pymaxloc = 0;
746
0
    if (!fpix)
747
0
        return ERROR_INT("fpix not defined", __func__, 1);
748
749
0
    maxval = -1.0e20f;
750
0
    xmaxloc = 0;
751
0
    ymaxloc = 0;
752
0
    fpixGetDimensions(fpix, &w, &h);
753
0
    data = fpixGetData(fpix);
754
0
    wpl = fpixGetWpl(fpix);
755
0
    for (i = 0; i < h; i++) {
756
0
        line = data + i * wpl;
757
0
        for (j = 0; j < w; j++) {
758
0
            if (line[j] > maxval) {
759
0
                maxval = line[j];
760
0
                xmaxloc = j;
761
0
                ymaxloc = i;
762
0
            }
763
0
        }
764
0
    }
765
766
0
    if (pmaxval) *pmaxval = maxval;
767
0
    if (pxmaxloc) *pxmaxloc = xmaxloc;
768
0
    if (pymaxloc) *pymaxloc = ymaxloc;
769
0
    return 0;
770
0
}
771
772
773
/*!
774
 * \brief   dpixGetMin()
775
 *
776
 * \param[in]    dpix
777
 * \param[out]   pminval    [optional] min value
778
 * \param[out]   pxminloc   [optional] x location of min
779
 * \param[out]   pyminloc   [optional] y location of min
780
 * \return  0 if OK; 1 on error
781
 */
782
l_ok
783
dpixGetMin(DPIX       *dpix,
784
           l_float64  *pminval,
785
           l_int32    *pxminloc,
786
           l_int32    *pyminloc)
787
0
{
788
0
l_int32     i, j, w, h, wpl, xminloc, yminloc;
789
0
l_float64  *data, *line;
790
0
l_float64   minval;
791
792
0
    if (!pminval && !pxminloc && !pyminloc)
793
0
        return ERROR_INT("no return val requested", __func__, 1);
794
0
    if (pminval) *pminval = 0.0;
795
0
    if (pxminloc) *pxminloc = 0;
796
0
    if (pyminloc) *pyminloc = 0;
797
0
    if (!dpix)
798
0
        return ERROR_INT("dpix not defined", __func__, 1);
799
800
0
    minval = +1.0e300;
801
0
    xminloc = 0;
802
0
    yminloc = 0;
803
0
    dpixGetDimensions(dpix, &w, &h);
804
0
    data = dpixGetData(dpix);
805
0
    wpl = dpixGetWpl(dpix);
806
0
    for (i = 0; i < h; i++) {
807
0
        line = data + i * wpl;
808
0
        for (j = 0; j < w; j++) {
809
0
            if (line[j] < minval) {
810
0
                minval = line[j];
811
0
                xminloc = j;
812
0
                yminloc = i;
813
0
            }
814
0
        }
815
0
    }
816
817
0
    if (pminval) *pminval = minval;
818
0
    if (pxminloc) *pxminloc = xminloc;
819
0
    if (pyminloc) *pyminloc = yminloc;
820
0
    return 0;
821
0
}
822
823
824
/*!
825
 * \brief   dpixGetMax()
826
 *
827
 * \param[in]    dpix
828
 * \param[out]   pmaxval    [optional] max value
829
 * \param[out]   pxmaxloc   [optional] x location of max
830
 * \param[out]   pymaxloc   [optional] y location of max
831
 * \return  0 if OK; 1 on error
832
 */
833
l_ok
834
dpixGetMax(DPIX       *dpix,
835
           l_float64  *pmaxval,
836
           l_int32    *pxmaxloc,
837
           l_int32    *pymaxloc)
838
0
{
839
0
l_int32     i, j, w, h, wpl, xmaxloc, ymaxloc;
840
0
l_float64  *data, *line;
841
0
l_float64   maxval;
842
843
0
    if (!pmaxval && !pxmaxloc && !pymaxloc)
844
0
        return ERROR_INT("no return val requested", __func__, 1);
845
0
    if (pmaxval) *pmaxval = 0.0;
846
0
    if (pxmaxloc) *pxmaxloc = 0;
847
0
    if (pymaxloc) *pymaxloc = 0;
848
0
    if (!dpix)
849
0
        return ERROR_INT("dpix not defined", __func__, 1);
850
851
0
    maxval = -1.0e20;
852
0
    xmaxloc = 0;
853
0
    ymaxloc = 0;
854
0
    dpixGetDimensions(dpix, &w, &h);
855
0
    data = dpixGetData(dpix);
856
0
    wpl = dpixGetWpl(dpix);
857
0
    for (i = 0; i < h; i++) {
858
0
        line = data + i * wpl;
859
0
        for (j = 0; j < w; j++) {
860
0
            if (line[j] > maxval) {
861
0
                maxval = line[j];
862
0
                xmaxloc = j;
863
0
                ymaxloc = i;
864
0
            }
865
0
        }
866
0
    }
867
868
0
    if (pmaxval) *pmaxval = maxval;
869
0
    if (pxmaxloc) *pxmaxloc = xmaxloc;
870
0
    if (pymaxloc) *pymaxloc = ymaxloc;
871
0
    return 0;
872
0
}
873
874
875
/*--------------------------------------------------------------------*
876
 *                       Special integer scaling                      *
877
 *--------------------------------------------------------------------*/
878
/*!
879
 * \brief   fpixScaleByInteger()
880
 *
881
 * \param[in]    fpixs     typically low resolution
882
 * \param[in]    factor    integer scaling factor
883
 * \return  fpixd interpolated result, or NULL on error
884
 *
885
 * <pre>
886
 * Notes:
887
 *      (1) The width wd of fpixd is related to ws of fpixs by:
888
 *              wd = factor * (ws - 1) + 1   (and ditto for the height)
889
 *          We avoid special-casing boundary pixels in the interpolation
890
 *          by constructing fpixd by inserting (factor - 1) interpolated
891
 *          pixels between each pixel in fpixs.  Then
892
 *               wd = ws + (ws - 1) * (factor - 1)    (same as above)
893
 *          This also has the advantage that if we subsample by %factor,
894
 *          throwing out all the interpolated pixels, we regain the
895
 *          original low resolution fpix.
896
 * </pre>
897
 */
898
FPIX *
899
fpixScaleByInteger(FPIX    *fpixs,
900
                   l_int32  factor)
901
0
{
902
0
l_int32     i, j, k, m, ws, hs, wd, hd, wpls, wpld;
903
0
l_float32   val0, val1, val2, val3;
904
0
l_float32  *datas, *datad, *lines, *lined, *fract;
905
0
FPIX       *fpixd;
906
907
0
    if (!fpixs)
908
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
909
910
0
    fpixGetDimensions(fpixs, &ws, &hs);
911
0
    wd = factor * (ws - 1) + 1;
912
0
    hd = factor * (hs - 1) + 1;
913
0
    fpixd = fpixCreate(wd, hd);
914
0
    datas = fpixGetData(fpixs);
915
0
    datad = fpixGetData(fpixd);
916
0
    wpls = fpixGetWpl(fpixs);
917
0
    wpld = fpixGetWpl(fpixd);
918
0
    fract = (l_float32 *)LEPT_CALLOC(factor, sizeof(l_float32));
919
0
    for (i = 0; i < factor; i++)
920
0
        fract[i] = i / (l_float32)factor;
921
0
    for (i = 0; i < hs - 1; i++) {
922
0
        lines = datas + i * wpls;
923
0
        for (j = 0; j < ws - 1; j++) {
924
0
            val0 = lines[j];
925
0
            val1 = lines[j + 1];
926
0
            val2 = lines[wpls + j];
927
0
            val3 = lines[wpls + j + 1];
928
0
            for (k = 0; k < factor; k++) {  /* rows of sub-block */
929
0
                lined = datad + (i * factor + k) * wpld;
930
0
                for (m = 0; m < factor; m++) {  /* cols of sub-block */
931
0
                     lined[j * factor + m] =
932
0
                            val0 * (1.0 - fract[m]) * (1.0 - fract[k]) +
933
0
                            val1 * fract[m] * (1.0 - fract[k]) +
934
0
                            val2 * (1.0 - fract[m]) * fract[k] +
935
0
                            val3 * fract[m] * fract[k];
936
0
                }
937
0
            }
938
0
        }
939
0
    }
940
941
        /* Do the right-most column of fpixd, skipping LR corner */
942
0
    for (i = 0; i < hs - 1; i++) {
943
0
        lines = datas + i * wpls;
944
0
        val0 = lines[ws - 1];
945
0
        val1 = lines[wpls + ws - 1];
946
0
        for (k = 0; k < factor; k++) {
947
0
            lined = datad + (i * factor + k) * wpld;
948
0
            lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k];
949
0
        }
950
0
    }
951
952
        /* Do the bottom-most row of fpixd */
953
0
    lines = datas + (hs - 1) * wpls;
954
0
    lined = datad + (hd - 1) * wpld;
955
0
    for (j = 0; j < ws - 1; j++) {
956
0
        val0 = lines[j];
957
0
        val1 = lines[j + 1];
958
0
        for (m = 0; m < factor; m++)
959
0
            lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m];
960
0
        lined[wd - 1] = lines[ws - 1];  /* LR corner */
961
0
    }
962
963
0
    LEPT_FREE(fract);
964
0
    return fpixd;
965
0
}
966
967
968
/*!
969
 * \brief   dpixScaleByInteger()
970
 *
971
 * \param[in]    dpixs     typically low resolution
972
 * \param[in]    factor    integer scaling factor
973
 * \return  dpixd interpolated result, or NULL on error
974
 *
975
 * <pre>
976
 * Notes:
977
 *      (1) The width wd of dpixd is related to ws of dpixs by:
978
 *              wd = factor * (ws - 1) + 1   (and ditto for the height)
979
 *          We avoid special-casing boundary pixels in the interpolation
980
 *          by constructing fpixd by inserting (factor - 1) interpolated
981
 *          pixels between each pixel in fpixs.  Then
982
 *               wd = ws + (ws - 1) * (factor - 1)    (same as above)
983
 *          This also has the advantage that if we subsample by %factor,
984
 *          throwing out all the interpolated pixels, we regain the
985
 *          original low resolution dpix.
986
 * </pre>
987
 */
988
DPIX *
989
dpixScaleByInteger(DPIX    *dpixs,
990
                   l_int32  factor)
991
0
{
992
0
l_int32     i, j, k, m, ws, hs, wd, hd, wpls, wpld;
993
0
l_float64   val0, val1, val2, val3;
994
0
l_float64  *datas, *datad, *lines, *lined, *fract;
995
0
DPIX       *dpixd;
996
997
0
    if (!dpixs)
998
0
        return (DPIX *)ERROR_PTR("dpixs not defined", __func__, NULL);
999
1000
0
    dpixGetDimensions(dpixs, &ws, &hs);
1001
0
    wd = factor * (ws - 1) + 1;
1002
0
    hd = factor * (hs - 1) + 1;
1003
0
    dpixd = dpixCreate(wd, hd);
1004
0
    datas = dpixGetData(dpixs);
1005
0
    datad = dpixGetData(dpixd);
1006
0
    wpls = dpixGetWpl(dpixs);
1007
0
    wpld = dpixGetWpl(dpixd);
1008
0
    fract = (l_float64 *)LEPT_CALLOC(factor, sizeof(l_float64));
1009
0
    for (i = 0; i < factor; i++)
1010
0
        fract[i] = i / (l_float64)factor;
1011
0
    for (i = 0; i < hs - 1; i++) {
1012
0
        lines = datas + i * wpls;
1013
0
        for (j = 0; j < ws - 1; j++) {
1014
0
            val0 = lines[j];
1015
0
            val1 = lines[j + 1];
1016
0
            val2 = lines[wpls + j];
1017
0
            val3 = lines[wpls + j + 1];
1018
0
            for (k = 0; k < factor; k++) {  /* rows of sub-block */
1019
0
                lined = datad + (i * factor + k) * wpld;
1020
0
                for (m = 0; m < factor; m++) {  /* cols of sub-block */
1021
0
                     lined[j * factor + m] =
1022
0
                            val0 * (1.0 - fract[m]) * (1.0 - fract[k]) +
1023
0
                            val1 * fract[m] * (1.0 - fract[k]) +
1024
0
                            val2 * (1.0 - fract[m]) * fract[k] +
1025
0
                            val3 * fract[m] * fract[k];
1026
0
                }
1027
0
            }
1028
0
        }
1029
0
    }
1030
1031
        /* Do the right-most column of dpixd, skipping LR corner */
1032
0
    for (i = 0; i < hs - 1; i++) {
1033
0
        lines = datas + i * wpls;
1034
0
        val0 = lines[ws - 1];
1035
0
        val1 = lines[wpls + ws - 1];
1036
0
        for (k = 0; k < factor; k++) {
1037
0
            lined = datad + (i * factor + k) * wpld;
1038
0
            lined[wd - 1] = val0 * (1.0 - fract[k]) + val1 * fract[k];
1039
0
        }
1040
0
    }
1041
1042
        /* Do the bottom-most row of dpixd */
1043
0
    lines = datas + (hs - 1) * wpls;
1044
0
    lined = datad + (hd - 1) * wpld;
1045
0
    for (j = 0; j < ws - 1; j++) {
1046
0
        val0 = lines[j];
1047
0
        val1 = lines[j + 1];
1048
0
        for (m = 0; m < factor; m++)
1049
0
            lined[j * factor + m] = val0 * (1.0 - fract[m]) + val1 * fract[m];
1050
0
        lined[wd - 1] = lines[ws - 1];  /* LR corner */
1051
0
    }
1052
1053
0
    LEPT_FREE(fract);
1054
0
    return dpixd;
1055
0
}
1056
1057
1058
/*--------------------------------------------------------------------*
1059
 *                        Arithmetic operations                       *
1060
 *--------------------------------------------------------------------*/
1061
/*!
1062
 * \brief   fpixLinearCombination()
1063
 *
1064
 * \param[in]    fpixd    [optional] this can be null, or equal to fpixs1
1065
 * \param[in]    fpixs1   can be equal to fpixd
1066
 * \param[in]    fpixs2
1067
 * \param[in]    a, b     multiplication factors on fpixs1 and fpixs2, rsp.
1068
 * \return  fpixd always
1069
 *
1070
 * <pre>
1071
 * Notes:
1072
 *      (1) Computes pixelwise linear combination: a * src1 + b * src2
1073
 *      (2) Alignment is to UL corner; src1 and src2 do not have to be
1074
 *          the same size.
1075
 *      (3) There are 2 cases.  The result can go to a new dest, or
1076
 *          in-place to fpixs1:
1077
 *          * fpixd == null:   (src1 + src2) --> new fpixd
1078
 *          * fpixd == fpixs1:  (src1 + src2) --> src1  (in-place)
1079
 * </pre>
1080
 */
1081
FPIX *
1082
fpixLinearCombination(FPIX      *fpixd,
1083
                      FPIX      *fpixs1,
1084
                      FPIX      *fpixs2,
1085
                      l_float32  a,
1086
                      l_float32  b)
1087
0
{
1088
0
l_int32     i, j, ws, hs, w, h, wpls, wpld;
1089
0
l_float32  *datas, *datad, *lines, *lined;
1090
1091
0
    if (!fpixs1)
1092
0
        return (FPIX *)ERROR_PTR("fpixs1 not defined", __func__, fpixd);
1093
0
    if (!fpixs2)
1094
0
        return (FPIX *)ERROR_PTR("fpixs2 not defined", __func__, fpixd);
1095
0
    if (fpixd && (fpixd != fpixs1))
1096
0
        return (FPIX *)ERROR_PTR("invalid inplace operation", __func__, fpixd);
1097
1098
0
    if (!fpixd)
1099
0
        fpixd = fpixCopy(fpixs1);
1100
0
    datas = fpixGetData(fpixs2);
1101
0
    datad = fpixGetData(fpixd);
1102
0
    wpls = fpixGetWpl(fpixs2);
1103
0
    wpld = fpixGetWpl(fpixd);
1104
0
    fpixGetDimensions(fpixs2, &ws, &hs);
1105
0
    fpixGetDimensions(fpixd, &w, &h);
1106
0
    w = L_MIN(ws, w);
1107
0
    h = L_MIN(hs, h);
1108
0
    for (i = 0; i < h; i++) {
1109
0
        lines = datas + i * wpls;
1110
0
        lined = datad + i * wpld;
1111
0
        for (j = 0; j < w; j++)
1112
0
            lined[j] = a * lined[j] + b * lines[j];
1113
0
    }
1114
1115
0
    return fpixd;
1116
0
}
1117
1118
1119
/*!
1120
 * \brief   fpixAddMultConstant()
1121
 *
1122
 * \param[in]    fpix
1123
 * \param[in]    addc     use 0.0 to skip the operation
1124
 * \param[in]    multc    use 1.0 to skip the operation
1125
 * \return  0 if OK, 1 on error
1126
 *
1127
 * <pre>
1128
 * Notes:
1129
 *      (1) This is an in-place operation.
1130
 *      (2) It can be used to multiply each pixel by a constant,
1131
 *          and also to add a constant to each pixel.  Multiplication
1132
 *          is done first.
1133
 * </pre>
1134
 */
1135
l_ok
1136
fpixAddMultConstant(FPIX      *fpix,
1137
                    l_float32  addc,
1138
                    l_float32  multc)
1139
0
{
1140
0
l_int32     i, j, w, h, wpl;
1141
0
l_float32  *line, *data;
1142
1143
0
    if (!fpix)
1144
0
        return ERROR_INT("fpix not defined", __func__, 1);
1145
1146
0
    if (addc == 0.0 && multc == 1.0)
1147
0
        return 0;
1148
1149
0
    fpixGetDimensions(fpix, &w, &h);
1150
0
    data = fpixGetData(fpix);
1151
0
    wpl = fpixGetWpl(fpix);
1152
0
    for (i = 0; i < h; i++) {
1153
0
        line = data + i * wpl;
1154
0
        if (addc == 0.0) {
1155
0
            for (j = 0; j < w; j++)
1156
0
                line[j] *= multc;
1157
0
        } else if (multc == 1.0) {
1158
0
            for (j = 0; j < w; j++)
1159
0
                line[j] += addc;
1160
0
        } else {
1161
0
            for (j = 0; j < w; j++) {
1162
0
                line[j] = multc * line[j] + addc;
1163
0
            }
1164
0
        }
1165
0
    }
1166
1167
0
    return 0;
1168
0
}
1169
1170
1171
/*!
1172
 * \brief   dpixLinearCombination()
1173
 *
1174
 * \param[in]    dpixd    [optional] this can be null, or equal to dpixs1
1175
 * \param[in]    dpixs1   can be equal to dpixd
1176
 * \param[in]    dpixs2
1177
 * \param[in]    a, b     multiplication factors on dpixs1 and dpixs2, rsp.
1178
 * \return  dpixd always
1179
 *
1180
 * <pre>
1181
 * Notes:
1182
 *      (1) Computes pixelwise linear combination: a * src1 + b * src2
1183
 *      (2) Alignment is to UL corner; src1 and src2 do not have to be
1184
 *          the same size.
1185
 *      (3) There are 2 cases.  The result can go to a new dest, or
1186
 *          in-place to dpixs1:
1187
 *          * dpixd == null:   (src1 + src2) --> new dpixd
1188
 *          * dpixd == dpixs1:  (src1 + src2) --> src1  (in-place)
1189
 * </pre>
1190
 */
1191
DPIX *
1192
dpixLinearCombination(DPIX      *dpixd,
1193
                      DPIX      *dpixs1,
1194
                      DPIX      *dpixs2,
1195
                      l_float32  a,
1196
                      l_float32  b)
1197
0
{
1198
0
l_int32     i, j, ws, hs, w, h, wpls, wpld;
1199
0
l_float64  *datas, *datad, *lines, *lined;
1200
1201
0
    if (!dpixs1)
1202
0
        return (DPIX *)ERROR_PTR("dpixs1 not defined", __func__, dpixd);
1203
0
    if (!dpixs2)
1204
0
        return (DPIX *)ERROR_PTR("dpixs2 not defined", __func__, dpixd);
1205
0
    if (dpixd && (dpixd != dpixs1))
1206
0
        return (DPIX *)ERROR_PTR("invalid inplace operation", __func__, dpixd);
1207
1208
0
    if (!dpixd)
1209
0
        dpixd = dpixCopy(dpixs1);
1210
0
    datas = dpixGetData(dpixs2);
1211
0
    datad = dpixGetData(dpixd);
1212
0
    wpls = dpixGetWpl(dpixs2);
1213
0
    wpld = dpixGetWpl(dpixd);
1214
0
    dpixGetDimensions(dpixs2, &ws, &hs);
1215
0
    dpixGetDimensions(dpixd, &w, &h);
1216
0
    w = L_MIN(ws, w);
1217
0
    h = L_MIN(hs, h);
1218
0
    for (i = 0; i < h; i++) {
1219
0
        lines = datas + i * wpls;
1220
0
        lined = datad + i * wpld;
1221
0
        for (j = 0; j < w; j++)
1222
0
            lined[j] = a * lined[j] + b * lines[j];
1223
0
    }
1224
1225
0
    return dpixd;
1226
0
}
1227
1228
1229
/*!
1230
 * \brief   dpixAddMultConstant()
1231
 *
1232
 * \param[in]    dpix
1233
 * \param[in]    addc     use 0.0 to skip the operation
1234
 * \param[in]    multc    use 1.0 to skip the operation
1235
 * \return  0 if OK, 1 on error
1236
 *
1237
 * <pre>
1238
 * Notes:
1239
 *      (1) This is an in-place operation.
1240
 *      (2) It can be used to multiply each pixel by a constant,
1241
 *          and also to add a constant to each pixel.  Multiplication
1242
 *          is done first.
1243
 * </pre>
1244
 */
1245
l_ok
1246
dpixAddMultConstant(DPIX      *dpix,
1247
                    l_float64  addc,
1248
                    l_float64  multc)
1249
0
{
1250
0
l_int32     i, j, w, h, wpl;
1251
0
l_float64  *line, *data;
1252
1253
0
    if (!dpix)
1254
0
        return ERROR_INT("dpix not defined", __func__, 1);
1255
1256
0
    if (addc == 0.0 && multc == 1.0)
1257
0
        return 0;
1258
1259
0
    dpixGetDimensions(dpix, &w, &h);
1260
0
    data = dpixGetData(dpix);
1261
0
    wpl = dpixGetWpl(dpix);
1262
0
    for (i = 0; i < h; i++) {
1263
0
        line = data + i * wpl;
1264
0
        if (addc == 0.0) {
1265
0
            for (j = 0; j < w; j++)
1266
0
                line[j] *= multc;
1267
0
        } else if (multc == 1.0) {
1268
0
            for (j = 0; j < w; j++)
1269
0
                line[j] += addc;
1270
0
        } else {
1271
0
            for (j = 0; j < w; j++)
1272
0
                line[j] = multc * line[j] + addc;
1273
0
        }
1274
0
    }
1275
1276
0
    return 0;
1277
0
}
1278
1279
1280
/*--------------------------------------------------------------------*
1281
 *                              Set all                               *
1282
 *--------------------------------------------------------------------*/
1283
/*!
1284
 * \brief   fpixSetAllArbitrary()
1285
 *
1286
 * \param[in]    fpix
1287
 * \param[in]    inval    to set at each pixel
1288
 * \return  0 if OK, 1 on error
1289
 */
1290
l_ok
1291
fpixSetAllArbitrary(FPIX      *fpix,
1292
                    l_float32  inval)
1293
0
{
1294
0
l_int32     i, j, w, h;
1295
0
l_float32  *data, *line;
1296
1297
0
    if (!fpix)
1298
0
        return ERROR_INT("fpix not defined", __func__, 1);
1299
1300
0
    fpixGetDimensions(fpix, &w, &h);
1301
0
    data = fpixGetData(fpix);
1302
0
    for (i = 0; i < h; i++) {
1303
0
        line = data + i * w;
1304
0
        for (j = 0; j < w; j++)
1305
0
            *(line + j) = inval;
1306
0
    }
1307
1308
0
    return 0;
1309
0
}
1310
1311
1312
/*!
1313
 * \brief   dpixSetAllArbitrary()
1314
 *
1315
 * \param[in]    dpix
1316
 * \param[in]    inval    to set at each pixel
1317
 * \return  0 if OK, 1 on error
1318
 */
1319
l_ok
1320
dpixSetAllArbitrary(DPIX      *dpix,
1321
                    l_float64  inval)
1322
0
{
1323
0
l_int32     i, j, w, h;
1324
0
l_float64  *data, *line;
1325
1326
0
    if (!dpix)
1327
0
        return ERROR_INT("dpix not defined", __func__, 1);
1328
1329
0
    dpixGetDimensions(dpix, &w, &h);
1330
0
    data = dpixGetData(dpix);
1331
0
    for (i = 0; i < h; i++) {
1332
0
        line = data + i * w;
1333
0
        for (j = 0; j < w; j++)
1334
0
            *(line + j) = inval;
1335
0
    }
1336
1337
0
    return 0;
1338
0
}
1339
1340
1341
/*--------------------------------------------------------------------*
1342
 *                          Border functions                          *
1343
 *--------------------------------------------------------------------*/
1344
/*!
1345
 * \brief   fpixAddBorder()
1346
 *
1347
 * \param[in]    fpixs
1348
 * \param[in]    left, right, top, bot     pixels on each side to be added
1349
 * \return  fpixd, or NULL on error
1350
 *
1351
 * <pre>
1352
 * Notes:
1353
 *      (1) Adds border of '0' 32-bit pixels
1354
 * </pre>
1355
 */
1356
FPIX *
1357
fpixAddBorder(FPIX    *fpixs,
1358
              l_int32  left,
1359
              l_int32  right,
1360
              l_int32  top,
1361
              l_int32  bot)
1362
0
{
1363
0
l_int32  ws, hs, wd, hd;
1364
0
FPIX    *fpixd;
1365
1366
0
    if (!fpixs)
1367
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1368
1369
0
    if (left <= 0 && right <= 0 && top <= 0 && bot <= 0)
1370
0
        return fpixCopy(fpixs);
1371
0
    fpixGetDimensions(fpixs, &ws, &hs);
1372
0
    wd = ws + left + right;
1373
0
    hd = hs + top + bot;
1374
0
    if ((fpixd = fpixCreate(wd, hd)) == NULL)
1375
0
        return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL);
1376
1377
0
    fpixCopyResolution(fpixd, fpixs);
1378
0
    fpixRasterop(fpixd, left, top, ws, hs, fpixs, 0, 0);
1379
0
    return fpixd;
1380
0
}
1381
1382
1383
/*!
1384
 * \brief   fpixRemoveBorder()
1385
 *
1386
 * \param[in]    fpixs
1387
 * \param[in]    left, right, top, bot     pixels on each side to be removed
1388
 * \return  fpixd, or NULL on error
1389
 */
1390
FPIX *
1391
fpixRemoveBorder(FPIX    *fpixs,
1392
                 l_int32  left,
1393
                 l_int32  right,
1394
                 l_int32  top,
1395
                 l_int32  bot)
1396
0
{
1397
0
l_int32  ws, hs, wd, hd;
1398
0
FPIX    *fpixd;
1399
1400
0
    if (!fpixs)
1401
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1402
1403
0
    if (left <= 0 && right <= 0 && top <= 0 && bot <= 0)
1404
0
        return fpixCopy(fpixs);
1405
0
    fpixGetDimensions(fpixs, &ws, &hs);
1406
0
    wd = ws - left - right;
1407
0
    hd = hs - top - bot;
1408
0
    if (wd <= 0 || hd <= 0)
1409
0
        return (FPIX *)ERROR_PTR("width & height not both > 0", __func__, NULL);
1410
0
    if ((fpixd = fpixCreate(wd, hd)) == NULL)
1411
0
        return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL);
1412
1413
0
    fpixCopyResolution(fpixd, fpixs);
1414
0
    fpixRasterop(fpixd, 0, 0, wd, hd, fpixs, left, top);
1415
0
    return fpixd;
1416
0
}
1417
1418
1419
1420
/*!
1421
 * \brief   fpixAddMirroredBorder()
1422
 *
1423
 * \param[in]    fpixs
1424
 * \param[in]    left, right, top, bot      pixels on each side to be added
1425
 * \return  fpixd, or NULL on error
1426
 *
1427
 * <pre>
1428
 * Notes:
1429
 *      (1) See pixAddMirroredBorder() for situations of usage.
1430
 * </pre>
1431
 */
1432
FPIX *
1433
fpixAddMirroredBorder(FPIX    *fpixs,
1434
                      l_int32  left,
1435
                      l_int32  right,
1436
                      l_int32  top,
1437
                      l_int32  bot)
1438
0
{
1439
0
l_int32  i, j, w, h;
1440
0
FPIX    *fpixd;
1441
1442
0
    if (!fpixs)
1443
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1444
1445
0
    fpixd = fpixAddBorder(fpixs, left, right, top, bot);
1446
0
    fpixGetDimensions(fpixs, &w, &h);
1447
0
    for (j = 0; j < left; j++)
1448
0
        fpixRasterop(fpixd, left - 1 - j, top, 1, h,
1449
0
                     fpixd, left + j, top);
1450
0
    for (j = 0; j < right; j++)
1451
0
        fpixRasterop(fpixd, left + w + j, top, 1, h,
1452
0
                     fpixd, left + w - 1 - j, top);
1453
0
    for (i = 0; i < top; i++)
1454
0
        fpixRasterop(fpixd, 0, top - 1 - i, left + w + right, 1,
1455
0
                     fpixd, 0, top + i);
1456
0
    for (i = 0; i < bot; i++)
1457
0
        fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1,
1458
0
                     fpixd, 0, top + h - 1 - i);
1459
1460
0
    return fpixd;
1461
0
}
1462
1463
1464
/*!
1465
 * \brief   fpixAddContinuedBorder()
1466
 *
1467
 * \param[in]    fpixs
1468
 * \param[in]    left, right, top, bot     pixels on each side to be added
1469
 * \return  fpixd, or NULL on error
1470
 *
1471
 * <pre>
1472
 * Notes:
1473
 *      (1) This adds pixels on each side whose values are equal to
1474
 *          the value on the closest boundary pixel.
1475
 * </pre>
1476
 */
1477
FPIX *
1478
fpixAddContinuedBorder(FPIX    *fpixs,
1479
                       l_int32  left,
1480
                       l_int32  right,
1481
                       l_int32  top,
1482
                       l_int32  bot)
1483
0
{
1484
0
l_int32  i, j, w, h;
1485
0
FPIX    *fpixd;
1486
1487
0
    if (!fpixs)
1488
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1489
1490
0
    fpixd = fpixAddBorder(fpixs, left, right, top, bot);
1491
0
    fpixGetDimensions(fpixs, &w, &h);
1492
0
    for (j = 0; j < left; j++)
1493
0
        fpixRasterop(fpixd, j, top, 1, h, fpixd, left, top);
1494
0
    for (j = 0; j < right; j++)
1495
0
        fpixRasterop(fpixd, left + w + j, top, 1, h, fpixd, left + w - 1, top);
1496
0
    for (i = 0; i < top; i++)
1497
0
        fpixRasterop(fpixd, 0, i, left + w + right, 1, fpixd, 0, top);
1498
0
    for (i = 0; i < bot; i++)
1499
0
        fpixRasterop(fpixd, 0, top + h + i, left + w + right, 1,
1500
0
                     fpixd, 0, top + h - 1);
1501
1502
0
    return fpixd;
1503
0
}
1504
1505
1506
/*!
1507
 * \brief   fpixAddSlopeBorder()
1508
 *
1509
 * \param[in]    fpixs
1510
 * \param[in]    left, right, top, bot     pixels on each side to be added
1511
 * \return  fpixd, or NULL on error
1512
 *
1513
 * <pre>
1514
 * Notes:
1515
 *      (1) This adds pixels on each side whose values have a normal
1516
 *          derivative equal to the normal derivative at the boundary
1517
 *          of fpixs.
1518
 * </pre>
1519
 */
1520
FPIX *
1521
fpixAddSlopeBorder(FPIX    *fpixs,
1522
                   l_int32  left,
1523
                   l_int32  right,
1524
                   l_int32  top,
1525
                   l_int32  bot)
1526
0
{
1527
0
l_int32    i, j, w, h, fullw, fullh;
1528
0
l_float32  val1, val2, del;
1529
0
FPIX      *fpixd;
1530
1531
0
    if (!fpixs)
1532
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1533
1534
0
    fpixd = fpixAddBorder(fpixs, left, right, top, bot);
1535
0
    fpixGetDimensions(fpixs, &w, &h);
1536
1537
        /* Left */
1538
0
    for (i = top; i < top + h; i++) {
1539
0
        fpixGetPixel(fpixd, left, i, &val1);
1540
0
        fpixGetPixel(fpixd, left + 1, i, &val2);
1541
0
        del = val1 - val2;
1542
0
        for (j = 0; j < left; j++)
1543
0
            fpixSetPixel(fpixd, j, i, val1 + del * (left - j));
1544
0
    }
1545
1546
        /* Right */
1547
0
    fullw = left + w + right;
1548
0
    for (i = top; i < top + h; i++) {
1549
0
        fpixGetPixel(fpixd, left + w - 1, i, &val1);
1550
0
        fpixGetPixel(fpixd, left + w - 2, i, &val2);
1551
0
        del = val1 - val2;
1552
0
        for (j = left + w; j < fullw; j++)
1553
0
            fpixSetPixel(fpixd, j, i, val1 + del * (j - left - w + 1));
1554
0
    }
1555
1556
        /* Top */
1557
0
    for (j = 0; j < fullw; j++) {
1558
0
        fpixGetPixel(fpixd, j, top, &val1);
1559
0
        fpixGetPixel(fpixd, j, top + 1, &val2);
1560
0
        del = val1 - val2;
1561
0
        for (i = 0; i < top; i++)
1562
0
            fpixSetPixel(fpixd, j, i, val1 + del * (top - i));
1563
0
    }
1564
1565
        /* Bottom */
1566
0
    fullh = top + h + bot;
1567
0
    for (j = 0; j < fullw; j++) {
1568
0
        fpixGetPixel(fpixd, j, top + h - 1, &val1);
1569
0
        fpixGetPixel(fpixd, j, top + h - 2, &val2);
1570
0
        del = val1 - val2;
1571
0
        for (i = top + h; i < fullh; i++)
1572
0
            fpixSetPixel(fpixd, j, i, val1 + del * (i - top - h + 1));
1573
0
    }
1574
1575
0
    return fpixd;
1576
0
}
1577
1578
1579
/*--------------------------------------------------------------------*
1580
 *                          Simple rasterop                           *
1581
 *--------------------------------------------------------------------*/
1582
/*!
1583
 * \brief   fpixRasterop()
1584
 *
1585
 * \param[in]    fpixd    dest fpix
1586
 * \param[in]    dx       x val of UL corner of dest rectangle
1587
 * \param[in]    dy       y val of UL corner of dest rectangle
1588
 * \param[in]    dw       width of dest rectangle
1589
 * \param[in]    dh       height of dest rectangle
1590
 * \param[in]    fpixs    src fpix
1591
 * \param[in]    sx       x val of UL corner of src rectangle
1592
 * \param[in]    sy       y val of UL corner of src rectangle
1593
 * \return  0 if OK; 1 on error.
1594
 *
1595
 * <pre>
1596
 * Notes:
1597
 *      (1) This is similar in structure to pixRasterop(), except
1598
 *          it only allows copying from the source into the destination.
1599
 *          For that reason, no op code is necessary.  Additionally,
1600
 *          all pixels are 32 bit words (float values), which makes
1601
 *          the copy very simple.
1602
 *      (2) Clipping of both src and dest fpix are done automatically.
1603
 *      (3) This allows in-place copying, without checking to see if
1604
 *          the result is valid:  use for in-place with caution!
1605
 * </pre>
1606
 */
1607
l_ok
1608
fpixRasterop(FPIX    *fpixd,
1609
             l_int32  dx,
1610
             l_int32  dy,
1611
             l_int32  dw,
1612
             l_int32  dh,
1613
             FPIX    *fpixs,
1614
             l_int32  sx,
1615
             l_int32  sy)
1616
0
{
1617
0
l_int32     fsw, fsh, fdw, fdh, dhangw, shangw, dhangh, shangh;
1618
0
l_int32     i, j, wpls, wpld;
1619
0
l_float32  *datas, *datad, *lines, *lined;
1620
1621
0
    if (!fpixs)
1622
0
        return ERROR_INT("fpixs not defined", __func__, 1);
1623
0
    if (!fpixd)
1624
0
        return ERROR_INT("fpixd not defined", __func__, 1);
1625
1626
    /* -------------------------------------------------------- *
1627
     *      Clip to maximum rectangle with both src and dest    *
1628
     * -------------------------------------------------------- */
1629
0
    fpixGetDimensions(fpixs, &fsw, &fsh);
1630
0
    fpixGetDimensions(fpixd, &fdw, &fdh);
1631
1632
        /* First clip horizontally (sx, dx, dw) */
1633
0
    if (dx < 0) {
1634
0
        sx -= dx;  /* increase sx */
1635
0
        dw += dx;  /* reduce dw */
1636
0
        dx = 0;
1637
0
    }
1638
0
    if (sx < 0) {
1639
0
        dx -= sx;  /* increase dx */
1640
0
        dw += sx;  /* reduce dw */
1641
0
        sx = 0;
1642
0
    }
1643
0
    dhangw = dx + dw - fdw;  /* rect overhang of dest to right */
1644
0
    if (dhangw > 0)
1645
0
        dw -= dhangw;  /* reduce dw */
1646
0
    shangw = sx + dw - fsw;   /* rect overhang of src to right */
1647
0
    if (shangw > 0)
1648
0
        dw -= shangw;  /* reduce dw */
1649
1650
        /* Then clip vertically (sy, dy, dh) */
1651
0
    if (dy < 0) {
1652
0
        sy -= dy;  /* increase sy */
1653
0
        dh += dy;  /* reduce dh */
1654
0
        dy = 0;
1655
0
    }
1656
0
    if (sy < 0) {
1657
0
        dy -= sy;  /* increase dy */
1658
0
        dh += sy;  /* reduce dh */
1659
0
        sy = 0;
1660
0
    }
1661
0
    dhangh = dy + dh - fdh;  /* rect overhang of dest below */
1662
0
    if (dhangh > 0)
1663
0
        dh -= dhangh;  /* reduce dh */
1664
0
    shangh = sy + dh - fsh;  /* rect overhang of src below */
1665
0
    if (shangh > 0)
1666
0
        dh -= shangh;  /* reduce dh */
1667
1668
        /* if clipped entirely, quit */
1669
0
    if ((dw <= 0) || (dh <= 0))
1670
0
        return 0;
1671
1672
    /* -------------------------------------------------------- *
1673
     *                    Copy block of data                    *
1674
     * -------------------------------------------------------- */
1675
0
    datas = fpixGetData(fpixs);
1676
0
    datad = fpixGetData(fpixd);
1677
0
    wpls = fpixGetWpl(fpixs);
1678
0
    wpld = fpixGetWpl(fpixd);
1679
0
    datas += sy * wpls + sx;  /* at UL corner of block */
1680
0
    datad += dy * wpld + dx;  /* at UL corner of block */
1681
0
    for (i = 0; i < dh; i++) {
1682
0
        lines = datas + i * wpls;
1683
0
        lined = datad + i * wpld;
1684
0
        for (j = 0; j < dw; j++) {
1685
0
            *lined = *lines;
1686
0
            lines++;
1687
0
            lined++;
1688
0
        }
1689
0
    }
1690
1691
0
    return 0;
1692
0
}
1693
1694
1695
/*--------------------------------------------------------------------*
1696
 *                   Rotation by multiples of 90 degrees              *
1697
 *--------------------------------------------------------------------*/
1698
/*!
1699
 * \brief   fpixRotateOrth()
1700
 *
1701
 * \param[in]    fpixs
1702
 * \param[in]    quads    0-3; number of 90 degree cw rotations
1703
 * \return  fpixd, or NULL on error
1704
 */
1705
FPIX *
1706
fpixRotateOrth(FPIX     *fpixs,
1707
               l_int32  quads)
1708
0
{
1709
0
    if (!fpixs)
1710
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1711
0
    if (quads < 0 || quads > 3)
1712
0
        return (FPIX *)ERROR_PTR("quads not in {0,1,2,3}", __func__, NULL);
1713
1714
0
    if (quads == 0)
1715
0
        return fpixCopy(fpixs);
1716
0
    else if (quads == 1)
1717
0
        return fpixRotate90(fpixs, 1);
1718
0
    else if (quads == 2)
1719
0
        return fpixRotate180(NULL, fpixs);
1720
0
    else /* quads == 3 */
1721
0
        return fpixRotate90(fpixs, -1);
1722
0
}
1723
1724
1725
/*!
1726
 * \brief   fpixRotate180()
1727
 *
1728
 * \param[in]    fpixd    [optional] can be null, or equal to fpixs
1729
 * \param[in]    fpixs
1730
 * \return  fpixd, or NULL on error
1731
 *
1732
 * <pre>
1733
 * Notes:
1734
 *      (1) This does a 180 rotation of the image about the center,
1735
 *          which is equivalent to a left-right flip about a vertical
1736
 *          line through the image center, followed by a top-bottom
1737
 *          flip about a horizontal line through the image center.
1738
 *      (2) There are 2 cases for input:
1739
 *          (a) fpixd == null (creates a new fpixd)
1740
 *          (b) fpixd == fpixs (in-place operation)
1741
 *      (3) For clarity, use these two patterns:
1742
 *          (a) fpixd = fpixRotate180(NULL, fpixs);
1743
 *          (b) fpixRotate180(fpixs, fpixs);
1744
 * </pre>
1745
 */
1746
FPIX *
1747
fpixRotate180(FPIX  *fpixd,
1748
              FPIX  *fpixs)
1749
0
{
1750
0
    if (!fpixs)
1751
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1752
1753
        /* Prepare pixd for in-place operation */
1754
0
    if (!fpixd)
1755
0
        fpixd = fpixCopy(fpixs);
1756
1757
0
    fpixFlipLR(fpixd, fpixd);
1758
0
    fpixFlipTB(fpixd, fpixd);
1759
0
    return fpixd;
1760
0
}
1761
1762
1763
/*!
1764
 * \brief   fpixRotate90()
1765
 *
1766
 * \param[in]    fpixs
1767
 * \param[in]    direction     1 = clockwise; -1 = counter-clockwise
1768
 * \return  fpixd, or NULL on error
1769
 *
1770
 * <pre>
1771
 * Notes:
1772
 *      (1) This does a 90 degree rotation of the image about the center,
1773
 *          either cw or ccw, returning a new pix.
1774
 *      (2) The direction must be either 1 (cw) or -1 (ccw).
1775
 * </pre>
1776
 */
1777
FPIX *
1778
fpixRotate90(FPIX    *fpixs,
1779
             l_int32  direction)
1780
0
{
1781
0
l_int32     i, j, wd, hd, wpls, wpld;
1782
0
l_float32  *datas, *datad, *lines, *lined;
1783
0
FPIX       *fpixd;
1784
1785
0
    if (!fpixs)
1786
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1787
0
    if (direction != 1 && direction != -1)
1788
0
        return (FPIX *)ERROR_PTR("invalid direction", __func__, NULL);
1789
1790
0
    fpixGetDimensions(fpixs, &hd, &wd);
1791
0
    if ((fpixd = fpixCreate(wd, hd)) == NULL)
1792
0
        return (FPIX *)ERROR_PTR("fpixd not made", __func__, NULL);
1793
0
    fpixCopyResolution(fpixd, fpixs);
1794
1795
0
    datas = fpixGetData(fpixs);
1796
0
    wpls = fpixGetWpl(fpixs);
1797
0
    datad = fpixGetData(fpixd);
1798
0
    wpld = fpixGetWpl(fpixd);
1799
0
    if (direction == 1) {  /* clockwise */
1800
0
        for (i = 0; i < hd; i++) {
1801
0
            lined = datad + i * wpld;
1802
0
            lines = datas + (wd - 1) * wpls;
1803
0
            for (j = 0; j < wd; j++) {
1804
0
                lined[j] = lines[i];
1805
0
                lines -= wpls;
1806
0
            }
1807
0
        }
1808
0
    } else {  /* ccw */
1809
0
        for (i = 0; i < hd; i++) {
1810
0
            lined = datad + i * wpld;
1811
0
            lines = datas;
1812
0
            for (j = 0; j < wd; j++) {
1813
0
                lined[j] = lines[hd - 1 - i];
1814
0
                lines += wpls;
1815
0
            }
1816
0
        }
1817
0
    }
1818
1819
0
    return fpixd;
1820
0
}
1821
1822
1823
/*!
1824
 * \brief   pixFlipLR()
1825
 *
1826
 * \param[in]    fpixd    [optional] can be null, or equal to fpixs
1827
 * \param[in]    fpixs
1828
 * \return  fpixd, or NULL on error
1829
 *
1830
 * <pre>
1831
 * Notes:
1832
 *      (1) This does a left-right flip of the image, which is
1833
 *          equivalent to a rotation out of the plane about a
1834
 *          vertical line through the image center.
1835
 *      (2) There are 2 cases for input:
1836
 *          (a) fpixd == null (creates a new fpixd)
1837
 *          (b) fpixd == fpixs (in-place operation)
1838
 *      (3) For clarity, use these two patterns:
1839
 *          (a) fpixd = fpixFlipLR(NULL, fpixs);
1840
 *          (b) fpixFlipLR(fpixs, fpixs);
1841
 * </pre>
1842
 */
1843
FPIX *
1844
fpixFlipLR(FPIX  *fpixd,
1845
           FPIX  *fpixs)
1846
0
{
1847
0
l_int32     i, j, w, h, wpl, bpl;
1848
0
l_float32  *line, *data, *buffer;
1849
1850
0
    if (!fpixs)
1851
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1852
1853
        /* Prepare fpixd for in-place operation */
1854
0
    if (!fpixd)
1855
0
        fpixd = fpixCopy(fpixs);
1856
1857
0
    fpixGetDimensions(fpixd, &w, &h);
1858
0
    data = fpixGetData(fpixd);
1859
0
    wpl = fpixGetWpl(fpixd);  /* 4-byte words */
1860
0
    bpl = 4 * wpl;
1861
0
    buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32));
1862
0
    for (i = 0; i < h; i++) {
1863
0
        line = data + i * wpl;
1864
0
        memcpy(buffer, line, bpl);
1865
0
        for (j = 0; j < w; j++)
1866
0
            line[j] = buffer[w - 1 - j];
1867
0
    }
1868
0
    LEPT_FREE(buffer);
1869
0
    return fpixd;
1870
0
}
1871
1872
1873
/*!
1874
 * \brief   fpixFlipTB()
1875
 *
1876
 * \param[in]    fpixd    [optional] can be null, or equal to fpixs
1877
 * \param[in]    fpixs
1878
 * \return  fpixd, or NULL on error
1879
 *
1880
 * <pre>
1881
 * Notes:
1882
 *      (1) This does a top-bottom flip of the image, which is
1883
 *          equivalent to a rotation out of the plane about a
1884
 *          horizontal line through the image center.
1885
 *      (2) There are 2 cases for input:
1886
 *          (a) fpixd == null (creates a new fpixd)
1887
 *          (b) fpixd == fpixs (in-place operation)
1888
 *      (3) For clarity, use these two patterns:
1889
 *          (a) fpixd = fpixFlipTB(NULL, fpixs);
1890
 *          (b) fpixFlipTB(fpixs, fpixs);
1891
 * </pre>
1892
 */
1893
FPIX *
1894
fpixFlipTB(FPIX  *fpixd,
1895
           FPIX  *fpixs)
1896
0
{
1897
0
l_int32     i, k, h, h2, wpl, bpl;
1898
0
l_float32  *linet, *lineb, *data, *buffer;
1899
1900
0
    if (!fpixs)
1901
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1902
1903
        /* Prepare fpixd for in-place operation */
1904
0
    if (!fpixd)
1905
0
        fpixd = fpixCopy(fpixs);
1906
1907
0
    data = fpixGetData(fpixd);
1908
0
    wpl = fpixGetWpl(fpixd);
1909
0
    fpixGetDimensions(fpixd, NULL, &h);
1910
0
    buffer = (l_float32 *)LEPT_CALLOC(wpl, sizeof(l_float32));
1911
0
    h2 = h / 2;
1912
0
    bpl = 4 * wpl;
1913
0
    for (i = 0, k = h - 1; i < h2; i++, k--) {
1914
0
        linet = data + i * wpl;
1915
0
        lineb = data + k * wpl;
1916
0
        memcpy(buffer, linet, bpl);
1917
0
        memcpy(linet, lineb, bpl);
1918
0
        memcpy(lineb, buffer, bpl);
1919
0
    }
1920
0
    LEPT_FREE(buffer);
1921
0
    return fpixd;
1922
0
}
1923
1924
1925
/*--------------------------------------------------------------------*
1926
 *            Affine and projective interpolated transforms           *
1927
 *--------------------------------------------------------------------*/
1928
/*!
1929
 * \brief   fpixAffinePta()
1930
 *
1931
 * \param[in]    fpixs     8 bpp
1932
 * \param[in]    ptad      4 pts of final coordinate space
1933
 * \param[in]    ptas      4 pts of initial coordinate space
1934
 * \param[in]    border    size of extension with constant normal derivative
1935
 * \param[in]    inval     value brought in; typ. 0
1936
 * \return  fpixd, or NULL on error
1937
 *
1938
 * <pre>
1939
 * Notes:
1940
 *      (1) If %border > 0, all four sides are extended by that distance,
1941
 *          and removed after the transformation is finished.  Pixels
1942
 *          that would be brought in to the trimmed result from outside
1943
 *          the extended region are assigned %inval.  The purpose of
1944
 *          extending the image is to avoid such assignments.
1945
 *      (2) On the other hand, you may want to give all pixels that
1946
 *          are brought in from outside fpixs a specific value.  In that
1947
 *          case, set %border == 0.
1948
 * </pre>
1949
 */
1950
FPIX *
1951
fpixAffinePta(FPIX      *fpixs,
1952
              PTA       *ptad,
1953
              PTA       *ptas,
1954
              l_int32    border,
1955
              l_float32  inval)
1956
0
{
1957
0
l_float32  *vc;
1958
0
PTA        *ptas2, *ptad2;
1959
0
FPIX       *fpixs2, *fpixd, *fpixd2;
1960
1961
0
    if (!fpixs)
1962
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
1963
0
    if (!ptas)
1964
0
        return (FPIX *)ERROR_PTR("ptas not defined", __func__, NULL);
1965
0
    if (!ptad)
1966
0
        return (FPIX *)ERROR_PTR("ptad not defined", __func__, NULL);
1967
1968
        /* If a border is to be added, also translate the ptas */
1969
0
    if (border > 0) {
1970
0
        ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0);
1971
0
        ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0);
1972
0
        fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border);
1973
0
    } else {
1974
0
        ptas2 = ptaClone(ptas);
1975
0
        ptad2 = ptaClone(ptad);
1976
0
        fpixs2 = fpixClone(fpixs);
1977
0
    }
1978
1979
        /* Get backwards transform from dest to src, and apply it */
1980
0
    getAffineXformCoeffs(ptad2, ptas2, &vc);
1981
0
    fpixd2 = fpixAffine(fpixs2, vc, inval);
1982
0
    fpixDestroy(&fpixs2);
1983
0
    ptaDestroy(&ptas2);
1984
0
    ptaDestroy(&ptad2);
1985
0
    LEPT_FREE(vc);
1986
1987
0
    if (border == 0)
1988
0
        return fpixd2;
1989
1990
        /* Remove the added border */
1991
0
    fpixd = fpixRemoveBorder(fpixd2, border, border, border, border);
1992
0
    fpixDestroy(&fpixd2);
1993
0
    return fpixd;
1994
0
}
1995
1996
1997
/*!
1998
 * \brief   fpixAffine()
1999
 *
2000
 * \param[in]    fpixs    8 bpp
2001
 * \param[in]    vc       vector of 8 coefficients for projective transformation
2002
 * \param[in]    inval    value brought in; typ. 0
2003
 * \return  fpixd, or NULL on error
2004
 */
2005
FPIX *
2006
fpixAffine(FPIX       *fpixs,
2007
           l_float32  *vc,
2008
           l_float32   inval)
2009
0
{
2010
0
l_int32     i, j, w, h, wpld;
2011
0
l_float32   val;
2012
0
l_float32  *datas, *datad, *lined;
2013
0
l_float32   x, y;
2014
0
FPIX       *fpixd;
2015
2016
0
    if (!fpixs)
2017
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
2018
0
    fpixGetDimensions(fpixs, &w, &h);
2019
0
    if (!vc)
2020
0
        return (FPIX *)ERROR_PTR("vc not defined", __func__, NULL);
2021
2022
0
    datas = fpixGetData(fpixs);
2023
0
    fpixd = fpixCreateTemplate(fpixs);
2024
0
    fpixSetAllArbitrary(fpixd, inval);
2025
0
    datad = fpixGetData(fpixd);
2026
0
    wpld = fpixGetWpl(fpixd);
2027
2028
        /* Iterate over destination pixels */
2029
0
    for (i = 0; i < h; i++) {
2030
0
        lined = datad + i * wpld;
2031
0
        for (j = 0; j < w; j++) {
2032
                /* Compute float src pixel location corresponding to (i,j) */
2033
0
            affineXformPt(vc, j, i, &x, &y);
2034
0
            linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val);
2035
0
            *(lined + j) = val;
2036
0
        }
2037
0
    }
2038
2039
0
    return fpixd;
2040
0
}
2041
2042
2043
/*!
2044
 * \brief   fpixProjectivePta()
2045
 *
2046
 * \param[in]    fpixs     8 bpp
2047
 * \param[in]    ptad      4 pts of final coordinate space
2048
 * \param[in]    ptas      4 pts of initial coordinate space
2049
 * \param[in]    border    size of extension with constant normal derivative
2050
 * \param[in]    inval     value brought in; typ. 0
2051
 * \return  fpixd, or NULL on error
2052
 *
2053
 * <pre>
2054
 * Notes:
2055
 *      (1) If %border > 0, all four sides are extended by that distance,
2056
 *          and removed after the transformation is finished.  Pixels
2057
 *          that would be brought in to the trimmed result from outside
2058
 *          the extended region are assigned %inval.  The purpose of
2059
 *          extending the image is to avoid such assignments.
2060
 *      (2) On the other hand, you may want to give all pixels that
2061
 *          are brought in from outside fpixs a specific value.  In that
2062
 *          case, set %border == 0.
2063
 * </pre>
2064
 */
2065
FPIX *
2066
fpixProjectivePta(FPIX      *fpixs,
2067
                  PTA       *ptad,
2068
                  PTA       *ptas,
2069
                  l_int32    border,
2070
                  l_float32  inval)
2071
0
{
2072
0
l_float32  *vc;
2073
0
PTA        *ptas2, *ptad2;
2074
0
FPIX       *fpixs2, *fpixd, *fpixd2;
2075
2076
0
    if (!fpixs)
2077
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
2078
0
    if (!ptas)
2079
0
        return (FPIX *)ERROR_PTR("ptas not defined", __func__, NULL);
2080
0
    if (!ptad)
2081
0
        return (FPIX *)ERROR_PTR("ptad not defined", __func__, NULL);
2082
2083
        /* If a border is to be added, also translate the ptas */
2084
0
    if (border > 0) {
2085
0
        ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0);
2086
0
        ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0);
2087
0
        fpixs2 = fpixAddSlopeBorder(fpixs, border, border, border, border);
2088
0
    } else {
2089
0
        ptas2 = ptaClone(ptas);
2090
0
        ptad2 = ptaClone(ptad);
2091
0
        fpixs2 = fpixClone(fpixs);
2092
0
    }
2093
2094
        /* Get backwards transform from dest to src, and apply it */
2095
0
    getProjectiveXformCoeffs(ptad2, ptas2, &vc);
2096
0
    fpixd2 = fpixProjective(fpixs2, vc, inval);
2097
0
    fpixDestroy(&fpixs2);
2098
0
    ptaDestroy(&ptas2);
2099
0
    ptaDestroy(&ptad2);
2100
0
    LEPT_FREE(vc);
2101
2102
0
    if (border == 0)
2103
0
        return fpixd2;
2104
2105
        /* Remove the added border */
2106
0
    fpixd = fpixRemoveBorder(fpixd2, border, border, border, border);
2107
0
    fpixDestroy(&fpixd2);
2108
0
    return fpixd;
2109
0
}
2110
2111
2112
/*!
2113
 * \brief   fpixProjective()
2114
 *
2115
 * \param[in]    fpixs     8 bpp
2116
 * \param[in]    vc        vector of 8 coefficients for projective transform
2117
 * \param[in]    inval     value brought in; typ. 0
2118
 * \return  fpixd, or NULL on error
2119
 */
2120
FPIX *
2121
fpixProjective(FPIX       *fpixs,
2122
               l_float32  *vc,
2123
               l_float32   inval)
2124
0
{
2125
0
l_int32     i, j, w, h, wpld;
2126
0
l_float32   val;
2127
0
l_float32  *datas, *datad, *lined;
2128
0
l_float32   x, y;
2129
0
FPIX       *fpixd;
2130
2131
0
    if (!fpixs)
2132
0
        return (FPIX *)ERROR_PTR("fpixs not defined", __func__, NULL);
2133
0
    fpixGetDimensions(fpixs, &w, &h);
2134
0
    if (!vc)
2135
0
        return (FPIX *)ERROR_PTR("vc not defined", __func__, NULL);
2136
2137
0
    datas = fpixGetData(fpixs);
2138
0
    fpixd = fpixCreateTemplate(fpixs);
2139
0
    fpixSetAllArbitrary(fpixd, inval);
2140
0
    datad = fpixGetData(fpixd);
2141
0
    wpld = fpixGetWpl(fpixd);
2142
2143
        /* Iterate over destination pixels */
2144
0
    for (i = 0; i < h; i++) {
2145
0
        lined = datad + i * wpld;
2146
0
        for (j = 0; j < w; j++) {
2147
                /* Compute float src pixel location corresponding to (i,j) */
2148
0
            projectiveXformPt(vc, j, i, &x, &y);
2149
0
            linearInterpolatePixelFloat(datas, w, h, x, y, inval, &val);
2150
0
            *(lined + j) = val;
2151
0
        }
2152
0
    }
2153
2154
0
    return fpixd;
2155
0
}
2156
2157
2158
/*!
2159
 * \brief   linearInterpolatePixelFloat()
2160
 *
2161
 * \param[in]    datas     ptr to beginning of float image data
2162
 * \param[in]    w, h      dimensions of image
2163
 * \param[in]    x, y      floating pt location for evaluation
2164
 * \param[in]    inval     float value brought in from the outside when the
2165
 *                         input x,y location is outside the image
2166
 * \param[out]   pval      interpolated float value
2167
 * \return  0 if OK, 1 on error
2168
 *
2169
 * <pre>
2170
 * Notes:
2171
 *      (1) This is a standard linear interpolation function.  It is
2172
 *          equivalent to area weighting on each component, and
2173
 *          avoids "jaggies" when rendering sharp edges.
2174
 * </pre>
2175
 */
2176
l_ok
2177
linearInterpolatePixelFloat(l_float32  *datas,
2178
                            l_int32     w,
2179
                            l_int32     h,
2180
                            l_float32   x,
2181
                            l_float32   y,
2182
                            l_float32   inval,
2183
                            l_float32  *pval)
2184
0
{
2185
0
l_int32     xpm, ypm, xp, yp, xf, yf;
2186
0
l_float32   v00, v01, v10, v11;
2187
0
l_float32  *lines;
2188
2189
0
    if (!pval)
2190
0
        return ERROR_INT("&val not defined", __func__, 1);
2191
0
    *pval = inval;
2192
0
    if (!datas)
2193
0
        return ERROR_INT("datas not defined", __func__, 1);
2194
2195
        /* Skip if off the edge */
2196
0
    if (x < 0.0 || y < 0.0 || x > w - 2.0 || y > h - 2.0)
2197
0
        return 0;
2198
2199
0
    xpm = (l_int32)(16.0 * x + 0.5);
2200
0
    ypm = (l_int32)(16.0 * y + 0.5);
2201
0
    xp = xpm >> 4;
2202
0
    yp = ypm >> 4;
2203
0
    xf = xpm & 0x0f;
2204
0
    yf = ypm & 0x0f;
2205
2206
#if  DEBUG
2207
    if (xf < 0 || yf < 0)
2208
        lept_stderr("xp = %d, yp = %d, xf = %d, yf = %d\n", xp, yp, xf, yf);
2209
#endif  /* DEBUG */
2210
2211
        /* Interpolate by area weighting. */
2212
0
    lines = datas + yp * w;
2213
0
    v00 = (16.0 - xf) * (16.0 - yf) * (*(lines + xp));
2214
0
    v10 = xf * (16.0 - yf) * (*(lines + xp + 1));
2215
0
    v01 = (16.0 - xf) * yf * (*(lines + w + xp));
2216
0
    v11 = (l_float32)(xf) * yf * (*(lines + w + xp + 1));
2217
0
    *pval = (v00 + v01 + v10 + v11) / 256.0;
2218
0
    return 0;
2219
0
}
2220
2221
2222
/*--------------------------------------------------------------------*
2223
 *                      Thresholding to 1 bpp Pix                     *
2224
 *--------------------------------------------------------------------*/
2225
/*!
2226
 * \brief   fpixThresholdToPix()
2227
 *
2228
 * \param[in]    fpix
2229
 * \param[in]    thresh
2230
 * \return  pixd 1 bpp, or NULL on error
2231
 *
2232
 * <pre>
2233
 * Notes:
2234
 *      (1) For all values of fpix that are <= thresh, sets the pixel
2235
 *          in pixd to 1.
2236
 * </pre>
2237
 */
2238
PIX *
2239
fpixThresholdToPix(FPIX      *fpix,
2240
                   l_float32  thresh)
2241
0
{
2242
0
l_int32     i, j, w, h, wpls, wpld;
2243
0
l_float32  *datas, *lines;
2244
0
l_uint32   *datad, *lined;
2245
0
PIX        *pixd;
2246
2247
0
    if (!fpix)
2248
0
        return (PIX *)ERROR_PTR("fpix not defined", __func__, NULL);
2249
2250
0
    fpixGetDimensions(fpix, &w, &h);
2251
0
    datas = fpixGetData(fpix);
2252
0
    wpls = fpixGetWpl(fpix);
2253
0
    pixd = pixCreate(w, h, 1);
2254
0
    datad = pixGetData(pixd);
2255
0
    wpld = pixGetWpl(pixd);
2256
0
    for (i = 0; i < h; i++) {
2257
0
        lines = datas + i * wpls;
2258
0
        lined = datad + i * wpld;
2259
0
        for (j = 0; j < w; j++) {
2260
0
            if (lines[j] <= thresh)
2261
0
                SET_DATA_BIT(lined, j);
2262
0
        }
2263
0
    }
2264
2265
0
    return pixd;
2266
0
}
2267
2268
2269
/*--------------------------------------------------------------------*
2270
 *                Generate function from components                   *
2271
 *--------------------------------------------------------------------*/
2272
/*!
2273
 * \brief   pixComponentFunction()
2274
 *
2275
 * \param[in]    pix                       32 bpp rgb
2276
 * \param[in]    rnum, gnum, bnum          coefficients for numerator
2277
 * \param[in]    rdenom, gdenom, bdenom    coefficients for denominator
2278
 * \return  fpixd, or NULL on error
2279
 *
2280
 * <pre>
2281
 * Notes:
2282
 *      (1) This stores a function of the component values of each
2283
 *          input pixel in %fpixd.
2284
 *      (2) The function is a ratio of linear combinations of component values.
2285
 *          There are two special cases for denominator coefficients:
2286
 *          (a) The denominator is 1.0: input 0 for all denominator coefficients
2287
 *          (b) Only one component is used in the denominator: input 1.0
2288
 *              for that denominator component and 0.0 for the other two.
2289
 *      (3) If the denominator is 0, multiply by an arbitrary number that
2290
 *          is much larger than 1.  Choose 256 "arbitrarily".
2291
 *
2292
 * </pre>
2293
 */
2294
FPIX *
2295
pixComponentFunction(PIX       *pix,
2296
                     l_float32  rnum,
2297
                     l_float32  gnum,
2298
                     l_float32  bnum,
2299
                     l_float32  rdenom,
2300
                     l_float32  gdenom,
2301
                     l_float32  bdenom)
2302
0
{
2303
0
l_int32     i, j, w, h, wpls, wpld, rval, gval, bval, zerodenom, onedenom;
2304
0
l_float32   fnum, fdenom;
2305
0
l_uint32   *datas, *lines;
2306
0
l_float32  *datad, *lined, *recip;
2307
0
FPIX       *fpixd;
2308
2309
0
    if (!pix || pixGetDepth(pix) != 32)
2310
0
        return (FPIX *)ERROR_PTR("pix undefined or not 32 bpp", __func__, NULL);
2311
2312
0
    pixGetDimensions(pix, &w, &h, NULL);
2313
0
    datas = pixGetData(pix);
2314
0
    wpls = pixGetWpl(pix);
2315
0
    fpixd = fpixCreate(w, h);
2316
0
    datad = fpixGetData(fpixd);
2317
0
    wpld = fpixGetWpl(fpixd);
2318
0
    zerodenom = (rdenom == 0.0 && gdenom == 0.0 && bdenom == 0.0) ? 1: 0;
2319
0
    onedenom = ((rdenom == 1.0 && gdenom == 0.0 && bdenom == 0.0) ||
2320
0
                (rdenom == 0.0 && gdenom == 1.0 && bdenom == 0.0) ||
2321
0
                (rdenom == 0.0 && gdenom == 0.0 && bdenom == 1.0)) ? 1 : 0;
2322
0
    recip = NULL;
2323
0
    if (onedenom) {
2324
0
        recip = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32));
2325
0
        recip[0] = 256;  /* arbitrary large number */
2326
0
        for (i = 1; i < 256; i++)
2327
0
            recip[i] = 1.0 / (l_float32)i;
2328
0
    }
2329
0
    for (i = 0; i < h; i++) {
2330
0
        lines = datas + i * wpls;
2331
0
        lined = datad + i * wpld;
2332
0
        if (zerodenom) {
2333
0
            for (j = 0; j < w; j++) {
2334
0
                extractRGBValues(lines[j], &rval, &gval, &bval);
2335
0
                lined[j] = rnum * rval + gnum * gval + bnum * bval;
2336
0
            }
2337
0
        } else if (onedenom && rdenom == 1.0) {
2338
0
            for (j = 0; j < w; j++) {
2339
0
                extractRGBValues(lines[j], &rval, &gval, &bval);
2340
0
                lined[j]
2341
0
                    = recip[rval] * (rnum * rval + gnum * gval + bnum * bval);
2342
0
            }
2343
0
        } else if (onedenom && gdenom == 1.0) {
2344
0
            for (j = 0; j < w; j++) {
2345
0
                extractRGBValues(lines[j], &rval, &gval, &bval);
2346
0
                lined[j]
2347
0
                    = recip[gval] * (rnum * rval + gnum * gval + bnum * bval);
2348
0
            }
2349
0
        } else if (onedenom && bdenom == 1.0) {
2350
0
            for (j = 0; j < w; j++) {
2351
0
                extractRGBValues(lines[j], &rval, &gval, &bval);
2352
0
                lined[j]
2353
0
                    = recip[bval] * (rnum * rval + gnum * gval + bnum * bval);
2354
0
            }
2355
0
        } else {  /* general case */
2356
0
            for (j = 0; j < w; j++) {
2357
0
                extractRGBValues(lines[j], &rval, &gval, &bval);
2358
0
                fnum = rnum * rval + gnum * gval + bnum * bval;
2359
0
                fdenom = rdenom * rval + gdenom * gval + bdenom * bval;
2360
0
                lined[j] = (fdenom == 0) ? 256.0 * fnum : fnum / fdenom;
2361
0
            }
2362
0
        }
2363
0
    }
2364
2365
0
    LEPT_FREE(recip);
2366
0
    return fpixd;
2367
0
}