comparison mupdf-source/thirdparty/leptonica/src/shear.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 /*!
29 * \file shear.c
30 * <pre>
31 *
32 * About arbitrary lines
33 * PIX *pixHShear()
34 * PIX *pixVShear()
35 *
36 * About special 'points': UL corner and center
37 * PIX *pixHShearCorner()
38 * PIX *pixVShearCorner()
39 * PIX *pixHShearCenter()
40 * PIX *pixVShearCenter()
41 *
42 * In place about arbitrary lines
43 * l_int32 pixHShearIP()
44 * l_int32 pixVShearIP()
45 *
46 * Linear interpolated shear about arbitrary lines
47 * PIX *pixHShearLI()
48 * PIX *pixVShearLI()
49 *
50 * Static helper
51 * static l_float32 normalizeAngleForShear()
52 * </pre>
53 */
54
55 #ifdef HAVE_CONFIG_H
56 #include <config_auto.h>
57 #endif /* HAVE_CONFIG_H */
58
59 #include <string.h>
60 #include <math.h>
61 #include "allheaders.h"
62
63 /* Shear angle must not get too close to -pi/2 or pi/2 */
64 static const l_float32 MinDiffFromHalfPi = 0.04f;
65
66 static l_float32 normalizeAngleForShear(l_float32 radang, l_float32 mindif);
67
68
69 #ifndef NO_CONSOLE_IO
70 #define DEBUG 0
71 #endif /* ~NO_CONSOLE_IO */
72
73
74 /*-------------------------------------------------------------*
75 * About arbitrary lines *
76 *-------------------------------------------------------------*/
77 /*!
78 * \brief pixHShear()
79 *
80 * \param[in] pixd [optional] this can be null, equal to pixs,
81 * or different from pixs
82 * \param[in] pixs any depth; cmap ok
83 * \param[in] yloc location of horizontal line, measured from origin
84 * \param[in] radang angle in radians
85 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
86 * \return pixd, always
87 *
88 * <pre>
89 * Notes:
90 * (1) There are 3 cases:
91 * (a) pixd == null (make a new pixd)
92 * (b) pixd == pixs (in-place)
93 * (c) pixd != pixs
94 * (2) For these three cases, use these patterns, respectively:
95 * pixd = pixHShear(NULL, pixs, ...);
96 * pixHShear(pixs, pixs, ...);
97 * pixHShear(pixd, pixs, ...);
98 * (3) This shear leaves the horizontal line of pixels at y = yloc
99 * invariant. For a positive shear angle, pixels above this
100 * line are shoved to the right, and pixels below this line
101 * move to the left.
102 * (4) With positive shear angle, this can be used, along with
103 * pixVShear(), to perform a cw rotation, either with 2 shears
104 * (for small angles) or in the general case with 3 shears.
105 * (5) Changing the value of yloc is equivalent to translating
106 * the result horizontally.
107 * (6) This brings in %incolor pixels from outside the image.
108 * (7) In-place shears do not work on cmapped pix, because the
109 * in-place operation cannot initialize to the requested %incolor,
110 * so we shear from a copy.
111 * (8) The angle is brought into the range [-pi, -pi]. It is
112 * not permitted to be within MinDiffFromHalfPi radians
113 * from either -pi/2 or pi/2.
114 * </pre>
115 */
116 PIX *
117 pixHShear(PIX *pixd,
118 PIX *pixs,
119 l_int32 yloc,
120 l_float32 radang,
121 l_int32 incolor)
122 {
123 l_int32 sign, w, h;
124 l_int32 y, yincr, inityincr, hshift;
125 l_float32 tanangle, invangle;
126
127 if (!pixs)
128 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
129 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
130 return (PIX *)ERROR_PTR("invalid incolor value", __func__, pixd);
131
132 if (pixd == pixs) { /* in place */
133 if (!pixGetColormap(pixs)) {
134 pixHShearIP(pixd, yloc, radang, incolor);
135 } else { /* can't do in-place with a colormap */
136 PIX *pix1 = pixCopy(NULL, pixs);
137 pixHShear(pixd, pix1, yloc, radang, incolor);
138 pixDestroy(&pix1);
139 }
140 return pixd;
141 }
142
143 /* Make sure pixd exists and is same size as pixs */
144 if (!pixd) {
145 if ((pixd = pixCreateTemplate(pixs)) == NULL)
146 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
147 } else { /* pixd != pixs */
148 pixResizeImageData(pixd, pixs);
149 }
150
151 /* Normalize angle. If no rotation, return a copy */
152 radang = normalizeAngleForShear(radang, MinDiffFromHalfPi);
153 if (radang == 0.0 || tan(radang) == 0.0)
154 return pixCopy(pixd, pixs);
155
156 /* Initialize to value of incoming pixels */
157 pixSetBlackOrWhite(pixd, incolor);
158
159 pixGetDimensions(pixs, &w, &h, NULL);
160 sign = L_SIGN(radang);
161 tanangle = tan(radang);
162 invangle = L_ABS(1. / tanangle);
163 inityincr = (l_int32)(invangle / 2.);
164 yincr = (l_int32)invangle;
165 pixRasterop(pixd, 0, yloc - inityincr, w, 2 * inityincr, PIX_SRC,
166 pixs, 0, yloc - inityincr);
167
168 for (hshift = 1, y = yloc + inityincr; y < h; hshift++) {
169 yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc);
170 if (h - y < yincr) /* reduce for last one if req'd */
171 yincr = h - y;
172 pixRasterop(pixd, -sign*hshift, y, w, yincr, PIX_SRC, pixs, 0, y);
173 #if DEBUG
174 lept_stderr("y = %d, hshift = %d, yincr = %d\n", y, hshift, yincr);
175 #endif /* DEBUG */
176 y += yincr;
177 }
178
179 for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) {
180 yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5);
181 if (y < yincr) /* reduce for last one if req'd */
182 yincr = y;
183 pixRasterop(pixd, -sign*hshift, y - yincr, w, yincr, PIX_SRC,
184 pixs, 0, y - yincr);
185 #if DEBUG
186 lept_stderr("y = %d, hshift = %d, yincr = %d\n",
187 y - yincr, hshift, yincr);
188 #endif /* DEBUG */
189 y -= yincr;
190 }
191
192 return pixd;
193 }
194
195
196 /*!
197 * \brief pixVShear()
198 *
199 * \param[in] pixd [optional], this can be null, equal to pixs,
200 * or different from pixs
201 * \param[in] pixs any depth; cmap ok
202 * \param[in] xloc location of vertical line, measured from origin
203 * \param[in] radang angle in radians; not too close to +-(pi / 2)
204 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
205 * \return pixd, or NULL on error
206 *
207 * <pre>
208 * Notes:
209 * (1) There are 3 cases:
210 * (a) pixd == null (make a new pixd)
211 * (b) pixd == pixs (in-place)
212 * (c) pixd != pixs
213 * (2) For these three cases, use these patterns, respectively:
214 * pixd = pixVShear(NULL, pixs, ...);
215 * pixVShear(pixs, pixs, ...);
216 * pixVShear(pixd, pixs, ...);
217 * (3) This shear leaves the vertical line of pixels at x = xloc
218 * invariant. For a positive shear angle, pixels to the right
219 * of this line are shoved downward, and pixels to the left
220 * of the line move upward.
221 * (4) With positive shear angle, this can be used, along with
222 * pixHShear(), to perform a cw rotation, either with 2 shears
223 * (for small angles) or in the general case with 3 shears.
224 * (5) Changing the value of xloc is equivalent to translating
225 * the result vertically.
226 * (6) This brings in %incolor pixels from outside the image.
227 * (7) In-place shears do not work on cmapped pix, because the
228 * in-place operation cannot initialize to the requested %incolor,
229 * so we shear from a copy.
230 * (8) The angle is brought into the range [-pi, -pi]. It is
231 * not permitted to be within MinDiffFromHalfPi radians
232 * from either -pi/2 or pi/2.
233 * </pre>
234 */
235 PIX *
236 pixVShear(PIX *pixd,
237 PIX *pixs,
238 l_int32 xloc,
239 l_float32 radang,
240 l_int32 incolor)
241 {
242 l_int32 sign, w, h;
243 l_int32 x, xincr, initxincr, vshift;
244 l_float32 tanangle, invangle;
245
246 if (!pixs)
247 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
248 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
249 return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL);
250
251 if (pixd == pixs) { /* in place */
252 if (!pixGetColormap(pixs)) {
253 pixVShearIP(pixd, xloc, radang, incolor);
254 } else { /* can't do in-place with a colormap */
255 PIX *pix1 = pixCopy(NULL, pixs);
256 pixVShear(pixd, pix1, xloc, radang, incolor);
257 pixDestroy(&pix1);
258 }
259 return pixd;
260 }
261
262 /* Make sure pixd exists and is same size as pixs */
263 if (!pixd) {
264 if ((pixd = pixCreateTemplate(pixs)) == NULL)
265 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
266 } else { /* pixd != pixs */
267 pixResizeImageData(pixd, pixs);
268 }
269
270 /* Normalize angle. If no rotation, return a copy */
271 radang = normalizeAngleForShear(radang, MinDiffFromHalfPi);
272 if (radang == 0.0 || tan(radang) == 0.0)
273 return pixCopy(pixd, pixs);
274
275 /* Initialize to value of incoming pixels */
276 pixSetBlackOrWhite(pixd, incolor);
277
278 pixGetDimensions(pixs, &w, &h, NULL);
279 sign = L_SIGN(radang);
280 tanangle = tan(radang);
281 invangle = L_ABS(1. / tanangle);
282 initxincr = (l_int32)(invangle / 2.);
283 xincr = (l_int32)invangle;
284 pixRasterop(pixd, xloc - initxincr, 0, 2 * initxincr, h, PIX_SRC,
285 pixs, xloc - initxincr, 0);
286
287 for (vshift = 1, x = xloc + initxincr; x < w; vshift++) {
288 xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc);
289 if (w - x < xincr) /* reduce for last one if req'd */
290 xincr = w - x;
291 pixRasterop(pixd, x, sign*vshift, xincr, h, PIX_SRC, pixs, x, 0);
292 #if DEBUG
293 lept_stderr("x = %d, vshift = %d, xincr = %d\n", x, vshift, xincr);
294 #endif /* DEBUG */
295 x += xincr;
296 }
297
298 for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) {
299 xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5);
300 if (x < xincr) /* reduce for last one if req'd */
301 xincr = x;
302 pixRasterop(pixd, x - xincr, sign*vshift, xincr, h, PIX_SRC,
303 pixs, x - xincr, 0);
304 #if DEBUG
305 lept_stderr("x = %d, vshift = %d, xincr = %d\n",
306 x - xincr, vshift, xincr);
307 #endif /* DEBUG */
308 x -= xincr;
309 }
310
311 return pixd;
312 }
313
314
315
316 /*-------------------------------------------------------------*
317 * Shears about UL corner and center *
318 *-------------------------------------------------------------*/
319 /*!
320 * \brief pixHShearCorner()
321 *
322 * \param[in] pixd [optional], if not null, must be equal to pixs
323 * \param[in] pixs any depth
324 * \param[in] radang angle in radians
325 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
326 * \return pixd, or NULL on error.
327 *
328 * <pre>
329 * Notes:
330 * (1) See pixHShear() for usage.
331 * (2) This does a horizontal shear about the UL corner, with (+) shear
332 * pushing increasingly leftward (-x) with increasing y.
333 * </pre>
334 */
335 PIX *
336 pixHShearCorner(PIX *pixd,
337 PIX *pixs,
338 l_float32 radang,
339 l_int32 incolor)
340 {
341 if (!pixs)
342 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
343
344 return pixHShear(pixd, pixs, 0, radang, incolor);
345 }
346
347
348 /*!
349 * \brief pixVShearCorner()
350 *
351 * \param[in] pixd [optional], if not null, must be equal to pixs
352 * \param[in] pixs any depth
353 * \param[in] radang angle in radians
354 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
355 * \return pixd, or NULL on error.
356 *
357 * <pre>
358 * Notes:
359 * (1) See pixVShear() for usage.
360 * (2) This does a vertical shear about the UL corner, with (+) shear
361 * pushing increasingly downward (+y) with increasing x.
362 * </pre>
363 */
364 PIX *
365 pixVShearCorner(PIX *pixd,
366 PIX *pixs,
367 l_float32 radang,
368 l_int32 incolor)
369 {
370 if (!pixs)
371 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
372
373 return pixVShear(pixd, pixs, 0, radang, incolor);
374 }
375
376
377 /*!
378 * \brief pixHShearCenter()
379 *
380 * \param[in] pixd [optional] if not null, must be equal to pixs
381 * \param[in] pixs any depth
382 * \param[in] radang angle in radians
383 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
384 * \return pixd, or NULL on error.
385 *
386 * <pre>
387 * Notes:
388 * (1) See pixHShear() for usage.
389 * (2) This does a horizontal shear about the center, with (+) shear
390 * pushing increasingly leftward (-x) with increasing y.
391 * </pre>
392 */
393 PIX *
394 pixHShearCenter(PIX *pixd,
395 PIX *pixs,
396 l_float32 radang,
397 l_int32 incolor)
398 {
399 if (!pixs)
400 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
401
402 return pixHShear(pixd, pixs, pixGetHeight(pixs) / 2, radang, incolor);
403 }
404
405
406 /*!
407 * \brief pixVShearCenter()
408 *
409 * \param[in] pixd [optional] if not null, must be equal to pixs
410 * \param[in] pixs any depth
411 * \param[in] radang angle in radians
412 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
413 * \return pixd, or NULL on error.
414 *
415 * <pre>
416 * Notes:
417 * (1) See pixVShear() for usage.
418 * (2) This does a vertical shear about the center, with (+) shear
419 * pushing increasingly downward (+y) with increasing x.
420 * </pre>
421 */
422 PIX *
423 pixVShearCenter(PIX *pixd,
424 PIX *pixs,
425 l_float32 radang,
426 l_int32 incolor)
427 {
428 if (!pixs)
429 return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd);
430
431 return pixVShear(pixd, pixs, pixGetWidth(pixs) / 2, radang, incolor);
432 }
433
434
435
436 /*--------------------------------------------------------------------------*
437 * In place about arbitrary lines *
438 *--------------------------------------------------------------------------*/
439 /*!
440 * \brief pixHShearIP()
441 *
442 * \param[in] pixs any depth; no cmap
443 * \param[in] yloc location of horizontal line, measured from origin
444 * \param[in] radang angle in radians
445 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
446 * \return 0 if OK; 1 on error
447 *
448 * <pre>
449 * Notes:
450 * (1) This is an in-place version of pixHShear(); see comments there.
451 * (2) This brings in 'incolor' pixels from outside the image.
452 * (3) pixs cannot be colormapped, because the in-place operation
453 * only blits in 0 or 1 bits, not an arbitrary colormap index.
454 * (4) Does a horizontal full-band shear about the line with (+) shear
455 * pushing increasingly leftward (-x) with increasing y.
456 * </pre>
457 */
458 l_ok
459 pixHShearIP(PIX *pixs,
460 l_int32 yloc,
461 l_float32 radang,
462 l_int32 incolor)
463 {
464 l_int32 sign, w, h;
465 l_int32 y, yincr, inityincr, hshift;
466 l_float32 tanangle, invangle;
467
468 if (!pixs)
469 return ERROR_INT("pixs not defined", __func__, 1);
470 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
471 return ERROR_INT("invalid incolor value", __func__, 1);
472 if (pixGetColormap(pixs))
473 return ERROR_INT("pixs is colormapped", __func__, 1);
474
475 /* Normalize angle */
476 radang = normalizeAngleForShear(radang, MinDiffFromHalfPi);
477 if (radang == 0.0 || tan(radang) == 0.0)
478 return 0;
479
480 sign = L_SIGN(radang);
481 pixGetDimensions(pixs, &w, &h, NULL);
482 tanangle = tan(radang);
483 invangle = L_ABS(1. / tanangle);
484 inityincr = (l_int32)(invangle / 2.);
485 yincr = (l_int32)invangle;
486
487 if (inityincr > 0)
488 pixRasteropHip(pixs, yloc - inityincr, 2 * inityincr, 0, incolor);
489
490 for (hshift = 1, y = yloc + inityincr; y < h; hshift++) {
491 yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc);
492 if (yincr == 0) continue;
493 if (h - y < yincr) /* reduce for last one if req'd */
494 yincr = h - y;
495 pixRasteropHip(pixs, y, yincr, -sign*hshift, incolor);
496 y += yincr;
497 }
498
499 for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) {
500 yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5);
501 if (yincr == 0) continue;
502 if (y < yincr) /* reduce for last one if req'd */
503 yincr = y;
504 pixRasteropHip(pixs, y - yincr, yincr, -sign*hshift, incolor);
505 y -= yincr;
506 }
507
508 return 0;
509 }
510
511
512 /*!
513 * \brief pixVShearIP()
514 *
515 * \param[in] pixs any depth; no cmap
516 * \param[in] xloc location of vertical line, measured from origin
517 * \param[in] radang angle in radians
518 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
519 * \return 0 if OK; 1 on error
520 *
521 * <pre>
522 * Notes:
523 * (1) This is an in-place version of pixVShear(); see comments there.
524 * (2) This brings in 'incolor' pixels from outside the image.
525 * (3) pixs cannot be colormapped, because the in-place operation
526 * only blits in 0 or 1 bits, not an arbitrary colormap index.
527 * (4) Does a vertical full-band shear about the line with (+) shear
528 * pushing increasingly downward (+y) with increasing x.
529 * </pre>
530 */
531 l_ok
532 pixVShearIP(PIX *pixs,
533 l_int32 xloc,
534 l_float32 radang,
535 l_int32 incolor)
536 {
537 l_int32 sign, w, h;
538 l_int32 x, xincr, initxincr, vshift;
539 l_float32 tanangle, invangle;
540
541 if (!pixs)
542 return ERROR_INT("pixs not defined", __func__, 1);
543 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
544 return ERROR_INT("invalid incolor value", __func__, 1);
545 if (pixGetColormap(pixs))
546 return ERROR_INT("pixs is colormapped", __func__, 1);
547
548 /* Normalize angle */
549 radang = normalizeAngleForShear(radang, MinDiffFromHalfPi);
550 if (radang == 0.0 || tan(radang) == 0.0)
551 return 0;
552
553 sign = L_SIGN(radang);
554 pixGetDimensions(pixs, &w, &h, NULL);
555 tanangle = tan(radang);
556 invangle = L_ABS(1. / tanangle);
557 initxincr = (l_int32)(invangle / 2.);
558 xincr = (l_int32)invangle;
559
560 if (initxincr > 0)
561 pixRasteropVip(pixs, xloc - initxincr, 2 * initxincr, 0, incolor);
562
563 for (vshift = 1, x = xloc + initxincr; x < w; vshift++) {
564 xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc);
565 if (xincr == 0) continue;
566 if (w - x < xincr) /* reduce for last one if req'd */
567 xincr = w - x;
568 pixRasteropVip(pixs, x, xincr, sign*vshift, incolor);
569 x += xincr;
570 }
571
572 for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) {
573 xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5);
574 if (xincr == 0) continue;
575 if (x < xincr) /* reduce for last one if req'd */
576 xincr = x;
577 pixRasteropVip(pixs, x - xincr, xincr, sign*vshift, incolor);
578 x -= xincr;
579 }
580
581 return 0;
582 }
583
584
585 /*-------------------------------------------------------------------------*
586 * Linear interpolated shear about arbitrary lines *
587 *-------------------------------------------------------------------------*/
588 /*!
589 * \brief pixHShearLI()
590 *
591 * \param[in] pixs 8 bpp or 32 bpp, or colormapped
592 * \param[in] yloc location of horizontal line, measured from origin
593 * \param[in] radang angle in radians, in range (-pi/2 ... pi/2)
594 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
595 * \return pixd sheared, or NULL on error
596 *
597 * <pre>
598 * Notes:
599 * (1) This does horizontal shear with linear interpolation for
600 * accurate results on 8 bpp gray, 32 bpp rgb, or cmapped images.
601 * It is relatively slow compared to the sampled version
602 * implemented by rasterop, but the result is much smoother.
603 * (2) This shear leaves the horizontal line of pixels at y = yloc
604 * invariant. For a positive shear angle, pixels above this
605 * line are shoved to the right, and pixels below this line
606 * move to the left.
607 * (3) Any colormap is removed.
608 * (4) The angle is brought into the range [-pi/2 + del, pi/2 - del],
609 * where del == MinDiffFromHalfPi.
610 * </pre>
611 */
612 PIX *
613 pixHShearLI(PIX *pixs,
614 l_int32 yloc,
615 l_float32 radang,
616 l_int32 incolor)
617 {
618 l_int32 i, jd, x, xp, xf, w, h, d, wm, wpls, wpld, val, rval, gval, bval;
619 l_uint32 word0, word1;
620 l_uint32 *datas, *datad, *lines, *lined;
621 l_float32 tanangle, xshift;
622 PIX *pix, *pixd;
623
624 if (!pixs)
625 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
626 pixGetDimensions(pixs, &w, &h, &d);
627 if (d != 8 && d != 32 && !pixGetColormap(pixs))
628 return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL);
629 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
630 return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL);
631 if (yloc < 0 || yloc >= h)
632 return (PIX *)ERROR_PTR("yloc not in [0 ... h-1]", __func__, NULL);
633
634 if (pixGetColormap(pixs))
635 pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
636 else
637 pix = pixClone(pixs);
638
639 /* Normalize angle. If no rotation, return a copy */
640 radang = normalizeAngleForShear(radang, MinDiffFromHalfPi);
641 if (radang == 0.0 || tan(radang) == 0.0) {
642 pixDestroy(&pix);
643 return pixCopy(NULL, pixs);
644 }
645
646 /* Initialize to value of incoming pixels */
647 pixd = pixCreateTemplate(pix);
648 pixSetBlackOrWhite(pixd, incolor);
649
650 /* Standard linear interp: subdivide each pixel into 64 parts */
651 d = pixGetDepth(pixd); /* 8 or 32 */
652 datas = pixGetData(pix);
653 datad = pixGetData(pixd);
654 wpls = pixGetWpl(pix);
655 wpld = pixGetWpl(pixd);
656 tanangle = tan(radang);
657 for (i = 0; i < h; i++) {
658 lines = datas + i * wpls;
659 lined = datad + i * wpld;
660 xshift = (yloc - i) * tanangle;
661 for (jd = 0; jd < w; jd++) {
662 x = (l_int32)(64.0 * (-xshift + jd) + 0.5);
663 xp = x / 64;
664 xf = x & 63;
665 wm = w - 1;
666 if (xp < 0 || xp > wm) continue;
667 if (d == 8) {
668 if (xp < wm) {
669 val = ((63 - xf) * GET_DATA_BYTE(lines, xp) +
670 xf * GET_DATA_BYTE(lines, xp + 1) + 31) / 63;
671 } else { /* xp == wm */
672 val = GET_DATA_BYTE(lines, xp);
673 }
674 SET_DATA_BYTE(lined, jd, val);
675 } else { /* d == 32 */
676 if (xp < wm) {
677 word0 = *(lines + xp);
678 word1 = *(lines + xp + 1);
679 rval = ((63 - xf) * ((word0 >> L_RED_SHIFT) & 0xff) +
680 xf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63;
681 gval = ((63 - xf) * ((word0 >> L_GREEN_SHIFT) & 0xff) +
682 xf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63;
683 bval = ((63 - xf) * ((word0 >> L_BLUE_SHIFT) & 0xff) +
684 xf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63;
685 composeRGBPixel(rval, gval, bval, lined + jd);
686 } else { /* xp == wm */
687 lined[jd] = lines[xp];
688 }
689 }
690 }
691 }
692
693 pixDestroy(&pix);
694 return pixd;
695 }
696
697
698 /*!
699 * \brief pixVShearLI()
700 *
701 * \param[in] pixs 8 bpp or 32 bpp, or colormapped
702 * \param[in] xloc location of vertical line, measured from origin
703 * \param[in] radang angle in radians, in range (-pi/2 ... pi/2)
704 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK;
705 * \return pixd sheared, or NULL on error
706 *
707 * <pre>
708 * Notes:
709 * (1) This does vertical shear with linear interpolation for
710 * accurate results on 8 bpp gray, 32 bpp rgb, or cmapped images.
711 * It is relatively slow compared to the sampled version
712 * implemented by rasterop, but the result is much smoother.
713 * (2) This shear leaves the vertical line of pixels at x = xloc
714 * invariant. For a positive shear angle, pixels to the right
715 * of this line are shoved downward, and pixels to the left
716 * of the line move upward.
717 * (3) Any colormap is removed.
718 * (4) The angle is brought into the range [-pi/2 + del, pi/2 - del],
719 * where del == MinDiffFromHalfPi.
720 * </pre>
721 */
722 PIX *
723 pixVShearLI(PIX *pixs,
724 l_int32 xloc,
725 l_float32 radang,
726 l_int32 incolor)
727 {
728 l_int32 id, y, yp, yf, j, w, h, d, hm, wpls, wpld, val, rval, gval, bval;
729 l_uint32 word0, word1;
730 l_uint32 *datas, *datad, *lines, *lined;
731 l_float32 tanangle, yshift;
732 PIX *pix, *pixd;
733
734 if (!pixs)
735 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
736 pixGetDimensions(pixs, &w, &h, &d);
737 if (d != 8 && d != 32 && !pixGetColormap(pixs))
738 return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL);
739 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
740 return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL);
741 if (xloc < 0 || xloc >= w)
742 return (PIX *)ERROR_PTR("xloc not in [0 ... w-1]", __func__, NULL);
743
744 if (pixGetColormap(pixs))
745 pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
746 else
747 pix = pixClone(pixs);
748
749 /* Normalize angle. If no rotation, return a copy */
750 radang = normalizeAngleForShear(radang, MinDiffFromHalfPi);
751 if (radang == 0.0 || tan(radang) == 0.0) {
752 pixDestroy(&pix);
753 return pixCopy(NULL, pixs);
754 }
755
756 /* Initialize to value of incoming pixels */
757 pixd = pixCreateTemplate(pix);
758 pixSetBlackOrWhite(pixd, incolor);
759
760 /* Standard linear interp: subdivide each pixel into 64 parts */
761 d = pixGetDepth(pixd); /* 8 or 32 */
762 datas = pixGetData(pix);
763 datad = pixGetData(pixd);
764 wpls = pixGetWpl(pix);
765 wpld = pixGetWpl(pixd);
766 tanangle = tan(radang);
767 for (j = 0; j < w; j++) {
768 yshift = (j - xloc) * tanangle;
769 for (id = 0; id < h; id++) {
770 y = (l_int32)(64.0 * (-yshift + id) + 0.5);
771 yp = y / 64;
772 yf = y & 63;
773 hm = h - 1;
774 if (yp < 0 || yp > hm) continue;
775 lines = datas + yp * wpls;
776 lined = datad + id * wpld;
777 if (d == 8) {
778 if (yp < hm) {
779 val = ((63 - yf) * GET_DATA_BYTE(lines, j) +
780 yf * GET_DATA_BYTE(lines + wpls, j) + 31) / 63;
781 } else { /* yp == hm */
782 val = GET_DATA_BYTE(lines, j);
783 }
784 SET_DATA_BYTE(lined, j, val);
785 } else { /* d == 32 */
786 if (yp < hm) {
787 word0 = *(lines + j);
788 word1 = *(lines + wpls + j);
789 rval = ((63 - yf) * ((word0 >> L_RED_SHIFT) & 0xff) +
790 yf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63;
791 gval = ((63 - yf) * ((word0 >> L_GREEN_SHIFT) & 0xff) +
792 yf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63;
793 bval = ((63 - yf) * ((word0 >> L_BLUE_SHIFT) & 0xff) +
794 yf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63;
795 composeRGBPixel(rval, gval, bval, lined + j);
796 } else { /* yp == hm */
797 lined[j] = lines[j];
798 }
799 }
800 }
801 }
802
803 pixDestroy(&pix);
804 return pixd;
805 }
806
807
808 /*-------------------------------------------------------------------------*
809 * Angle normalization *
810 *-------------------------------------------------------------------------*/
811 static l_float32
812 normalizeAngleForShear(l_float32 radang,
813 l_float32 mindif)
814 {
815 l_float32 pi2;
816
817 /* Bring angle into range [-pi/2, pi/2] */
818 pi2 = 3.14159265f / 2.0f;
819 if (radang < -pi2 || radang > pi2)
820 radang = radang - (l_int32)(radang / pi2) * pi2;
821
822 /* If angle is too close to pi/2 or -pi/2, move it */
823 if (radang > pi2 - mindif) {
824 L_WARNING("angle close to pi/2; shifting away\n", __func__);
825 radang = pi2 - mindif;
826 } else if (radang < -pi2 + mindif) {
827 L_WARNING("angle close to -pi/2; shifting away\n", __func__);
828 radang = -pi2 + mindif;
829 }
830
831 return radang;
832 }