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