comparison mupdf-source/thirdparty/leptonica/src/bilinear.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 bilinear.c
29 * <pre>
30 *
31 * Bilinear (4 pt) image transformation using a sampled
32 * (to nearest integer) transform on each dest point
33 * PIX *pixBilinearSampledPta()
34 * PIX *pixBilinearSampled()
35 *
36 * Bilinear (4 pt) image transformation using interpolation
37 * (or area mapping) for anti-aliasing images that are
38 * 2, 4, or 8 bpp gray, or colormapped, or 32 bpp RGB
39 * PIX *pixBilinearPta()
40 * PIX *pixBilinear()
41 * PIX *pixBilinearPtaColor()
42 * PIX *pixBilinearColor()
43 * PIX *pixBilinearPtaGray()
44 * PIX *pixBilinearGray()
45 *
46 * Bilinear transform including alpha (blend) component
47 * PIX *pixBilinearPtaWithAlpha()
48 *
49 * Bilinear coordinate transformation
50 * l_int32 getBilinearXformCoeffs()
51 * l_int32 bilinearXformSampledPt()
52 * l_int32 bilinearXformPt()
53 *
54 * A bilinear transform can be specified as a specific functional
55 * mapping between 4 points in the source and 4 points in the dest.
56 * It can be used as an approximation to a (nonlinear) projective
57 * transform, because for small warps it is very similar and
58 * it is more stable. (Projective transforms have a division
59 * by a quantity that can get arbitrarily small.)
60 *
61 * We give both a bilinear coordinate transformation and
62 * a bilinear image transformation.
63 *
64 * For the former, we ask for the coordinate value (x',y')
65 * in the transformed space for any point (x,y) in the original
66 * space. The coefficients of the transformation are found by
67 * solving 8 simultaneous equations for the 8 coordinates of
68 * the 4 points in src and dest. The transformation can then
69 * be used to compute the associated image transform, by
70 * computing, for each dest pixel, the relevant pixel(s) in
71 * the source. This can be done either by taking the closest
72 * src pixel to each transformed dest pixel ("sampling") or
73 * by doing an interpolation and averaging over 4 source
74 * pixels with appropriate weightings ("interpolated").
75 *
76 * A typical application would be to remove some of the
77 * keystoning due to a projective transform in the imaging system.
78 *
79 * The bilinear transform is given by specifying two equations:
80 *
81 * x' = ax + by + cxy + d
82 * y' = ex + fy + gxy + h
83 *
84 * where the eight coefficients have been computed from four
85 * sets of these equations, each for two corresponding data pts.
86 * In practice, once the coefficients are known, we use the
87 * equations "backwards": for each point (x,y) in the dest image,
88 * these two equations are used to compute the corresponding point
89 * (x',y') in the src. That computed point in the src is then used
90 * to determine the corresponding dest pixel value in one of two ways:
91 *
92 * ~ sampling: simply take the value of the src pixel in which this
93 * point falls
94 * ~ interpolation: take appropriate linear combinations of the
95 * four src pixels that this dest pixel would
96 * overlap, with the coefficients proportional
97 * to the amount of overlap
98 *
99 * For small warp, like rotation, area mapping in the
100 * interpolation is equivalent to linear interpolation.
101 *
102 * Typical relative timing of transforms (sampled = 1.0):
103 * 8 bpp: sampled 1.0
104 * interpolated 1.6
105 * 32 bpp: sampled 1.0
106 * interpolated 1.8
107 * Additionally, the computation time/pixel is nearly the same
108 * for 8 bpp and 32 bpp, for both sampled and interpolated.
109 * </pre>
110 */
111
112 #ifdef HAVE_CONFIG_H
113 #include <config_auto.h>
114 #endif /* HAVE_CONFIG_H */
115
116 #include <string.h>
117 #include <math.h>
118 #include "allheaders.h"
119
120 extern l_float32 AlphaMaskBorderVals[2];
121
122 /*-------------------------------------------------------------*
123 * Sampled bilinear image transformation *
124 *-------------------------------------------------------------*/
125 /*!
126 * \brief pixBilinearSampledPta()
127 *
128 * \param[in] pixs all depths
129 * \param[in] ptad 4 pts of final coordinate space
130 * \param[in] ptas 4 pts of initial coordinate space
131 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
132 * \return pixd, or NULL on error
133 *
134 * <pre>
135 * Notes:
136 * (1) Brings in either black or white pixels from the boundary.
137 * (2) Retains colormap, which you can do for a sampled transform..
138 * (3) No 3 of the 4 points may be collinear.
139 * (4) For 8 and 32 bpp pix, better quality is obtained by the
140 * somewhat slower pixBilinearPta(). See that
141 * function for relative timings between sampled and interpolated.
142 * </pre>
143 */
144 PIX *
145 pixBilinearSampledPta(PIX *pixs,
146 PTA *ptad,
147 PTA *ptas,
148 l_int32 incolor)
149 {
150 l_float32 *vc;
151 PIX *pixd;
152
153 if (!pixs)
154 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
155 if (!ptas)
156 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
157 if (!ptad)
158 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
159 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
160 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
161 if (ptaGetCount(ptas) != 4)
162 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL);
163 if (ptaGetCount(ptad) != 4)
164 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL);
165
166 /* Get backwards transform from dest to src, and apply it */
167 getBilinearXformCoeffs(ptad, ptas, &vc);
168 pixd = pixBilinearSampled(pixs, vc, incolor);
169 LEPT_FREE(vc);
170
171 return pixd;
172 }
173
174
175 /*!
176 * \brief pixBilinearSampled()
177 *
178 * \param[in] pixs all depths
179 * \param[in] vc vector of 8 coefficients for bilinear transformation
180 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
181 * \return pixd, or NULL on error
182 *
183 * <pre>
184 * Notes:
185 * (1) Brings in either black or white pixels from the boundary.
186 * (2) Retains colormap, which you can do for a sampled transform..
187 * (3) For 8 or 32 bpp, much better quality is obtained by the
188 * somewhat slower pixBilinear(). See that function
189 * for relative timings between sampled and interpolated.
190 * </pre>
191 */
192 PIX *
193 pixBilinearSampled(PIX *pixs,
194 l_float32 *vc,
195 l_int32 incolor)
196 {
197 l_int32 i, j, w, h, d, x, y, wpls, wpld, color, cmapindex;
198 l_uint32 val;
199 l_uint32 *datas, *datad, *lines, *lined;
200 PIX *pixd;
201 PIXCMAP *cmap;
202
203 if (!pixs)
204 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
205 if (!vc)
206 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
207 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
208 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
209 pixGetDimensions(pixs, &w, &h, &d);
210 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 32)
211 return (PIX *)ERROR_PTR("depth not 1, 2, 4, 8 or 16", __func__, NULL);
212
213 /* Init all dest pixels to color to be brought in from outside */
214 pixd = pixCreateTemplate(pixs);
215 if ((cmap = pixGetColormap(pixs)) != NULL) {
216 if (incolor == L_BRING_IN_WHITE)
217 color = 1;
218 else
219 color = 0;
220 pixcmapAddBlackOrWhite(cmap, color, &cmapindex);
221 pixSetAllArbitrary(pixd, cmapindex);
222 } else {
223 if ((d == 1 && incolor == L_BRING_IN_WHITE) ||
224 (d > 1 && incolor == L_BRING_IN_BLACK)) {
225 pixClearAll(pixd);
226 } else {
227 pixSetAll(pixd);
228 }
229 }
230
231 /* Scan over the dest pixels */
232 datas = pixGetData(pixs);
233 wpls = pixGetWpl(pixs);
234 datad = pixGetData(pixd);
235 wpld = pixGetWpl(pixd);
236 for (i = 0; i < h; i++) {
237 lined = datad + i * wpld;
238 for (j = 0; j < w; j++) {
239 bilinearXformSampledPt(vc, j, i, &x, &y);
240 if (x < 0 || y < 0 || x >=w || y >= h)
241 continue;
242 lines = datas + y * wpls;
243 if (d == 1) {
244 val = GET_DATA_BIT(lines, x);
245 SET_DATA_BIT_VAL(lined, j, val);
246 } else if (d == 8) {
247 val = GET_DATA_BYTE(lines, x);
248 SET_DATA_BYTE(lined, j, val);
249 } else if (d == 32) {
250 lined[j] = lines[x];
251 } else if (d == 2) {
252 val = GET_DATA_DIBIT(lines, x);
253 SET_DATA_DIBIT(lined, j, val);
254 } else if (d == 4) {
255 val = GET_DATA_QBIT(lines, x);
256 SET_DATA_QBIT(lined, j, val);
257 }
258 }
259 }
260
261 return pixd;
262 }
263
264
265 /*---------------------------------------------------------------------*
266 * Interpolated bilinear image transformation *
267 *---------------------------------------------------------------------*/
268 /*!
269 * \brief pixBilinearPta()
270 *
271 * \param[in] pixs all depths; colormap ok
272 * \param[in] ptad 4 pts of final coordinate space
273 * \param[in] ptas 4 pts of initial coordinate space
274 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
275 * \return pixd, or NULL on error
276 *
277 * <pre>
278 * Notes:
279 * (1) Brings in either black or white pixels from the boundary
280 * (2) Removes any existing colormap, if necessary, before transforming
281 * </pre>
282 */
283 PIX *
284 pixBilinearPta(PIX *pixs,
285 PTA *ptad,
286 PTA *ptas,
287 l_int32 incolor)
288 {
289 l_int32 d;
290 l_uint32 colorval;
291 PIX *pixt1, *pixt2, *pixd;
292
293 if (!pixs)
294 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
295 if (!ptas)
296 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
297 if (!ptad)
298 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
299 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
300 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
301 if (ptaGetCount(ptas) != 4)
302 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL);
303 if (ptaGetCount(ptad) != 4)
304 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL);
305
306 if (pixGetDepth(pixs) == 1)
307 return pixBilinearSampledPta(pixs, ptad, ptas, incolor);
308
309 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
310 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
311 d = pixGetDepth(pixt1);
312 if (d < 8)
313 pixt2 = pixConvertTo8(pixt1, FALSE);
314 else
315 pixt2 = pixClone(pixt1);
316 d = pixGetDepth(pixt2);
317
318 /* Compute actual color to bring in from edges */
319 colorval = 0;
320 if (incolor == L_BRING_IN_WHITE) {
321 if (d == 8)
322 colorval = 255;
323 else /* d == 32 */
324 colorval = 0xffffff00;
325 }
326
327 if (d == 8)
328 pixd = pixBilinearPtaGray(pixt2, ptad, ptas, colorval);
329 else /* d == 32 */
330 pixd = pixBilinearPtaColor(pixt2, ptad, ptas, colorval);
331 pixDestroy(&pixt1);
332 pixDestroy(&pixt2);
333 return pixd;
334 }
335
336
337 /*!
338 * \brief pixBilinear()
339 *
340 * \param[in] pixs all depths; colormap ok
341 * \param[in] vc vector of 8 coefficients for bilinear transformation
342 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
343 * \return pixd, or NULL on error
344 *
345 * <pre>
346 * Notes:
347 * (1) Brings in either black or white pixels from the boundary
348 * (2) Removes any existing colormap, if necessary, before transforming
349 * </pre>
350 */
351 PIX *
352 pixBilinear(PIX *pixs,
353 l_float32 *vc,
354 l_int32 incolor)
355 {
356 l_int32 d;
357 l_uint32 colorval;
358 PIX *pixt1, *pixt2, *pixd;
359
360 if (!pixs)
361 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
362 if (!vc)
363 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
364
365 if (pixGetDepth(pixs) == 1)
366 return pixBilinearSampled(pixs, vc, incolor);
367
368 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
369 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
370 d = pixGetDepth(pixt1);
371 if (d < 8)
372 pixt2 = pixConvertTo8(pixt1, FALSE);
373 else
374 pixt2 = pixClone(pixt1);
375 d = pixGetDepth(pixt2);
376
377 /* Compute actual color to bring in from edges */
378 colorval = 0;
379 if (incolor == L_BRING_IN_WHITE) {
380 if (d == 8)
381 colorval = 255;
382 else /* d == 32 */
383 colorval = 0xffffff00;
384 }
385
386 if (d == 8)
387 pixd = pixBilinearGray(pixt2, vc, colorval);
388 else /* d == 32 */
389 pixd = pixBilinearColor(pixt2, vc, colorval);
390 pixDestroy(&pixt1);
391 pixDestroy(&pixt2);
392 return pixd;
393 }
394
395
396 /*!
397 * \brief pixBilinearPtaColor()
398 *
399 * \param[in] pixs 32 bpp
400 * \param[in] ptad 4 pts of final coordinate space
401 * \param[in] ptas 4 pts of initial coordinate space
402 * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
403 * \return pixd, or NULL on error
404 */
405 PIX *
406 pixBilinearPtaColor(PIX *pixs,
407 PTA *ptad,
408 PTA *ptas,
409 l_uint32 colorval)
410 {
411 l_float32 *vc;
412 PIX *pixd;
413
414 if (!pixs)
415 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
416 if (!ptas)
417 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
418 if (!ptad)
419 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
420 if (pixGetDepth(pixs) != 32)
421 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
422 if (ptaGetCount(ptas) != 4)
423 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL);
424 if (ptaGetCount(ptad) != 4)
425 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL);
426
427 /* Get backwards transform from dest to src, and apply it */
428 getBilinearXformCoeffs(ptad, ptas, &vc);
429 pixd = pixBilinearColor(pixs, vc, colorval);
430 LEPT_FREE(vc);
431
432 return pixd;
433 }
434
435
436 /*!
437 * \brief pixBilinearColor()
438 *
439 * \param[in] pixs 32 bpp
440 * \param[in] vc vector of 8 coefficients for bilinear transformation
441 * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
442 * \return pixd, or NULL on error
443 */
444 PIX *
445 pixBilinearColor(PIX *pixs,
446 l_float32 *vc,
447 l_uint32 colorval)
448 {
449 l_int32 i, j, w, h, d, wpls, wpld;
450 l_uint32 val;
451 l_uint32 *datas, *datad, *lined;
452 l_float32 x, y;
453 PIX *pix1, *pix2, *pixd;
454
455 if (!pixs)
456 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
457 pixGetDimensions(pixs, &w, &h, &d);
458 if (d != 32)
459 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
460 if (!vc)
461 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
462
463 datas = pixGetData(pixs);
464 wpls = pixGetWpl(pixs);
465 pixd = pixCreateTemplate(pixs);
466 pixSetAllArbitrary(pixd, colorval);
467 datad = pixGetData(pixd);
468 wpld = pixGetWpl(pixd);
469
470 /* Iterate over destination pixels */
471 for (i = 0; i < h; i++) {
472 lined = datad + i * wpld;
473 for (j = 0; j < w; j++) {
474 /* Compute float src pixel location corresponding to (i,j) */
475 bilinearXformPt(vc, j, i, &x, &y);
476 linearInterpolatePixelColor(datas, wpls, w, h, x, y, colorval,
477 &val);
478 *(lined + j) = val;
479 }
480 }
481
482 /* If rgba, transform the pixs alpha channel and insert in pixd */
483 if (pixGetSpp(pixs) == 4) {
484 pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
485 pix2 = pixBilinearGray(pix1, vc, 255); /* bring in opaque */
486 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
487 pixDestroy(&pix1);
488 pixDestroy(&pix2);
489 }
490
491 return pixd;
492 }
493
494
495 /*!
496 * \brief pixBilinearPtaGray()
497 *
498 * \param[in] pixs 8 bpp
499 * \param[in] ptad 4 pts of final coordinate space
500 * \param[in] ptas 4 pts of initial coordinate space
501 * \param[in] grayval e.g., 0 to bring in BLACK, 255 for WHITE
502 * \return pixd, or NULL on error
503 */
504 PIX *
505 pixBilinearPtaGray(PIX *pixs,
506 PTA *ptad,
507 PTA *ptas,
508 l_uint8 grayval)
509 {
510 l_float32 *vc;
511 PIX *pixd;
512
513 if (!pixs)
514 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
515 if (!ptas)
516 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
517 if (!ptad)
518 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
519 if (pixGetDepth(pixs) != 8)
520 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
521 if (ptaGetCount(ptas) != 4)
522 return (PIX *)ERROR_PTR("ptas count not 4", __func__, NULL);
523 if (ptaGetCount(ptad) != 4)
524 return (PIX *)ERROR_PTR("ptad count not 4", __func__, NULL);
525
526 /* Get backwards transform from dest to src, and apply it */
527 getBilinearXformCoeffs(ptad, ptas, &vc);
528 pixd = pixBilinearGray(pixs, vc, grayval);
529 LEPT_FREE(vc);
530
531 return pixd;
532 }
533
534
535 /*!
536 * \brief pixBilinearGray()
537 *
538 * \param[in] pixs 8 bpp
539 * \param[in] vc vector of 8 coefficients for bilinear transformation
540 * \param[in] grayval e.g., 0 to bring in BLACK, 255 for WHITE
541 * \return pixd, or NULL on error
542 */
543 PIX *
544 pixBilinearGray(PIX *pixs,
545 l_float32 *vc,
546 l_uint8 grayval)
547 {
548 l_int32 i, j, w, h, wpls, wpld, val;
549 l_uint32 *datas, *datad, *lined;
550 l_float32 x, y;
551 PIX *pixd;
552
553 if (!pixs)
554 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
555 pixGetDimensions(pixs, &w, &h, NULL);
556 if (pixGetDepth(pixs) != 8)
557 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
558 if (!vc)
559 return (PIX *)ERROR_PTR("vc not defined", __func__, NULL);
560
561 datas = pixGetData(pixs);
562 wpls = pixGetWpl(pixs);
563 pixd = pixCreateTemplate(pixs);
564 pixSetAllArbitrary(pixd, grayval);
565 datad = pixGetData(pixd);
566 wpld = pixGetWpl(pixd);
567
568 /* Iterate over destination pixels */
569 for (i = 0; i < h; i++) {
570 lined = datad + i * wpld;
571 for (j = 0; j < w; j++) {
572 /* Compute float src pixel location corresponding to (i,j) */
573 bilinearXformPt(vc, j, i, &x, &y);
574 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val);
575 SET_DATA_BYTE(lined, j, val);
576 }
577 }
578
579 return pixd;
580 }
581
582
583 /*-------------------------------------------------------------------------*
584 * Bilinear transform including alpha (blend) component *
585 *-------------------------------------------------------------------------*/
586 /*!
587 * \brief pixBilinearPtaWithAlpha()
588 *
589 * \param[in] pixs 32 bpp rgb
590 * \param[in] ptad 4 pts of final coordinate space
591 * \param[in] ptas 4 pts of initial coordinate space
592 * \param[in] pixg [optional] 8 bpp, can be null
593 * \param[in] fract between 0.0 and 1.0, with 0.0 fully transparent
594 * and 1.0 fully opaque
595 * \param[in] border of pixels added to capture transformed source pixels
596 * \return pixd, or NULL on error
597 *
598 * <pre>
599 * Notes:
600 * (1) The alpha channel is transformed separately from pixs,
601 * and aligns with it, being fully transparent outside the
602 * boundary of the transformed pixs. For pixels that are fully
603 * transparent, a blending function like pixBlendWithGrayMask()
604 * will give zero weight to corresponding pixels in pixs.
605 * (2) If %pixg is NULL, it is generated as an alpha layer that is
606 * partially opaque, using %fract. Otherwise, it is cropped
607 * to %pixs if required and %fract is ignored. The alpha channel
608 * in %pixs is never used.
609 * (3) Colormaps are removed.
610 * (4) When pixs is transformed, it doesn't matter what color is brought
611 * in because the alpha channel will be transparent (0) there.
612 * (5) To avoid losing source pixels in the destination, it may be
613 * necessary to add a border to the source pix before doing
614 * the bilinear transformation. This can be any non-negative number.
615 * (6) The input %ptad and %ptas are in a coordinate space before
616 * the border is added. Internally, we compensate for this
617 * before doing the bilinear transform on the image after
618 * the border is added.
619 * (7) The default setting for the border values in the alpha channel
620 * is 0 (transparent) for the outermost ring of pixels and
621 * (0.5 * fract * 255) for the second ring. When blended over
622 * a second image, this
623 * (a) shrinks the visible image to make a clean overlap edge
624 * with an image below, and
625 * (b) softens the edges by weakening the aliasing there.
626 * Use l_setAlphaMaskBorder() to change these values.
627 * </pre>
628 */
629 PIX *
630 pixBilinearPtaWithAlpha(PIX *pixs,
631 PTA *ptad,
632 PTA *ptas,
633 PIX *pixg,
634 l_float32 fract,
635 l_int32 border)
636 {
637 l_int32 ws, hs, d;
638 PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga;
639 PTA *ptad2, *ptas2;
640
641 if (!pixs)
642 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
643 pixGetDimensions(pixs, &ws, &hs, &d);
644 if (d != 32 && pixGetColormap(pixs) == NULL)
645 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL);
646 if (pixg && pixGetDepth(pixg) != 8) {
647 L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n",
648 __func__);
649 pixg = NULL;
650 }
651 if (!pixg && (fract < 0.0 || fract > 1.0)) {
652 L_WARNING("invalid fract; using 1.0 (fully transparent)\n", __func__);
653 fract = 1.0;
654 }
655 if (!pixg && fract == 0.0)
656 L_WARNING("fully opaque alpha; image cannot be blended\n", __func__);
657 if (!ptad)
658 return (PIX *)ERROR_PTR("ptad not defined", __func__, NULL);
659 if (!ptas)
660 return (PIX *)ERROR_PTR("ptas not defined", __func__, NULL);
661
662 /* Add border; the color doesn't matter */
663 pixb1 = pixAddBorder(pixs, border, 0);
664
665 /* Transform the ptr arrays to work on the bordered image */
666 ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0);
667 ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0);
668
669 /* Do separate bilinear transform of rgb channels of pixs and of pixg */
670 pixd = pixBilinearPtaColor(pixb1, ptad2, ptas2, 0);
671 if (!pixg) {
672 pixg2 = pixCreate(ws, hs, 8);
673 if (fract == 1.0)
674 pixSetAll(pixg2);
675 else
676 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract));
677 } else {
678 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs);
679 }
680 if (ws > 10 && hs > 10) { /* see note 7 */
681 pixSetBorderRingVal(pixg2, 1,
682 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0]));
683 pixSetBorderRingVal(pixg2, 2,
684 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1]));
685
686 }
687 pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */
688 pixga = pixBilinearPtaGray(pixb2, ptad2, ptas2, 0);
689 pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL);
690 pixSetSpp(pixd, 4);
691
692 pixDestroy(&pixg2);
693 pixDestroy(&pixb1);
694 pixDestroy(&pixb2);
695 pixDestroy(&pixga);
696 ptaDestroy(&ptad2);
697 ptaDestroy(&ptas2);
698 return pixd;
699 }
700
701
702 /*-------------------------------------------------------------*
703 * Bilinear coordinate transformation *
704 *-------------------------------------------------------------*/
705 /*!
706 * \brief getBilinearXformCoeffs()
707 *
708 * \param[in] ptas source 4 points; unprimed
709 * \param[in] ptad transformed 4 points; primed
710 * \param[out] pvc vector of coefficients of transform
711 * \return 0 if OK; 1 on error
712 *
713 * <pre>
714 * We have a set of 8 equations, describing the bilinear
715 * transformation that takes 4 points ptas into 4 other
716 * points ptad. These equations are:
717 *
718 * x1' = c[0]*x1 + c[1]*y1 + c[2]*x1*y1 + c[3]
719 * y1' = c[4]*x1 + c[5]*y1 + c[6]*x1*y1 + c[7]
720 * x2' = c[0]*x2 + c[1]*y2 + c[2]*x2*y2 + c[3]
721 * y2' = c[4]*x2 + c[5]*y2 + c[6]*x2*y2 + c[7]
722 * x3' = c[0]*x3 + c[1]*y3 + c[2]*x3*y3 + c[3]
723 * y3' = c[4]*x3 + c[5]*y3 + c[6]*x3*y3 + c[7]
724 * x4' = c[0]*x4 + c[1]*y4 + c[2]*x4*y4 + c[3]
725 * y4' = c[4]*x4 + c[5]*y4 + c[6]*x4*y4 + c[7]
726 *
727 * This can be represented as
728 *
729 * AC = B
730 *
731 * where B and C are column vectors
732 *
733 * B = [ x1' y1' x2' y2' x3' y3' x4' y4' ]
734 * C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ]
735 *
736 * and A is the 8x8 matrix
737 *
738 * x1 y1 x1*y1 1 0 0 0 0
739 * 0 0 0 0 x1 y1 x1*y1 1
740 * x2 y2 x2*y2 1 0 0 0 0
741 * 0 0 0 0 x2 y2 x2*y2 1
742 * x3 y3 x3*y3 1 0 0 0 0
743 * 0 0 0 0 x3 y3 x3*y3 1
744 * x4 y4 x4*y4 1 0 0 0 0
745 * 0 0 0 0 x4 y4 x4*y4 1
746 *
747 * These eight equations are solved here for the coefficients C.
748 *
749 * These eight coefficients can then be used to find the mapping
750 * x,y) --> (x',y':
751 *
752 * x' = c[0]x + c[1]y + c[2]xy + c[3]
753 * y' = c[4]x + c[5]y + c[6]xy + c[7]
754 *
755 * that are implemented in bilinearXformSampledPt and
756 * bilinearXFormPt.
757 * </pre>
758 */
759 l_ok
760 getBilinearXformCoeffs(PTA *ptas,
761 PTA *ptad,
762 l_float32 **pvc)
763 {
764 l_int32 i;
765 l_float32 x1, y1, x2, y2, x3, y3, x4, y4;
766 l_float32 *b; /* rhs vector of primed coords X'; coeffs returned in *pvc */
767 l_float32 *a[8]; /* 8x8 matrix A */
768
769 if (!ptas)
770 return ERROR_INT("ptas not defined", __func__, 1);
771 if (!ptad)
772 return ERROR_INT("ptad not defined", __func__, 1);
773 if (!pvc)
774 return ERROR_INT("&vc not defined", __func__, 1);
775
776 b = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32));
777 *pvc = b;
778 ptaGetPt(ptas, 0, &x1, &y1);
779 ptaGetPt(ptas, 1, &x2, &y2);
780 ptaGetPt(ptas, 2, &x3, &y3);
781 ptaGetPt(ptas, 3, &x4, &y4);
782 ptaGetPt(ptad, 0, &b[0], &b[1]);
783 ptaGetPt(ptad, 1, &b[2], &b[3]);
784 ptaGetPt(ptad, 2, &b[4], &b[5]);
785 ptaGetPt(ptad, 3, &b[6], &b[7]);
786
787 for (i = 0; i < 8; i++)
788 a[i] = (l_float32 *)LEPT_CALLOC(8, sizeof(l_float32));
789 a[0][0] = x1;
790 a[0][1] = y1;
791 a[0][2] = x1 * y1;
792 a[0][3] = 1.;
793 a[1][4] = x1;
794 a[1][5] = y1;
795 a[1][6] = x1 * y1;
796 a[1][7] = 1.;
797 a[2][0] = x2;
798 a[2][1] = y2;
799 a[2][2] = x2 * y2;
800 a[2][3] = 1.;
801 a[3][4] = x2;
802 a[3][5] = y2;
803 a[3][6] = x2 * y2;
804 a[3][7] = 1.;
805 a[4][0] = x3;
806 a[4][1] = y3;
807 a[4][2] = x3 * y3;
808 a[4][3] = 1.;
809 a[5][4] = x3;
810 a[5][5] = y3;
811 a[5][6] = x3 * y3;
812 a[5][7] = 1.;
813 a[6][0] = x4;
814 a[6][1] = y4;
815 a[6][2] = x4 * y4;
816 a[6][3] = 1.;
817 a[7][4] = x4;
818 a[7][5] = y4;
819 a[7][6] = x4 * y4;
820 a[7][7] = 1.;
821
822 gaussjordan(a, b, 8);
823
824 for (i = 0; i < 8; i++)
825 LEPT_FREE(a[i]);
826 return 0;
827 }
828
829
830 /*!
831 * \brief bilinearXformSampledPt()
832 *
833 * \param[in] vc vector of 8 coefficients
834 * \param[in] x, y initial point
835 * \param[out] pxp, pyp transformed point
836 * \return 0 if OK; 1 on error
837 *
838 * <pre>
839 * Notes:
840 * (1) This finds the nearest pixel coordinates of the transformed point.
841 * (2) It does not check ptrs for returned data!
842 * </pre>
843 */
844 l_ok
845 bilinearXformSampledPt(l_float32 *vc,
846 l_int32 x,
847 l_int32 y,
848 l_int32 *pxp,
849 l_int32 *pyp)
850 {
851
852 if (!vc)
853 return ERROR_INT("vc not defined", __func__, 1);
854
855 *pxp = (l_int32)(vc[0] * x + vc[1] * y + vc[2] * x * y + vc[3] + 0.5);
856 *pyp = (l_int32)(vc[4] * x + vc[5] * y + vc[6] * x * y + vc[7] + 0.5);
857 return 0;
858 }
859
860
861 /*!
862 * \brief bilinearXformPt()
863 *
864 * \param[in] vc vector of 8 coefficients
865 * \param[in] x, y initial point
866 * \param[out] pxp, pyp transformed point
867 * \return 0 if OK; 1 on error
868 *
869 * <pre>
870 * Notes:
871 * (1) This computes the floating point location of the transformed point.
872 * (2) It does not check ptrs for returned data!
873 * </pre>
874 */
875 l_ok
876 bilinearXformPt(l_float32 *vc,
877 l_int32 x,
878 l_int32 y,
879 l_float32 *pxp,
880 l_float32 *pyp)
881 {
882 if (!vc)
883 return ERROR_INT("vc not defined", __func__, 1);
884
885 *pxp = vc[0] * x + vc[1] * y + vc[2] * x * y + vc[3];
886 *pyp = vc[4] * x + vc[5] * y + vc[6] * x * y + vc[7];
887 return 0;
888 }