comparison mupdf-source/thirdparty/leptonica/src/fpix2.c @ 2:b50eed0cc0ef upstream

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