Coverage Report

Created: 2024-02-28 06:46

/src/leptonica/src/pixarith.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 pixarith.c
29
 * <pre>
30
 *
31
 *      One-image grayscale arithmetic operations (8, 16, 32 bpp)
32
 *           l_int32     pixAddConstantGray()
33
 *           l_int32     pixMultConstantGray()
34
 *
35
 *      Two-image grayscale arithmetic operations (8, 16, 32 bpp)
36
 *           PIX        *pixAddGray()
37
 *           PIX        *pixSubtractGray()
38
 *           PIX        *pixMultiplyGray()
39
 *
40
 *      Grayscale threshold operation (8, 16, 32 bpp)
41
 *           PIX        *pixThresholdToValue()
42
 *
43
 *      Image accumulator arithmetic operations
44
 *           PIX        *pixInitAccumulate()
45
 *           PIX        *pixFinalAccumulate()
46
 *           PIX        *pixFinalAccumulateThreshold()
47
 *           l_int32     pixAccumulate()
48
 *           l_int32     pixMultConstAccumulate()
49
 *
50
 *      Absolute value of difference
51
 *           PIX        *pixAbsDifference()
52
 *
53
 *      Sum of color images
54
 *           PIX        *pixAddRGB()
55
 *
56
 *      Two-image min and max operations (8 and 16 bpp)
57
 *           PIX        *pixMinOrMax()
58
 *
59
 *      Scale pix for maximum dynamic range
60
 *           PIX        *pixMaxDynamicRange()
61
 *           PIX        *pixMaxDynamicRangeRGB()
62
 *
63
 *      RGB pixel value scaling
64
 *           l_uint32    linearScaleRGBVal()
65
 *           l_uint32    logScaleRGBVal()
66
 *
67
 *      Log base2 lookup
68
 *           l_float32  *makeLogBase2Tab()
69
 *           l_float32   getLogBase2()
70
 *
71
 *      The image accumulator operations are used when you expect
72
 *      overflow from 8 bits on intermediate results.  For example,
73
 *      you might want a tophat contrast operator which is
74
 *         3*I - opening(I,S) - closing(I,S)
75
 *      To use these operations, first use the init to generate
76
 *      a 16 bpp image, use the accumulate to add or subtract 8 bpp
77
 *      images from that, or the multiply constant to multiply
78
 *      by a small constant (much less than 256 -- we don't want
79
 *      overflow from the 16 bit images!), and when you're finished
80
 *      use final to bring the result back to 8 bpp, clipped
81
 *      if necessary.  There is also a divide function, which
82
 *      can be used to divide one image by another, scaling the
83
 *      result for maximum dynamic range, and giving back the
84
 *      8 bpp result.
85
 *
86
 *      A simpler interface to the arithmetic operations is
87
 *      provided in pixacc.c.
88
 * </pre>
89
 */
90
91
#ifdef HAVE_CONFIG_H
92
#include <config_auto.h>
93
#endif  /* HAVE_CONFIG_H */
94
95
#include <string.h>
96
#include <math.h>
97
#include "allheaders.h"
98
99
/*-------------------------------------------------------------*
100
 *          One-image grayscale arithmetic operations          *
101
 *-------------------------------------------------------------*/
102
/*!
103
 * \brief   pixAddConstantGray()
104
 *
105
 * \param[in]    pixs   8, 16 or 32 bpp
106
 * \param[in]    val    amount to add to each pixel
107
 * \return  0 if OK, 1 on error
108
 *
109
 * <pre>
110
 * Notes:
111
 *      (1) In-place operation.
112
 *      (2) No clipping for 32 bpp.
113
 *      (3) For 8 and 16 bpp, if val > 0 the result is clipped
114
 *          to 0xff and 0xffff, rsp.
115
 *      (4) For 8 and 16 bpp, if val < 0 the result is clipped to 0.
116
 * </pre>
117
 */
118
l_ok
119
pixAddConstantGray(PIX      *pixs,
120
                   l_int32   val)
121
0
{
122
0
l_int32    i, j, w, h, d, wpl, pval;
123
0
l_uint32  *data, *line;
124
125
0
    if (!pixs)
126
0
        return ERROR_INT("pixs not defined", __func__, 1);
127
0
    pixGetDimensions(pixs, &w, &h, &d);
128
0
    if (d != 8 && d != 16 && d != 32)
129
0
        return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1);
130
131
0
    data = pixGetData(pixs);
132
0
    wpl = pixGetWpl(pixs);
133
0
    for (i = 0; i < h; i++) {
134
0
        line = data + i * wpl;
135
0
        if (d == 8) {
136
0
            if (val < 0) {
137
0
                for (j = 0; j < w; j++) {
138
0
                    pval = GET_DATA_BYTE(line, j);
139
0
                    pval = L_MAX(0, pval + val);
140
0
                    SET_DATA_BYTE(line, j, pval);
141
0
                }
142
0
            } else {  /* val >= 0 */
143
0
                for (j = 0; j < w; j++) {
144
0
                    pval = GET_DATA_BYTE(line, j);
145
0
                    pval = L_MIN(255, pval + val);
146
0
                    SET_DATA_BYTE(line, j, pval);
147
0
                }
148
0
            }
149
0
        } else if (d == 16) {
150
0
            if (val < 0) {
151
0
                for (j = 0; j < w; j++) {
152
0
                    pval = GET_DATA_TWO_BYTES(line, j);
153
0
                    pval = L_MAX(0, pval + val);
154
0
                    SET_DATA_TWO_BYTES(line, j, pval);
155
0
                }
156
0
            } else {  /* val >= 0 */
157
0
                for (j = 0; j < w; j++) {
158
0
                    pval = GET_DATA_TWO_BYTES(line, j);
159
0
                    pval = L_MIN(0xffff, pval + val);
160
0
                    SET_DATA_TWO_BYTES(line, j, pval);
161
0
                }
162
0
            }
163
0
        } else {  /* d == 32; no check for overflow (< 0 or > 0xffffffff) */
164
0
            for (j = 0; j < w; j++)
165
0
                *(line + j) += val;
166
0
        }
167
0
    }
168
169
0
    return 0;
170
0
}
171
172
173
/*!
174
 * \brief   pixMultConstantGray()
175
 *
176
 * \param[in]    pixs   8, 16 or 32 bpp
177
 * \param[in]    val    >= 0.0; amount to multiply by each pixel
178
 * \return  0 if OK, 1 on error
179
 *
180
 * <pre>
181
 * Notes:
182
 *      (1) In-place operation; val must be >= 0.
183
 *      (2) No clipping for 32 bpp.
184
 *      (3) For 8 and 16 bpp, the result is clipped to 0xff and 0xffff, rsp.
185
 * </pre>
186
 */
187
l_ok
188
pixMultConstantGray(PIX       *pixs,
189
                    l_float32  val)
190
0
{
191
0
l_int32    i, j, w, h, d, wpl, pval;
192
0
l_uint32   upval;
193
0
l_uint32  *data, *line;
194
195
0
    if (!pixs)
196
0
        return ERROR_INT("pixs not defined", __func__, 1);
197
0
    pixGetDimensions(pixs, &w, &h, &d);
198
0
    if (d != 8 && d != 16 && d != 32)
199
0
        return ERROR_INT("pixs not 8, 16 or 32 bpp", __func__, 1);
200
0
    if (val < 0.0)
201
0
        return ERROR_INT("val < 0.0", __func__, 1);
202
203
0
    data = pixGetData(pixs);
204
0
    wpl = pixGetWpl(pixs);
205
0
    for (i = 0; i < h; i++) {
206
0
        line = data + i * wpl;
207
0
        if (d == 8) {
208
0
            for (j = 0; j < w; j++) {
209
0
                pval = GET_DATA_BYTE(line, j);
210
0
                pval = (l_int32)(val * pval);
211
0
                pval = L_MIN(255, pval);
212
0
                SET_DATA_BYTE(line, j, pval);
213
0
            }
214
0
        } else if (d == 16) {
215
0
            for (j = 0; j < w; j++) {
216
0
                pval = GET_DATA_TWO_BYTES(line, j);
217
0
                pval = (l_int32)(val * pval);
218
0
                pval = L_MIN(0xffff, pval);
219
0
                SET_DATA_TWO_BYTES(line, j, pval);
220
0
            }
221
0
        } else {  /* d == 32; no clipping */
222
0
            for (j = 0; j < w; j++) {
223
0
                upval = *(line + j);
224
0
                upval = (l_uint32)(val * upval);
225
0
                *(line + j) = upval;
226
0
            }
227
0
        }
228
0
    }
229
230
0
    return 0;
231
0
}
232
233
234
/*-------------------------------------------------------------*
235
 *             Two-image grayscale arithmetic ops              *
236
 *-------------------------------------------------------------*/
237
/*!
238
 * \brief   pixAddGray()
239
 *
240
 * \param[in]    pixd    [optional]; this can be null, equal to pixs1, or
241
 *                       different from pixs1
242
 * \param[in]    pixs1   can be equal to pixd
243
 * \param[in]    pixs2
244
 * \return  pixd always
245
 *
246
 * <pre>
247
 * Notes:
248
 *      (1) Arithmetic addition of two 8, 16 or 32 bpp images.
249
 *      (2) For 8 and 16 bpp, we do explicit clipping to 0xff and 0xffff,
250
 *          respectively.
251
 *      (3) Alignment is to UL corner.
252
 *      (4) There are 3 cases.  The result can go to a new dest,
253
 *          in-place to pixs1, or to an existing input dest:
254
 *          * pixd == null:   (src1 + src2) --> new pixd
255
 *          * pixd == pixs1:  (src1 + src2) --> src1  (in-place)
256
 *          * pixd != pixs1:  (src1 + src2) --> input pixd
257
 *      (5) pixs2 must be different from both pixd and pixs1.
258
 * </pre>
259
 */
260
PIX *
261
pixAddGray(PIX  *pixd,
262
           PIX  *pixs1,
263
           PIX  *pixs2)
264
0
{
265
0
l_int32    i, j, d, ws, hs, w, h, wpls, wpld, val, sum;
266
0
l_uint32  *datas, *datad, *lines, *lined;
267
268
0
    if (!pixs1)
269
0
        return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
270
0
    if (!pixs2)
271
0
        return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
272
0
    if (pixs2 == pixs1)
273
0
        return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", __func__, pixd);
274
0
    if (pixs2 == pixd)
275
0
        return (PIX *)ERROR_PTR("pixs2 and pixd must differ", __func__, pixd);
276
0
    d = pixGetDepth(pixs1);
277
0
    if (d != 8 && d != 16 && d != 32)
278
0
        return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", __func__, pixd);
279
0
    if (pixGetDepth(pixs2) != d)
280
0
        return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", __func__, pixd);
281
0
    if (pixd && (pixGetDepth(pixd) != d))
282
0
        return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", __func__, pixd);
283
284
0
    if (!pixSizesEqual(pixs1, pixs2))
285
0
        L_WARNING("pixs1 and pixs2 not equal in size\n", __func__);
286
0
    if (pixd && !pixSizesEqual(pixs1, pixd))
287
0
        L_WARNING("pixs1 and pixd not equal in size\n", __func__);
288
289
0
    if (pixs1 != pixd)
290
0
        pixd = pixCopy(pixd, pixs1);
291
292
        /* pixd + pixs2 ==> pixd  */
293
0
    datas = pixGetData(pixs2);
294
0
    datad = pixGetData(pixd);
295
0
    wpls = pixGetWpl(pixs2);
296
0
    wpld = pixGetWpl(pixd);
297
0
    pixGetDimensions(pixs2, &ws, &hs, NULL);
298
0
    pixGetDimensions(pixd, &w, &h, NULL);
299
0
    w = L_MIN(ws, w);
300
0
    h = L_MIN(hs, h);
301
0
    for (i = 0; i < h; i++) {
302
0
        lined = datad + i * wpld;
303
0
        lines = datas + i * wpls;
304
0
        if (d == 8) {
305
0
            for (j = 0; j < w; j++) {
306
0
                sum = GET_DATA_BYTE(lines, j) + GET_DATA_BYTE(lined, j);
307
0
                val = L_MIN(sum, 255);
308
0
                SET_DATA_BYTE(lined, j, val);
309
0
            }
310
0
        } else if (d == 16) {
311
0
            for (j = 0; j < w; j++) {
312
0
                sum = GET_DATA_TWO_BYTES(lines, j)
313
0
                    + GET_DATA_TWO_BYTES(lined, j);
314
0
                val = L_MIN(sum, 0xffff);
315
0
                SET_DATA_TWO_BYTES(lined, j, val);
316
0
            }
317
0
        } else {   /* d == 32; no clipping */
318
0
            for (j = 0; j < w; j++)
319
0
                *(lined + j) += *(lines + j);
320
0
        }
321
0
    }
322
323
0
    return pixd;
324
0
}
325
326
327
/*!
328
 * \brief   pixSubtractGray()
329
 *
330
 * \param[in]    pixd     [optional]; this can be null, equal to pixs1, or
331
 *                        different from pixs1
332
 * \param[in]    pixs1    can be equal to pixd
333
 * \param[in]    pixs2
334
 * \return  pixd always
335
 *
336
 * <pre>
337
 * Notes:
338
 *      (1) Arithmetic subtraction of two 8, 16 or 32 bpp images.
339
 *      (2) Source pixs2 is always subtracted from source pixs1.
340
 *      (3) Do explicit clipping to 0.
341
 *      (4) Alignment is to UL corner.
342
 *      (5) There are 3 cases.  The result can go to a new dest,
343
 *          in-place to pixs1, or to an existing input dest:
344
 *          (a) pixd == null   (src1 - src2) --> new pixd
345
 *          (b) pixd == pixs1  (src1 - src2) --> src1  (in-place)
346
 *          (d) pixd != pixs1  (src1 - src2) --> input pixd
347
 *      (6) pixs2 must be different from both pixd and pixs1.
348
 * </pre>
349
 */
350
PIX *
351
pixSubtractGray(PIX  *pixd,
352
                PIX  *pixs1,
353
                PIX  *pixs2)
354
0
{
355
0
l_int32    i, j, w, h, ws, hs, d, wpls, wpld, val, diff;
356
0
l_uint32  *datas, *datad, *lines, *lined;
357
358
0
    if (!pixs1)
359
0
        return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
360
0
    if (!pixs2)
361
0
        return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
362
0
    if (pixs2 == pixs1)
363
0
        return (PIX *)ERROR_PTR("pixs2 and pixs1 must differ", __func__, pixd);
364
0
    if (pixs2 == pixd)
365
0
        return (PIX *)ERROR_PTR("pixs2 and pixd must differ", __func__, pixd);
366
0
    d = pixGetDepth(pixs1);
367
0
    if (d != 8 && d != 16 && d != 32)
368
0
        return (PIX *)ERROR_PTR("pix are not 8, 16 or 32 bpp", __func__, pixd);
369
0
    if (pixGetDepth(pixs2) != d)
370
0
        return (PIX *)ERROR_PTR("depths differ (pixs1, pixs2)", __func__, pixd);
371
0
    if (pixd && (pixGetDepth(pixd) != d))
372
0
        return (PIX *)ERROR_PTR("depths differ (pixs1, pixd)", __func__, pixd);
373
374
0
    if (!pixSizesEqual(pixs1, pixs2))
375
0
        L_WARNING("pixs1 and pixs2 not equal in size\n", __func__);
376
0
    if (pixd && !pixSizesEqual(pixs1, pixd))
377
0
        L_WARNING("pixs1 and pixd not equal in size\n", __func__);
378
379
0
    if (pixs1 != pixd)
380
0
        pixd = pixCopy(pixd, pixs1);
381
382
        /* pixd - pixs2 ==> pixd  */
383
0
    datas = pixGetData(pixs2);
384
0
    datad = pixGetData(pixd);
385
0
    wpls = pixGetWpl(pixs2);
386
0
    wpld = pixGetWpl(pixd);
387
0
    pixGetDimensions(pixs2, &ws, &hs, NULL);
388
0
    pixGetDimensions(pixd, &w, &h, NULL);
389
0
    w = L_MIN(ws, w);
390
0
    h = L_MIN(hs, h);
391
0
    for (i = 0; i < h; i++) {
392
0
        lined = datad + i * wpld;
393
0
        lines = datas + i * wpls;
394
0
        if (d == 8) {
395
0
            for (j = 0; j < w; j++) {
396
0
                diff = GET_DATA_BYTE(lined, j) - GET_DATA_BYTE(lines, j);
397
0
                val = L_MAX(diff, 0);
398
0
                SET_DATA_BYTE(lined, j, val);
399
0
            }
400
0
        } else if (d == 16) {
401
0
            for (j = 0; j < w; j++) {
402
0
                diff = GET_DATA_TWO_BYTES(lined, j)
403
0
                       - GET_DATA_TWO_BYTES(lines, j);
404
0
                val = L_MAX(diff, 0);
405
0
                SET_DATA_TWO_BYTES(lined, j, val);
406
0
            }
407
0
        } else {  /* d == 32; no clipping */
408
0
            for (j = 0; j < w; j++)
409
0
                *(lined + j) -= *(lines + j);
410
0
        }
411
0
    }
412
413
0
    return pixd;
414
0
}
415
416
417
/*!
418
 * \brief   pixMultiplyGray()
419
 *
420
 * \param[in]    pixs    32 bpp rgb or 8 bpp gray
421
 * \param[in]    pixg    8 bpp gray
422
 * \param[in]    norm    multiplicative factor to avoid overflow; 0 for default
423
 * \return  pixd, or null on error
424
 *
425
 * <pre>
426
 * Notes:
427
 *      (1) This function can be used for correcting a scanned image
428
 *          under non-uniform illumination.  For that application,
429
 *          %pixs is the scanned image, %pixg is an image whose values
430
 *          are inversely related to light from a uniform (say, white)
431
 *          target, and %norm is typically the inverse of the maximum
432
 *          pixel value in %pixg.
433
 *      (2) Set norm = 0 to get the default value, which is the inverse
434
 *          of the max value in %pixg.  This avoids overflow in the product.
435
 *      (3) For 32 bpp %pixs, all 3 components are multiplied by the
436
 *          same number.
437
 *      (4) Alignment is to UL corner.
438
 * </pre>
439
 */
440
PIX *
441
pixMultiplyGray(PIX        *pixs,
442
                PIX        *pixg,
443
                l_float32   norm)
444
0
{
445
0
l_int32    i, j, w, h, d, ws, hs, ds, wpls, wplg, wpld;
446
0
l_int32    rval, gval, bval, rval2, gval2, bval2, vals, valg, val, maxgray;
447
0
l_uint32   val32;
448
0
l_uint32  *datas, *datag, *datad, *lines, *lineg, *lined;
449
0
PIX       *pixd;
450
451
0
    if (!pixs)
452
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
453
0
    pixGetDimensions(pixs, &ws, &hs, &ds);
454
0
    if (ds != 8 && ds != 32)
455
0
        return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", __func__, NULL);
456
0
    if (!pixg)
457
0
        return (PIX *)ERROR_PTR("pixg not defined", __func__, NULL);
458
0
    pixGetDimensions(pixg, &w, &h, &d);
459
0
    if (d != 8)
460
0
        return (PIX *)ERROR_PTR("pixg not 8 bpp", __func__, NULL);
461
462
0
    if (norm <= 0.0) {
463
0
        pixGetExtremeValue(pixg, 1, L_SELECT_MAX, NULL, NULL, NULL, &maxgray);
464
0
        norm = (maxgray > 0) ? 1.0f / (l_float32)maxgray : 1.0f;
465
0
    }
466
467
0
    if ((pixd = pixCreateTemplate(pixs)) == NULL)
468
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
469
0
    datas = pixGetData(pixs);
470
0
    datag = pixGetData(pixg);
471
0
    datad = pixGetData(pixd);
472
0
    wpls = pixGetWpl(pixs);
473
0
    wplg = pixGetWpl(pixg);
474
0
    wpld = pixGetWpl(pixd);
475
0
    w = L_MIN(ws, w);
476
0
    h = L_MIN(hs, h);
477
0
    for (i = 0; i < h; i++) {
478
0
        lines = datas + i * wpls;
479
0
        lineg = datag + i * wplg;
480
0
        lined = datad + i * wpld;
481
0
        if (ds == 8) {
482
0
            for (j = 0; j < w; j++) {
483
0
                vals = GET_DATA_BYTE(lines, j);
484
0
                valg = GET_DATA_BYTE(lineg, j);
485
0
                val = (l_int32)(vals * valg * norm + 0.5);
486
0
                val = L_MIN(255, val);
487
0
                SET_DATA_BYTE(lined, j, val);
488
0
            }
489
0
        } else {  /* ds == 32 */
490
0
            for (j = 0; j < w; j++) {
491
0
                val32 = *(lines + j);
492
0
                extractRGBValues(val32, &rval, &gval, &bval);
493
0
                valg = GET_DATA_BYTE(lineg, j);
494
0
                rval2 = (l_int32)(rval * valg * norm + 0.5);
495
0
                rval2 = L_MIN(255, rval2);
496
0
                gval2 = (l_int32)(gval * valg * norm + 0.5);
497
0
                gval2 = L_MIN(255, gval2);
498
0
                bval2 = (l_int32)(bval * valg * norm + 0.5);
499
0
                bval2 = L_MIN(255, bval2);
500
0
                composeRGBPixel(rval2, gval2, bval2, lined + j);
501
0
            }
502
0
        }
503
0
    }
504
505
0
    return pixd;
506
0
}
507
508
509
/*-------------------------------------------------------------*
510
 *                Grayscale threshold operation                *
511
 *-------------------------------------------------------------*/
512
/*!
513
 * \brief   pixThresholdToValue()
514
 *
515
 * \param[in]    pixd       [optional]; if not null, must be equal to pixs
516
 * \param[in]    pixs       8, 16, 32 bpp
517
 * \param[in]    threshval
518
 * \param[in]    setval
519
 * \return  pixd always
520
 *
521
 * <pre>
522
 * Notes:
523
 *    ~ operation can be in-place (pixs == pixd) or to a new pixd
524
 *    ~ if %setval > %threshval, sets pixels with a value >= threshval to setval
525
 *    ~ if %setval < %threshval, sets pixels with a value <= threshval to setval
526
 *    ~ if %setval == %threshval, no-op
527
 * </pre>
528
 */
529
PIX *
530
pixThresholdToValue(PIX      *pixd,
531
                    PIX      *pixs,
532
                    l_int32   threshval,
533
                    l_int32   setval)
534
0
{
535
0
l_int32    i, j, w, h, d, wpld, setabove;
536
0
l_uint32  *datad, *lined;
537
538
0
    if (!pixs)
539
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
540
0
    d = pixGetDepth(pixs);
541
0
    if (d != 8 && d != 16 && d != 32)
542
0
        return (PIX *)ERROR_PTR("pixs not 8, 16 or 32 bpp", __func__, pixd);
543
0
    if (pixd && (pixs != pixd))
544
0
        return (PIX *)ERROR_PTR("pixd exists and is not pixs", __func__, pixd);
545
0
    if (threshval < 0 || setval < 0)
546
0
        return (PIX *)ERROR_PTR("threshval & setval not < 0", __func__, pixd);
547
0
    if (d == 8 && setval > 255)
548
0
        return (PIX *)ERROR_PTR("setval > 255 for 8 bpp", __func__, pixd);
549
0
    if (d == 16 && setval > 0xffff)
550
0
        return (PIX *)ERROR_PTR("setval > 0xffff for 16 bpp", __func__, pixd);
551
552
0
    if (!pixd)
553
0
        pixd = pixCopy(NULL, pixs);
554
0
    if (setval == threshval) {
555
0
        L_WARNING("setval == threshval; no operation\n", __func__);
556
0
        return pixd;
557
0
    }
558
559
0
    datad = pixGetData(pixd);
560
0
    pixGetDimensions(pixd, &w, &h, NULL);
561
0
    wpld = pixGetWpl(pixd);
562
0
    if (setval > threshval)
563
0
        setabove = TRUE;
564
0
    else
565
0
        setabove = FALSE;
566
567
0
    for (i = 0; i < h; i++) {
568
0
        lined = datad + i * wpld;
569
0
        if (setabove == TRUE) {
570
0
            if (d == 8) {
571
0
                for (j = 0; j < w; j++) {
572
0
                    if (GET_DATA_BYTE(lined, j) - threshval >= 0)
573
0
                        SET_DATA_BYTE(lined, j, setval);
574
0
                }
575
0
            } else if (d == 16) {
576
0
                for (j = 0; j < w; j++) {
577
0
                    if (GET_DATA_TWO_BYTES(lined, j) - threshval >= 0)
578
0
                        SET_DATA_TWO_BYTES(lined, j, setval);
579
0
                }
580
0
            } else {  /* d == 32 */
581
0
                for (j = 0; j < w; j++) {
582
0
                    if (*(lined + j) >= threshval)
583
0
                        *(lined + j) = setval;
584
0
                }
585
0
            }
586
0
        } else { /* set if below or at threshold */
587
0
            if (d == 8) {
588
0
                for (j = 0; j < w; j++) {
589
0
                    if (GET_DATA_BYTE(lined, j) - threshval <= 0)
590
0
                        SET_DATA_BYTE(lined, j, setval);
591
0
                }
592
0
            } else if (d == 16) {
593
0
                for (j = 0; j < w; j++) {
594
0
                    if (GET_DATA_TWO_BYTES(lined, j) - threshval <= 0)
595
0
                        SET_DATA_TWO_BYTES(lined, j, setval);
596
0
                }
597
0
            } else {  /* d == 32 */
598
0
                for (j = 0; j < w; j++) {
599
0
                    if (*(lined + j) <= threshval)
600
0
                        *(lined + j) = setval;
601
0
                }
602
0
            }
603
0
        }
604
0
    }
605
606
0
    return pixd;
607
0
}
608
609
610
/*-------------------------------------------------------------*
611
 *            Image accumulator arithmetic operations          *
612
 *-------------------------------------------------------------*/
613
/*!
614
 * \brief   pixInitAccumulate()
615
 *
616
 * \param[in]    w, h      of accumulate array
617
 * \param[in]    offset    initialize the 32 bpp to have this
618
 *                         value; not more than 0x40000000
619
 * \return  pixd   32 bpp, or NULL on error
620
 *
621
 * <pre>
622
 * Notes:
623
 *      (1) %offset must be >= 0.
624
 *      (2) %offset is used so that we can do arithmetic
625
 *          with negative number results on l_uint32 data; it
626
 *          prevents the l_uint32 data from going negative.
627
 *      (3) Because we use l_int32 intermediate data results,
628
 *          these should never exceed the max of l_int32 (0x7fffffff).
629
 *          We do not permit the offset to be above 0x40000000,
630
 *          which is half way between 0 and the max of l_int32.
631
 *      (4) The same offset should be used for initialization,
632
 *          multiplication by a constant, and final extraction!
633
 *      (5) If you're only adding positive values, %offset can be 0.
634
 * </pre>
635
 */
636
PIX *
637
pixInitAccumulate(l_int32   w,
638
                  l_int32   h,
639
                  l_uint32  offset)
640
0
{
641
0
PIX  *pixd;
642
643
0
    if ((pixd = pixCreate(w, h, 32)) == NULL)
644
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
645
0
    if (offset > 0x40000000)
646
0
        offset = 0x40000000;
647
0
    pixSetAllArbitrary(pixd, offset);
648
0
    return pixd;
649
0
}
650
651
652
/*!
653
 * \brief   pixFinalAccumulate()
654
 *
655
 * \param[in]    pixs     32 bpp
656
 * \param[in]    offset   same as used for initialization
657
 * \param[in]    depth    8, 16 or 32 bpp, of destination
658
 * \return  pixd   8, 16 or 32 bpp, or NULL on error
659
 *
660
 * <pre>
661
 * Notes:
662
 *      (1) %offset must be >= 0 and should not exceed 0x40000000.
663
 *      (2) %offset is subtracted from the src 32 bpp image
664
 *      (3) For 8 bpp dest, the result is clipped to [0, 0xff]
665
 *      (4) For 16 bpp dest, the result is clipped to [0, 0xffff]
666
 * </pre>
667
 */
668
PIX *
669
pixFinalAccumulate(PIX      *pixs,
670
                   l_uint32  offset,
671
                   l_int32   depth)
672
0
{
673
0
l_int32    i, j, w, h, wpls, wpld, val;
674
0
l_uint32  *datas, *datad, *lines, *lined;
675
0
PIX       *pixd;
676
677
0
    if (!pixs)
678
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
679
0
    if (pixGetDepth(pixs) != 32)
680
0
        return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
681
0
    if (depth != 8 && depth != 16 && depth != 32)
682
0
        return (PIX *)ERROR_PTR("dest depth not 8, 16, 32 bpp", __func__, NULL);
683
0
    if (offset > 0x40000000)
684
0
        offset = 0x40000000;
685
686
0
    pixGetDimensions(pixs, &w, &h, NULL);
687
0
    if ((pixd = pixCreate(w, h, depth)) == NULL)
688
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
689
0
    pixCopyResolution(pixd, pixs);  /* but how did pixs get it initially? */
690
0
    datas = pixGetData(pixs);
691
0
    datad = pixGetData(pixd);
692
0
    wpls = pixGetWpl(pixs);
693
0
    wpld = pixGetWpl(pixd);
694
0
    if (depth == 8) {
695
0
        for (i = 0; i < h; i++) {
696
0
            lines = datas + i * wpls;
697
0
            lined = datad + i * wpld;
698
0
            for (j = 0; j < w; j++) {
699
0
                val = lines[j] - offset;
700
0
                val = L_MAX(0, val);
701
0
                val = L_MIN(255, val);
702
0
                SET_DATA_BYTE(lined, j, (l_uint8)val);
703
0
            }
704
0
        }
705
0
    } else if (depth == 16) {
706
0
        for (i = 0; i < h; i++) {
707
0
            lines = datas + i * wpls;
708
0
            lined = datad + i * wpld;
709
0
            for (j = 0; j < w; j++) {
710
0
                val = lines[j] - offset;
711
0
                val = L_MAX(0, val);
712
0
                val = L_MIN(0xffff, val);
713
0
                SET_DATA_TWO_BYTES(lined, j, (l_uint16)val);
714
0
            }
715
0
        }
716
0
    } else {  /* depth == 32 */
717
0
        for (i = 0; i < h; i++) {
718
0
            lines = datas + i * wpls;
719
0
            lined = datad + i * wpld;
720
0
            for (j = 0; j < w; j++)
721
0
                lined[j] = lines[j] - offset;
722
0
        }
723
0
    }
724
725
0
    return pixd;
726
0
}
727
728
729
/*!
730
 * \brief   pixFinalAccumulateThreshold()
731
 *
732
 * \param[in]    pixs        32 bpp
733
 * \param[in]    offset      same as used for initialization
734
 * \param[in]    threshold   values less than this are set in the destination
735
 * \return  pixd   1 bpp, or NULL on error
736
 *
737
 * <pre>
738
 * Notes:
739
 *      (1) %offset must be >= 0 and should not exceed 0x40000000.
740
 *      (2) %offset is subtracted from the src 32 bpp image
741
 * </pre>
742
 */
743
PIX *
744
pixFinalAccumulateThreshold(PIX      *pixs,
745
                            l_uint32  offset,
746
                            l_uint32  threshold)
747
0
{
748
0
l_int32    i, j, w, h, wpls, wpld, val;
749
0
l_uint32  *datas, *datad, *lines, *lined;
750
0
PIX       *pixd;
751
752
0
    if (!pixs)
753
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
754
0
    if (pixGetDepth(pixs) != 32)
755
0
        return (PIX *)ERROR_PTR("pixs not 32 bpp", __func__, NULL);
756
0
    if (offset > 0x40000000)
757
0
        offset = 0x40000000;
758
759
0
    pixGetDimensions(pixs, &w, &h, NULL);
760
0
    if ((pixd = pixCreate(w, h, 1)) == NULL)
761
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
762
0
    pixCopyResolution(pixd, pixs);  /* but how did pixs get it initially? */
763
0
    datas = pixGetData(pixs);
764
0
    datad = pixGetData(pixd);
765
0
    wpls = pixGetWpl(pixs);
766
0
    wpld = pixGetWpl(pixd);
767
0
    for (i = 0; i < h; i++) {
768
0
        lines = datas + i * wpls;
769
0
        lined = datad + i * wpld;
770
0
        for (j = 0; j < w; j++) {
771
0
            val = lines[j] - offset;
772
0
            if (val >= threshold) {
773
0
                SET_DATA_BIT(lined, j);
774
0
            }
775
0
        }
776
0
    }
777
778
0
    return pixd;
779
0
}
780
781
782
/*!
783
 * \brief   pixAccumulate()
784
 *
785
 * \param[in]    pixd    32 bpp
786
 * \param[in]    pixs    1, 8, 16 or 32 bpp
787
 * \param[in]    op      L_ARITH_ADD or L_ARITH_SUBTRACT
788
 * \return  0 if OK; 1 on error
789
 *
790
 * <pre>
791
 * Notes:
792
 *      (1) This adds or subtracts each pixs value from pixd.
793
 *      (2) This clips to the minimum of pixs and pixd, so they
794
 *          do not need to be the same size.
795
 *      (3) The alignment is to the origin [UL corner] of pixs & pixd.
796
 * </pre>
797
 */
798
l_ok
799
pixAccumulate(PIX     *pixd,
800
              PIX     *pixs,
801
              l_int32  op)
802
0
{
803
0
l_int32    i, j, w, h, d, wd, hd, wpls, wpld;
804
0
l_uint32  *datas, *datad, *lines, *lined;
805
806
807
0
    if (!pixd || (pixGetDepth(pixd) != 32))
808
0
        return ERROR_INT("pixd not defined or not 32 bpp", __func__, 1);
809
0
    if (!pixs)
810
0
        return ERROR_INT("pixs not defined", __func__, 1);
811
0
    d = pixGetDepth(pixs);
812
0
    if (d != 1 && d != 8 && d != 16 && d != 32)
813
0
        return ERROR_INT("pixs not 1, 8, 16 or 32 bpp", __func__, 1);
814
0
    if (op != L_ARITH_ADD && op != L_ARITH_SUBTRACT)
815
0
        return ERROR_INT("op must be in {L_ARITH_ADD, L_ARITH_SUBTRACT}",
816
0
                         __func__, 1);
817
818
0
    datas = pixGetData(pixs);
819
0
    datad = pixGetData(pixd);
820
0
    wpls = pixGetWpl(pixs);
821
0
    wpld = pixGetWpl(pixd);
822
0
    pixGetDimensions(pixs, &w, &h, NULL);
823
0
    pixGetDimensions(pixd, &wd, &hd, NULL);
824
0
    w = L_MIN(w, wd);
825
0
    h = L_MIN(h, hd);
826
0
    if (d == 1) {
827
0
        for (i = 0; i < h; i++) {
828
0
            lines = datas + i * wpls;
829
0
            lined = datad + i * wpld;
830
0
            if (op == L_ARITH_ADD) {
831
0
                for (j = 0; j < w; j++)
832
0
                    lined[j] += GET_DATA_BIT(lines, j);
833
0
            } else {  /* op == L_ARITH_SUBTRACT */
834
0
                for (j = 0; j < w; j++)
835
0
                    lined[j] -= GET_DATA_BIT(lines, j);
836
0
            }
837
0
        }
838
0
    } else if (d == 8) {
839
0
        for (i = 0; i < h; i++) {
840
0
            lines = datas + i * wpls;
841
0
            lined = datad + i * wpld;
842
0
            if (op == L_ARITH_ADD) {
843
0
                for (j = 0; j < w; j++)
844
0
                    lined[j] += GET_DATA_BYTE(lines, j);
845
0
            } else {  /* op == L_ARITH_SUBTRACT */
846
0
                for (j = 0; j < w; j++)
847
0
                    lined[j] -= GET_DATA_BYTE(lines, j);
848
0
            }
849
0
        }
850
0
    } else if (d == 16) {
851
0
        for (i = 0; i < h; i++) {
852
0
            lines = datas + i * wpls;
853
0
            lined = datad + i * wpld;
854
0
            if (op == L_ARITH_ADD) {
855
0
                for (j = 0; j < w; j++)
856
0
                    lined[j] += GET_DATA_TWO_BYTES(lines, j);
857
0
            } else {  /* op == L_ARITH_SUBTRACT */
858
0
                for (j = 0; j < w; j++)
859
0
                    lined[j] -= GET_DATA_TWO_BYTES(lines, j);
860
0
            }
861
0
        }
862
0
    } else {  /* d == 32 */
863
0
        for (i = 0; i < h; i++) {
864
0
            lines = datas + i * wpls;
865
0
            lined = datad + i * wpld;
866
0
            if (op == L_ARITH_ADD) {
867
0
                for (j = 0; j < w; j++)
868
0
                    lined[j] += lines[j];
869
0
            } else {  /* op == L_ARITH_SUBTRACT */
870
0
                for (j = 0; j < w; j++)
871
0
                    lined[j] -= lines[j];
872
0
            }
873
0
        }
874
0
    }
875
876
0
    return 0;
877
0
}
878
879
880
/*!
881
 * \brief   pixMultConstAccumulate()
882
 *
883
 * \param[in]    pixs      32 bpp
884
 * \param[in]    factor
885
 * \param[in]    offset    same as used for initialization
886
 * \return  0 if OK; 1 on error
887
 *
888
 * <pre>
889
 * Notes:
890
 *      (1) %offset must be >= 0 and should not exceed 0x40000000.
891
 *      (2) This multiplies each pixel, relative to offset, by %factor.
892
 *      (3) The result is returned with %offset back in place.
893
 * </pre>
894
 */
895
l_ok
896
pixMultConstAccumulate(PIX       *pixs,
897
                       l_float32  factor,
898
                       l_uint32   offset)
899
0
{
900
0
l_int32    i, j, w, h, wpl, val;
901
0
l_uint32  *data, *line;
902
903
0
    if (!pixs)
904
0
        return ERROR_INT("pixs not defined", __func__, 1);
905
0
    if (pixGetDepth(pixs) != 32)
906
0
        return ERROR_INT("pixs not 32 bpp", __func__, 1);
907
0
    if (offset > 0x40000000)
908
0
        offset = 0x40000000;
909
910
0
    pixGetDimensions(pixs, &w, &h, NULL);
911
0
    data = pixGetData(pixs);
912
0
    wpl = pixGetWpl(pixs);
913
0
    for (i = 0; i < h; i++) {
914
0
        line = data + i * wpl;
915
0
        for (j = 0; j < w; j++) {
916
0
            val = line[j] - offset;
917
0
            val = (l_int32)(val * factor);
918
0
            val += offset;
919
0
            line[j] = (l_uint32)val;
920
0
        }
921
0
    }
922
923
0
    return 0;
924
0
}
925
926
927
/*-----------------------------------------------------------------------*
928
 *                      Absolute value of difference                     *
929
 *-----------------------------------------------------------------------*/
930
/*!
931
 * \brief   pixAbsDifference()
932
 *
933
 * \param[in]    pixs1, pixs2    both either 8 or 16 bpp gray, or 32 bpp RGB
934
 * \return  pixd, or NULL on error
935
 *
936
 * <pre>
937
 * Notes:
938
 *      (1) The depth of pixs1 and pixs2 must be equal.
939
 *      (2) Clips computation to the min size, aligning the UL corners
940
 *      (3) For 8 and 16 bpp, assumes one gray component.
941
 *      (4) For 32 bpp, assumes 3 color components, and ignores the
942
 *          LSB of each word (the alpha channel)
943
 *      (5) Computes the absolute value of the difference between
944
 *          each component value.
945
 * </pre>
946
 */
947
PIX *
948
pixAbsDifference(PIX  *pixs1,
949
                 PIX  *pixs2)
950
0
{
951
0
l_int32    i, j, w, h, w2, h2, d, wpls1, wpls2, wpld, val1, val2, diff;
952
0
l_int32    rval1, gval1, bval1, rval2, gval2, bval2, rdiff, gdiff, bdiff;
953
0
l_uint32  *datas1, *datas2, *datad, *lines1, *lines2, *lined;
954
0
PIX       *pixd;
955
956
0
    if (!pixs1)
957
0
        return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL);
958
0
    if (!pixs2)
959
0
        return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL);
960
0
    d = pixGetDepth(pixs1);
961
0
    if (d != pixGetDepth(pixs2))
962
0
        return (PIX *)ERROR_PTR("src1 and src2 depths unequal", __func__, NULL);
963
0
    if (d != 8 && d != 16 && d != 32)
964
0
        return (PIX *)ERROR_PTR("depths not in {8, 16, 32}", __func__, NULL);
965
966
0
    pixGetDimensions(pixs1, &w, &h, NULL);
967
0
    pixGetDimensions(pixs2, &w2, &h2, NULL);
968
0
    w = L_MIN(w, w2);
969
0
    h = L_MIN(h, h2);
970
0
    if ((pixd = pixCreate(w, h, d)) == NULL)
971
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
972
0
    pixCopyResolution(pixd, pixs1);
973
0
    datas1 = pixGetData(pixs1);
974
0
    datas2 = pixGetData(pixs2);
975
0
    datad = pixGetData(pixd);
976
0
    wpls1 = pixGetWpl(pixs1);
977
0
    wpls2 = pixGetWpl(pixs2);
978
0
    wpld = pixGetWpl(pixd);
979
0
    if (d == 8) {
980
0
        for (i = 0; i < h; i++) {
981
0
            lines1 = datas1 + i * wpls1;
982
0
            lines2 = datas2 + i * wpls2;
983
0
            lined = datad + i * wpld;
984
0
            for (j = 0; j < w; j++) {
985
0
                val1 = GET_DATA_BYTE(lines1, j);
986
0
                val2 = GET_DATA_BYTE(lines2, j);
987
0
                diff = L_ABS(val1 - val2);
988
0
                SET_DATA_BYTE(lined, j, diff);
989
0
            }
990
0
        }
991
0
    } else if (d == 16) {
992
0
        for (i = 0; i < h; i++) {
993
0
            lines1 = datas1 + i * wpls1;
994
0
            lines2 = datas2 + i * wpls2;
995
0
            lined = datad + i * wpld;
996
0
            for (j = 0; j < w; j++) {
997
0
                val1 = GET_DATA_TWO_BYTES(lines1, j);
998
0
                val2 = GET_DATA_TWO_BYTES(lines2, j);
999
0
                diff = L_ABS(val1 - val2);
1000
0
                SET_DATA_TWO_BYTES(lined, j, diff);
1001
0
            }
1002
0
        }
1003
0
    } else {  /* d == 32 */
1004
0
        for (i = 0; i < h; i++) {
1005
0
            lines1 = datas1 + i * wpls1;
1006
0
            lines2 = datas2 + i * wpls2;
1007
0
            lined = datad + i * wpld;
1008
0
            for (j = 0; j < w; j++) {
1009
0
                extractRGBValues(lines1[j], &rval1, &gval1, &bval1);
1010
0
                extractRGBValues(lines2[j], &rval2, &gval2, &bval2);
1011
0
                rdiff = L_ABS(rval1 - rval2);
1012
0
                gdiff = L_ABS(gval1 - gval2);
1013
0
                bdiff = L_ABS(bval1 - bval2);
1014
0
                composeRGBPixel(rdiff, gdiff, bdiff, lined + j);
1015
0
            }
1016
0
        }
1017
0
    }
1018
1019
0
    return pixd;
1020
0
}
1021
1022
1023
/*-----------------------------------------------------------------------*
1024
 *                           Sum of color images                         *
1025
 *-----------------------------------------------------------------------*/
1026
/*!
1027
 * \brief   pixAddRGB()
1028
 *
1029
 * \param[in]    pixs1, pixs2    32 bpp RGB, or colormapped
1030
 * \return  pixd, or NULL on error
1031
 *
1032
 * <pre>
1033
 * Notes:
1034
 *      (1) Clips computation to the minimum size, aligning the UL corners.
1035
 *      (2) Removes any colormap to RGB, and ignores the LSB of each
1036
 *          pixel word (the alpha channel).
1037
 *      (3) Adds each component value, pixelwise, clipping to 255.
1038
 *      (4) This is useful to combine two images where most of the
1039
 *          pixels are essentially black, such as in pixPerceptualDiff().
1040
 * </pre>
1041
 */
1042
PIX *
1043
pixAddRGB(PIX  *pixs1,
1044
          PIX  *pixs2)
1045
0
{
1046
0
l_int32    i, j, w, h, d, w2, h2, d2, wplc1, wplc2, wpld;
1047
0
l_int32    rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval;
1048
0
l_uint32  *datac1, *datac2, *datad, *linec1, *linec2, *lined;
1049
0
PIX       *pixc1, *pixc2, *pixd;
1050
1051
0
    if (!pixs1)
1052
0
        return (PIX *)ERROR_PTR("pixs1 not defined", __func__, NULL);
1053
0
    if (!pixs2)
1054
0
        return (PIX *)ERROR_PTR("pixs2 not defined", __func__, NULL);
1055
0
    pixGetDimensions(pixs1, &w, &h, &d);
1056
0
    pixGetDimensions(pixs2, &w2, &h2, &d2);
1057
0
    if (!pixGetColormap(pixs1) && d != 32)
1058
0
        return (PIX *)ERROR_PTR("pixs1 not cmapped or rgb", __func__, NULL);
1059
0
    if (!pixGetColormap(pixs2) && d2 != 32)
1060
0
        return (PIX *)ERROR_PTR("pixs2 not cmapped or rgb", __func__, NULL);
1061
0
    if (pixGetColormap(pixs1))
1062
0
        pixc1 = pixRemoveColormap(pixs1, REMOVE_CMAP_TO_FULL_COLOR);
1063
0
    else
1064
0
        pixc1 = pixClone(pixs1);
1065
0
    if (pixGetColormap(pixs2))
1066
0
        pixc2 = pixRemoveColormap(pixs2, REMOVE_CMAP_TO_FULL_COLOR);
1067
0
    else
1068
0
        pixc2 = pixClone(pixs2);
1069
1070
0
    w = L_MIN(w, w2);
1071
0
    h = L_MIN(h, h2);
1072
0
    pixd = pixCreate(w, h, 32);
1073
0
    pixCopyResolution(pixd, pixs1);
1074
0
    datac1 = pixGetData(pixc1);
1075
0
    datac2 = pixGetData(pixc2);
1076
0
    datad = pixGetData(pixd);
1077
0
    wplc1 = pixGetWpl(pixc1);
1078
0
    wplc2 = pixGetWpl(pixc2);
1079
0
    wpld = pixGetWpl(pixd);
1080
0
    for (i = 0; i < h; i++) {
1081
0
        linec1 = datac1 + i * wplc1;
1082
0
        linec2 = datac2 + i * wplc2;
1083
0
        lined = datad + i * wpld;
1084
0
        for (j = 0; j < w; j++) {
1085
0
            extractRGBValues(linec1[j], &rval1, &gval1, &bval1);
1086
0
            extractRGBValues(linec2[j], &rval2, &gval2, &bval2);
1087
0
            rval = L_MIN(255, rval1 + rval2);
1088
0
            gval = L_MIN(255, gval1 + gval2);
1089
0
            bval = L_MIN(255, bval1 + bval2);
1090
0
            composeRGBPixel(rval, gval, bval, lined + j);
1091
0
        }
1092
0
    }
1093
1094
0
    pixDestroy(&pixc1);
1095
0
    pixDestroy(&pixc2);
1096
0
    return pixd;
1097
0
}
1098
1099
1100
/*-----------------------------------------------------------------------*
1101
 *             Two-image min and max operations (8 and 16 bpp)           *
1102
 *-----------------------------------------------------------------------*/
1103
/*!
1104
 * \brief   pixMinOrMax()
1105
 *
1106
 * \param[in]    pixd     [optional] destination: this can be null,
1107
 *                        equal to pixs1, or different from pixs1
1108
 * \param[in]    pixs1    can be equal to pixd
1109
 * \param[in]    pixs2
1110
 * \param[in]    type     L_CHOOSE_MIN, L_CHOOSE_MAX
1111
 * \return  pixd always
1112
 *
1113
 * <pre>
1114
 * Notes:
1115
 *      (1) This gives the min or max of two images, component-wise.
1116
 *      (2) The depth can be 8 or 16 bpp for 1 component, and 32 bpp
1117
 *          for a 3 component image.  For 32 bpp, ignore the LSB
1118
 *          of each word (the alpha channel)
1119
 *      (3) There are 3 cases:
1120
 *          ~  if pixd == null,   MinOrMax(src1, src2) --> new pixd
1121
 *          ~  if pixd == pixs1,  MinOrMax(src1, src2) --> src1  (in-place)
1122
 *          ~  if pixd != pixs1,  MinOrMax(src1, src2) --> input pixd
1123
 * </pre>
1124
 */
1125
PIX *
1126
pixMinOrMax(PIX     *pixd,
1127
            PIX     *pixs1,
1128
            PIX     *pixs2,
1129
            l_int32  type)
1130
0
{
1131
0
l_int32    d, ws, hs, w, h, wpls, wpld, i, j, vals, vald, val;
1132
0
l_int32    rval1, gval1, bval1, rval2, gval2, bval2, rval, gval, bval;
1133
0
l_uint32  *datas, *datad, *lines, *lined;
1134
1135
0
    if (!pixs1)
1136
0
        return (PIX *)ERROR_PTR("pixs1 not defined", __func__, pixd);
1137
0
    if (!pixs2)
1138
0
        return (PIX *)ERROR_PTR("pixs2 not defined", __func__, pixd);
1139
0
    if (pixs1 == pixs2)
1140
0
        return (PIX *)ERROR_PTR("pixs1 and pixs2 must differ", __func__, pixd);
1141
0
    if (type != L_CHOOSE_MIN && type != L_CHOOSE_MAX)
1142
0
        return (PIX *)ERROR_PTR("invalid type", __func__, pixd);
1143
0
    d = pixGetDepth(pixs1);
1144
0
    if (pixGetDepth(pixs2) != d)
1145
0
        return (PIX *)ERROR_PTR("depths unequal", __func__, pixd);
1146
0
    if (d != 8 && d != 16 && d != 32)
1147
0
        return (PIX *)ERROR_PTR("depth not 8, 16 or 32 bpp", __func__, pixd);
1148
1149
0
    if (pixs1 != pixd)
1150
0
        pixd = pixCopy(pixd, pixs1);
1151
1152
0
    pixGetDimensions(pixs2, &ws, &hs, NULL);
1153
0
    pixGetDimensions(pixd, &w, &h, NULL);
1154
0
    w = L_MIN(w, ws);
1155
0
    h = L_MIN(h, hs);
1156
0
    datas = pixGetData(pixs2);
1157
0
    datad = pixGetData(pixd);
1158
0
    wpls = pixGetWpl(pixs2);
1159
0
    wpld = pixGetWpl(pixd);
1160
0
    for (i = 0; i < h; i++) {
1161
0
        lines = datas + i * wpls;
1162
0
        lined = datad + i * wpld;
1163
0
        if (d == 8) {
1164
0
            for (j = 0; j < w; j++) {
1165
0
                vals = GET_DATA_BYTE(lines, j);
1166
0
                vald = GET_DATA_BYTE(lined, j);
1167
0
                if (type == L_CHOOSE_MIN)
1168
0
                    val = L_MIN(vals, vald);
1169
0
                else  /* type == L_CHOOSE_MAX */
1170
0
                    val = L_MAX(vals, vald);
1171
0
                SET_DATA_BYTE(lined, j, val);
1172
0
            }
1173
0
        } else if (d == 16) {
1174
0
            for (j = 0; j < w; j++) {
1175
0
                vals = GET_DATA_TWO_BYTES(lines, j);
1176
0
                vald = GET_DATA_TWO_BYTES(lined, j);
1177
0
                if (type == L_CHOOSE_MIN)
1178
0
                    val = L_MIN(vals, vald);
1179
0
                else  /* type == L_CHOOSE_MAX */
1180
0
                    val = L_MAX(vals, vald);
1181
0
                SET_DATA_TWO_BYTES(lined, j, val);
1182
0
            }
1183
0
        } else {  /* d == 32 */
1184
0
            for (j = 0; j < w; j++) {
1185
0
                extractRGBValues(lines[j], &rval1, &gval1, &bval1);
1186
0
                extractRGBValues(lined[j], &rval2, &gval2, &bval2);
1187
0
                if (type == L_CHOOSE_MIN) {
1188
0
                    rval = L_MIN(rval1, rval2);
1189
0
                    gval = L_MIN(gval1, gval2);
1190
0
                    bval = L_MIN(bval1, bval2);
1191
0
                } else {  /* type == L_CHOOSE_MAX */
1192
0
                    rval = L_MAX(rval1, rval2);
1193
0
                    gval = L_MAX(gval1, gval2);
1194
0
                    bval = L_MAX(bval1, bval2);
1195
0
                }
1196
0
                composeRGBPixel(rval, gval, bval, lined + j);
1197
0
            }
1198
0
        }
1199
0
    }
1200
1201
0
    return pixd;
1202
0
}
1203
1204
1205
/*-----------------------------------------------------------------------*
1206
 *                    Scale for maximum dynamic range                    *
1207
 *-----------------------------------------------------------------------*/
1208
/*!
1209
 * \brief   pixMaxDynamicRange()
1210
 *
1211
 * \param[in]    pixs    4, 8, 16 or 32 bpp source
1212
 * \param[in]    type    L_LINEAR_SCALE or L_LOG_SCALE
1213
 * \return  pixd    8 bpp, or NULL on error
1214
 *
1215
 * <pre>
1216
 * Notes:
1217
 *      (1) Scales pixel values to fit maximally within the dest 8 bpp pixd
1218
 *      (2) Assumes the source 'pixels' are a 1-component scalar.  For
1219
 *          a 32 bpp source, each pixel is treated as a single number --
1220
 *          not as a 3-component rgb pixel value.
1221
 *      (3) Uses a LUT for log scaling.
1222
 * </pre>
1223
 */
1224
PIX *
1225
pixMaxDynamicRange(PIX     *pixs,
1226
                   l_int32  type)
1227
0
{
1228
0
l_uint8     dval;
1229
0
l_int32     i, j, w, h, d, wpls, wpld, max;
1230
0
l_uint32   *datas, *datad;
1231
0
l_uint32    word, sval;
1232
0
l_uint32   *lines, *lined;
1233
0
l_float32   factor;
1234
0
l_float32  *tab;
1235
0
PIX        *pixd;
1236
1237
0
    if (!pixs)
1238
0
        return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
1239
0
    pixGetDimensions(pixs, &w, &h, &d);
1240
0
    if (d != 4 && d != 8 && d != 16 && d != 32)
1241
0
        return (PIX *)ERROR_PTR("pixs not in {4,8,16,32} bpp", __func__, NULL);
1242
0
    if (type != L_LINEAR_SCALE && type != L_LOG_SCALE)
1243
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
1244
1245
0
    if ((pixd = pixCreate(w, h, 8)) == NULL)
1246
0
        return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
1247
0
    pixCopyResolution(pixd, pixs);
1248
0
    datas = pixGetData(pixs);
1249
0
    datad = pixGetData(pixd);
1250
0
    wpls = pixGetWpl(pixs);
1251
0
    wpld = pixGetWpl(pixd);
1252
1253
        /* Get max */
1254
0
    max = 0;
1255
0
    for (i = 0; i < h; i++) {
1256
0
        lines = datas + i * wpls;
1257
0
        for (j = 0; j < wpls; j++) {
1258
0
            word = *(lines + j);
1259
0
            if (d == 4) {
1260
0
                max = L_MAX(max, word >> 28);
1261
0
                max = L_MAX(max, (word >> 24) & 0xf);
1262
0
                max = L_MAX(max, (word >> 20) & 0xf);
1263
0
                max = L_MAX(max, (word >> 16) & 0xf);
1264
0
                max = L_MAX(max, (word >> 12) & 0xf);
1265
0
                max = L_MAX(max, (word >> 8) & 0xf);
1266
0
                max = L_MAX(max, (word >> 4) & 0xf);
1267
0
                max = L_MAX(max, word & 0xf);
1268
0
            } else if (d == 8) {
1269
0
                max = L_MAX(max, word >> 24);
1270
0
                max = L_MAX(max, (word >> 16) & 0xff);
1271
0
                max = L_MAX(max, (word >> 8) & 0xff);
1272
0
                max = L_MAX(max, word & 0xff);
1273
0
            } else if (d == 16) {
1274
0
                max = L_MAX(max, word >> 16);
1275
0
                max = L_MAX(max, word & 0xffff);
1276
0
            } else {  /* d == 32 (rgb) */
1277
0
                max = L_MAX(max, word);
1278
0
            }
1279
0
        }
1280
0
    }
1281
1282
        /* Map to the full dynamic range */
1283
0
    if (d == 4) {
1284
0
        if (type == L_LINEAR_SCALE) {
1285
0
            factor = 255.f / (l_float32)max;
1286
0
            for (i = 0; i < h; i++) {
1287
0
                lines = datas + i * wpls;
1288
0
                lined = datad + i * wpld;
1289
0
                for (j = 0; j < w; j++) {
1290
0
                    sval = GET_DATA_QBIT(lines, j);
1291
0
                    dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1292
0
                    SET_DATA_QBIT(lined, j, dval);
1293
0
                }
1294
0
            }
1295
0
        } else {  /* type == L_LOG_SCALE) */
1296
0
            tab = makeLogBase2Tab();
1297
0
            factor = 255.f / getLogBase2(max, tab);
1298
0
            for (i = 0; i < h; i++) {
1299
0
                lines = datas + i * wpls;
1300
0
                lined = datad + i * wpld;
1301
0
                for (j = 0; j < w; j++) {
1302
0
                    sval = GET_DATA_QBIT(lines, j);
1303
0
                    dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1304
0
                    SET_DATA_BYTE(lined, j, dval);
1305
0
                }
1306
0
            }
1307
0
            LEPT_FREE(tab);
1308
0
        }
1309
0
    } else if (d == 8) {
1310
0
        if (type == L_LINEAR_SCALE) {
1311
0
            factor = 255.f / (l_float32)max;
1312
0
            for (i = 0; i < h; i++) {
1313
0
                lines = datas + i * wpls;
1314
0
                lined = datad + i * wpld;
1315
0
                for (j = 0; j < w; j++) {
1316
0
                    sval = GET_DATA_BYTE(lines, j);
1317
0
                    dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1318
0
                    SET_DATA_BYTE(lined, j, dval);
1319
0
                }
1320
0
            }
1321
0
        } else {  /* type == L_LOG_SCALE) */
1322
0
            tab = makeLogBase2Tab();
1323
0
            factor = 255.f / getLogBase2(max, tab);
1324
0
            for (i = 0; i < h; i++) {
1325
0
                lines = datas + i * wpls;
1326
0
                lined = datad + i * wpld;
1327
0
                for (j = 0; j < w; j++) {
1328
0
                    sval = GET_DATA_BYTE(lines, j);
1329
0
                    dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1330
0
                    SET_DATA_BYTE(lined, j, dval);
1331
0
                }
1332
0
            }
1333
0
            LEPT_FREE(tab);
1334
0
        }
1335
0
    } else if (d == 16) {
1336
0
        if (type == L_LINEAR_SCALE) {
1337
0
            factor = 255.f / (l_float32)max;
1338
0
            for (i = 0; i < h; i++) {
1339
0
                lines = datas + i * wpls;
1340
0
                lined = datad + i * wpld;
1341
0
                for (j = 0; j < w; j++) {
1342
0
                    sval = GET_DATA_TWO_BYTES(lines, j);
1343
0
                    dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1344
0
                    SET_DATA_BYTE(lined, j, dval);
1345
0
                }
1346
0
            }
1347
0
        } else {  /* type == L_LOG_SCALE) */
1348
0
            tab = makeLogBase2Tab();
1349
0
            factor = 255.f / getLogBase2(max, tab);
1350
0
            for (i = 0; i < h; i++) {
1351
0
                lines = datas + i * wpls;
1352
0
                lined = datad + i * wpld;
1353
0
                for (j = 0; j < w; j++) {
1354
0
                    sval = GET_DATA_TWO_BYTES(lines, j);
1355
0
                    dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1356
0
                    SET_DATA_BYTE(lined, j, dval);
1357
0
                }
1358
0
            }
1359
0
            LEPT_FREE(tab);
1360
0
        }
1361
0
    } else {  /* d == 32 */
1362
0
        if (type == L_LINEAR_SCALE) {
1363
0
            factor = 255.f / (l_float32)max;
1364
0
            for (i = 0; i < h; i++) {
1365
0
                lines = datas + i * wpls;
1366
0
                lined = datad + i * wpld;
1367
0
                for (j = 0; j < w; j++) {
1368
0
                    sval = lines[j];
1369
0
                    dval = (l_uint8)(factor * (l_float32)sval + 0.5);
1370
0
                    SET_DATA_BYTE(lined, j, dval);
1371
0
                }
1372
0
            }
1373
0
        } else {  /* type == L_LOG_SCALE) */
1374
0
            tab = makeLogBase2Tab();
1375
0
            factor = 255.f / getLogBase2(max, tab);
1376
0
            for (i = 0; i < h; i++) {
1377
0
                lines = datas + i * wpls;
1378
0
                lined = datad + i * wpld;
1379
0
                for (j = 0; j < w; j++) {
1380
0
                    sval = lines[j];
1381
0
                    dval = (l_uint8)(factor * getLogBase2(sval, tab) + 0.5);
1382
0
                    SET_DATA_BYTE(lined, j, dval);
1383
0
                }
1384
0
            }
1385
0
            LEPT_FREE(tab);
1386
0
        }
1387
0
    }
1388
1389
0
    return pixd;
1390
0
}
1391
1392
1393
/*!
1394
 * \brief   pixMaxDynamicRangeRGB()
1395
 *
1396
 * \param[in]    pixs    32 bpp rgb source
1397
 * \param[in]    type    L_LINEAR_SCALE or L_LOG_SCALE
1398
 * \return  pixd   32 bpp, or NULL on error
1399
 *
1400
 * <pre>
1401
 * Notes:
1402
 *      (1) Scales pixel values to fit maximally within a 32 bpp dest pixd
1403
 *      (2) All color components are scaled with the same factor, based
1404
 *          on the maximum r, g or b component in the image.  This should
1405
 *          not be used if the 32-bit value is a single number (e.g., a
1406
 *          count in a histogram generated by pixMakeHistoHS()).
1407
 *      (3) Uses a LUT for log scaling.
1408
 * </pre>
1409
 */
1410
PIX *
1411
pixMaxDynamicRangeRGB(PIX     *pixs,
1412
                      l_int32  type)
1413
0
{
1414
0
l_int32     i, j, w, h, wpls, wpld, max;
1415
0
l_uint32    sval, dval, word;
1416
0
l_uint32   *datas, *datad;
1417
0
l_uint32   *lines, *lined;
1418
0
l_float32   factor;
1419
0
l_float32  *tab;
1420
0
PIX        *pixd;
1421
1422
0
    if (!pixs || pixGetDepth(pixs) != 32)
1423
0
        return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", __func__, NULL);
1424
0
    if (type != L_LINEAR_SCALE && type != L_LOG_SCALE)
1425
0
        return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
1426
1427
        /* Get max */
1428
0
    pixd = pixCreateTemplate(pixs);
1429
0
    datas = pixGetData(pixs);
1430
0
    datad = pixGetData(pixd);
1431
0
    wpls = pixGetWpl(pixs);
1432
0
    wpld = pixGetWpl(pixd);
1433
0
    pixGetDimensions(pixs, &w, &h, NULL);
1434
0
    max = 0;
1435
0
    for (i = 0; i < h; i++) {
1436
0
        lines = datas + i * wpls;
1437
0
        for (j = 0; j < wpls; j++) {
1438
0
            word = lines[j];
1439
0
            max = L_MAX(max, word >> 24);
1440
0
            max = L_MAX(max, (word >> 16) & 0xff);
1441
0
            max = L_MAX(max, (word >> 8) & 0xff);
1442
0
        }
1443
0
    }
1444
0
    if (max == 0) {
1445
0
        L_WARNING("max = 0; setting to 1\n", __func__);
1446
0
        max = 1;
1447
0
    }
1448
1449
        /* Map to the full dynamic range */
1450
0
    if (type == L_LINEAR_SCALE) {
1451
0
        factor = 255.f / (l_float32)max;
1452
0
        for (i = 0; i < h; i++) {
1453
0
            lines = datas + i * wpls;
1454
0
            lined = datad + i * wpld;
1455
0
            for (j = 0; j < w; j++) {
1456
0
                sval = lines[j];
1457
0
                dval = linearScaleRGBVal(sval, factor);
1458
0
                lined[j] = dval;
1459
0
            }
1460
0
        }
1461
0
    } else {  /* type == L_LOG_SCALE) */
1462
0
        tab = makeLogBase2Tab();
1463
0
        factor = 255.f / getLogBase2(max, tab);
1464
0
        for (i = 0; i < h; i++) {
1465
0
            lines = datas + i * wpls;
1466
0
            lined = datad + i * wpld;
1467
0
            for (j = 0; j < w; j++) {
1468
0
                sval = lines[j];
1469
0
                dval = logScaleRGBVal(sval, tab, factor);
1470
0
                lined[j] = dval;
1471
0
            }
1472
0
        }
1473
0
        LEPT_FREE(tab);
1474
0
    }
1475
1476
0
    return pixd;
1477
0
}
1478
1479
1480
/*-----------------------------------------------------------------------*
1481
 *                         RGB pixel value scaling                       *
1482
 *-----------------------------------------------------------------------*/
1483
/*!
1484
 * \brief   linearScaleRGBVal()
1485
 *
1486
 * \param[in]    sval     32-bit rgb pixel value
1487
 * \param[in]    factor   multiplication factor on each component
1488
 * \return  dval  linearly scaled version of %sval
1489
 *
1490
 * <pre>
1491
 * Notes:
1492
 *      (1) %factor must be chosen to be not greater than (255 / maxcomp),
1493
 *          where maxcomp is the maximum value of the pixel components.
1494
 *          Otherwise, the product will overflow a uint8.  In use, factor
1495
 *          is the same for all pixels in a pix.
1496
 *      (2) No scaling is performed on the transparency ("A") component.
1497
 * </pre>
1498
 */
1499
l_uint32
1500
linearScaleRGBVal(l_uint32   sval,
1501
                  l_float32  factor)
1502
0
{
1503
0
l_uint32  dval;
1504
1505
0
    dval = ((l_uint8)(factor * (sval >> 24) + 0.5f) << 24) |
1506
0
           ((l_uint8)(factor * ((sval >> 16) & 0xff) + 0.5f) << 16) |
1507
0
           ((l_uint8)(factor * ((sval >> 8) & 0xff) + 0.5f) << 8) |
1508
0
           (sval & 0xff);
1509
0
    return dval;
1510
0
}
1511
1512
1513
/*!
1514
 * \brief   logScaleRGBVal()
1515
 *
1516
 * \param[in]    sval     32-bit rgb pixel value
1517
 * \param[in]    tab      256 entry log-base-2 table
1518
 * \param[in]    factor   multiplication factor on each component
1519
 * \return  dval  log scaled version of %sval
1520
 *
1521
 * <pre>
1522
 * Notes:
1523
 *      (1) %tab is made with makeLogBase2Tab().
1524
 *      (2) %factor must be chosen to be not greater than
1525
 *          255.0 / log[base2](maxcomp), where maxcomp is the maximum
1526
 *          value of the pixel components.  Otherwise, the product
1527
 *          will overflow a uint8.  In use, factor is the same for
1528
 *          all pixels in a pix.
1529
 *      (3) No scaling is performed on the transparency ("A") component.
1530
 * </pre>
1531
 */
1532
l_uint32
1533
logScaleRGBVal(l_uint32    sval,
1534
               l_float32  *tab,
1535
               l_float32   factor)
1536
0
{
1537
0
l_uint32  dval;
1538
1539
0
    dval = ((l_uint8)(factor * getLogBase2(sval >> 24, tab) + 0.5f) << 24) |
1540
0
           ((l_uint8)(factor * getLogBase2(((sval >> 16) & 0xff), tab) + 0.5f)
1541
0
                     << 16) |
1542
0
           ((l_uint8)(factor * getLogBase2(((sval >> 8) & 0xff), tab) + 0.5f)
1543
0
                     << 8) |
1544
0
           (sval & 0xff);
1545
0
    return dval;
1546
0
}
1547
1548
1549
/*-----------------------------------------------------------------------*
1550
 *                            Log base2 lookup                           *
1551
 *-----------------------------------------------------------------------*/
1552
/*
1553
 * \brief   makeLogBase2Tab()
1554
 *
1555
 * \return  tab   table giving the log[base2] of values from 1 to 255
1556
 */
1557
l_float32 *
1558
makeLogBase2Tab(void)
1559
0
{
1560
0
l_int32     i;
1561
0
l_float32   log2;
1562
0
l_float32  *tab;
1563
1564
0
    if ((tab = (l_float32 *)LEPT_CALLOC(256, sizeof(l_float32))) == NULL)
1565
0
        return (l_float32 *)ERROR_PTR("tab not made", __func__, NULL);
1566
1567
0
    log2 = (l_float32)log((l_float32)2);
1568
0
    for (i = 0; i < 256; i++)
1569
0
        tab[i] = (l_float32)log((l_float32)i) / log2;
1570
1571
0
    return tab;
1572
0
}
1573
1574
1575
/*
1576
 * \brief   getLogBase2()
1577
 *
1578
 * \param[in]    val       in range [0 ... 255]
1579
 * \param[in]    logtab    256-entry table of logs
1580
 * \return       logval    log[base2] of %val, or 0 on error
1581
 */
1582
l_float32
1583
getLogBase2(l_int32     val,
1584
            l_float32  *logtab)
1585
0
{
1586
0
    if (!logtab)
1587
0
        return ERROR_INT("logtab not defined", __func__, 0);
1588
1589
0
    if (val < 0x100)
1590
0
        return logtab[val];
1591
0
    else if (val < 0x10000)
1592
0
        return 8.0f + logtab[val >> 8];
1593
0
    else if (val < 0x1000000)
1594
0
        return 16.0f + logtab[val >> 16];
1595
0
    else
1596
0
        return 24.0f + logtab[val >> 24];
1597
0
}