/src/leptonica/src/shear.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*====================================================================* |
2 | | - Copyright (C) 2001 Leptonica. All rights reserved. |
3 | | - |
4 | | - Redistribution and use in source and binary forms, with or without |
5 | | - modification, are permitted provided that the following conditions |
6 | | - are met: |
7 | | - 1. Redistributions of source code must retain the above copyright |
8 | | - notice, this list of conditions and the following disclaimer. |
9 | | - 2. Redistributions in binary form must reproduce the above |
10 | | - copyright notice, this list of conditions and the following |
11 | | - disclaimer in the documentation and/or other materials |
12 | | - provided with the distribution. |
13 | | - |
14 | | - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
15 | | - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
16 | | - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
17 | | - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY |
18 | | - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
19 | | - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
20 | | - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
21 | | - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
22 | | - OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
23 | | - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
24 | | - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
25 | | *====================================================================*/ |
26 | | |
27 | | |
28 | | /*! |
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 | 131 | { |
123 | 131 | l_int32 sign, w, h; |
124 | 131 | l_int32 y, yincr, inityincr, hshift; |
125 | 131 | l_float32 tanangle, invangle; |
126 | | |
127 | 131 | if (!pixs) |
128 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
129 | 131 | if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) |
130 | 0 | return (PIX *)ERROR_PTR("invalid incolor value", __func__, pixd); |
131 | | |
132 | 131 | if (pixd == pixs) { /* in place */ |
133 | 0 | if (!pixGetColormap(pixs)) { |
134 | 0 | pixHShearIP(pixd, yloc, radang, incolor); |
135 | 0 | } else { /* can't do in-place with a colormap */ |
136 | 0 | PIX *pix1 = pixCopy(NULL, pixs); |
137 | 0 | pixHShear(pixd, pix1, yloc, radang, incolor); |
138 | 0 | pixDestroy(&pix1); |
139 | 0 | } |
140 | 0 | return pixd; |
141 | 0 | } |
142 | | |
143 | | /* Make sure pixd exists and is same size as pixs */ |
144 | 131 | if (!pixd) { |
145 | 131 | if ((pixd = pixCreateTemplate(pixs)) == NULL) |
146 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
147 | 131 | } else { /* pixd != pixs */ |
148 | 0 | pixResizeImageData(pixd, pixs); |
149 | 0 | } |
150 | | |
151 | | /* Normalize angle. If no rotation, return a copy */ |
152 | 131 | radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); |
153 | 131 | if (radang == 0.0 || tan(radang) == 0.0) |
154 | 0 | return pixCopy(pixd, pixs); |
155 | | |
156 | | /* Initialize to value of incoming pixels */ |
157 | 131 | pixSetBlackOrWhite(pixd, incolor); |
158 | | |
159 | 131 | pixGetDimensions(pixs, &w, &h, NULL); |
160 | 131 | sign = L_SIGN(radang); |
161 | 131 | tanangle = tan(radang); |
162 | 131 | invangle = L_ABS(1. / tanangle); |
163 | 131 | inityincr = (l_int32)(invangle / 2.); |
164 | 131 | yincr = (l_int32)invangle; |
165 | 131 | pixRasterop(pixd, 0, yloc - inityincr, w, 2 * inityincr, PIX_SRC, |
166 | 131 | pixs, 0, yloc - inityincr); |
167 | | |
168 | 240 | for (hshift = 1, y = yloc + inityincr; y < h; hshift++) { |
169 | 109 | yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc); |
170 | 109 | if (h - y < yincr) /* reduce for last one if req'd */ |
171 | 25 | yincr = h - y; |
172 | 109 | 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 | 109 | y += yincr; |
177 | 109 | } |
178 | | |
179 | 240 | for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) { |
180 | 109 | yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5); |
181 | 109 | if (y < yincr) /* reduce for last one if req'd */ |
182 | 25 | yincr = y; |
183 | 109 | pixRasterop(pixd, -sign*hshift, y - yincr, w, yincr, PIX_SRC, |
184 | 109 | 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 | 109 | y -= yincr; |
190 | 109 | } |
191 | | |
192 | 131 | return pixd; |
193 | 131 | } |
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 | 88.0k | { |
242 | 88.0k | l_int32 sign, w, h; |
243 | 88.0k | l_int32 x, xincr, initxincr, vshift; |
244 | 88.0k | l_float32 tanangle, invangle; |
245 | | |
246 | 88.0k | if (!pixs) |
247 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
248 | 88.0k | if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) |
249 | 0 | return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); |
250 | | |
251 | 88.0k | if (pixd == pixs) { /* in place */ |
252 | 0 | if (!pixGetColormap(pixs)) { |
253 | 0 | pixVShearIP(pixd, xloc, radang, incolor); |
254 | 0 | } else { /* can't do in-place with a colormap */ |
255 | 0 | PIX *pix1 = pixCopy(NULL, pixs); |
256 | 0 | pixVShear(pixd, pix1, xloc, radang, incolor); |
257 | 0 | pixDestroy(&pix1); |
258 | 0 | } |
259 | 0 | return pixd; |
260 | 0 | } |
261 | | |
262 | | /* Make sure pixd exists and is same size as pixs */ |
263 | 88.0k | if (!pixd) { |
264 | 131 | if ((pixd = pixCreateTemplate(pixs)) == NULL) |
265 | 0 | return (PIX *)ERROR_PTR("pixd not made", __func__, NULL); |
266 | 87.9k | } else { /* pixd != pixs */ |
267 | 87.9k | pixResizeImageData(pixd, pixs); |
268 | 87.9k | } |
269 | | |
270 | | /* Normalize angle. If no rotation, return a copy */ |
271 | 88.0k | radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); |
272 | 88.0k | if (radang == 0.0 || tan(radang) == 0.0) |
273 | 4.12k | return pixCopy(pixd, pixs); |
274 | | |
275 | | /* Initialize to value of incoming pixels */ |
276 | 83.9k | pixSetBlackOrWhite(pixd, incolor); |
277 | | |
278 | 83.9k | pixGetDimensions(pixs, &w, &h, NULL); |
279 | 83.9k | sign = L_SIGN(radang); |
280 | 83.9k | tanangle = tan(radang); |
281 | 83.9k | invangle = L_ABS(1. / tanangle); |
282 | 83.9k | initxincr = (l_int32)(invangle / 2.); |
283 | 83.9k | xincr = (l_int32)invangle; |
284 | 83.9k | pixRasterop(pixd, xloc - initxincr, 0, 2 * initxincr, h, PIX_SRC, |
285 | 83.9k | pixs, xloc - initxincr, 0); |
286 | | |
287 | 381k | for (vshift = 1, x = xloc + initxincr; x < w; vshift++) { |
288 | 297k | xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc); |
289 | 297k | if (w - x < xincr) /* reduce for last one if req'd */ |
290 | 49.5k | xincr = w - x; |
291 | 297k | 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 | 297k | x += xincr; |
296 | 297k | } |
297 | | |
298 | 84.1k | for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) { |
299 | 222 | xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5); |
300 | 222 | if (x < xincr) /* reduce for last one if req'd */ |
301 | 68 | xincr = x; |
302 | 222 | pixRasterop(pixd, x - xincr, sign*vshift, xincr, h, PIX_SRC, |
303 | 222 | 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 | 222 | x -= xincr; |
309 | 222 | } |
310 | | |
311 | 83.9k | return pixd; |
312 | 88.0k | } |
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 | 0 | { |
341 | 0 | if (!pixs) |
342 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
343 | | |
344 | 0 | return pixHShear(pixd, pixs, 0, radang, incolor); |
345 | 0 | } |
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 | 87.9k | { |
370 | 87.9k | if (!pixs) |
371 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
372 | | |
373 | 87.9k | return pixVShear(pixd, pixs, 0, radang, incolor); |
374 | 87.9k | } |
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 | 0 | { |
399 | 0 | if (!pixs) |
400 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
401 | | |
402 | 0 | return pixHShear(pixd, pixs, pixGetHeight(pixs) / 2, radang, incolor); |
403 | 0 | } |
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 | 0 | { |
428 | 0 | if (!pixs) |
429 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, pixd); |
430 | | |
431 | 0 | return pixVShear(pixd, pixs, pixGetWidth(pixs) / 2, radang, incolor); |
432 | 0 | } |
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 | 0 | { |
464 | 0 | l_int32 sign, w, h; |
465 | 0 | l_int32 y, yincr, inityincr, hshift; |
466 | 0 | l_float32 tanangle, invangle; |
467 | |
|
468 | 0 | if (!pixs) |
469 | 0 | return ERROR_INT("pixs not defined", __func__, 1); |
470 | 0 | if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) |
471 | 0 | return ERROR_INT("invalid incolor value", __func__, 1); |
472 | 0 | if (pixGetColormap(pixs)) |
473 | 0 | return ERROR_INT("pixs is colormapped", __func__, 1); |
474 | | |
475 | | /* Normalize angle */ |
476 | 0 | radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); |
477 | 0 | if (radang == 0.0 || tan(radang) == 0.0) |
478 | 0 | return 0; |
479 | | |
480 | 0 | sign = L_SIGN(radang); |
481 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
482 | 0 | tanangle = tan(radang); |
483 | 0 | invangle = L_ABS(1. / tanangle); |
484 | 0 | inityincr = (l_int32)(invangle / 2.); |
485 | 0 | yincr = (l_int32)invangle; |
486 | |
|
487 | 0 | if (inityincr > 0) |
488 | 0 | pixRasteropHip(pixs, yloc - inityincr, 2 * inityincr, 0, incolor); |
489 | |
|
490 | 0 | for (hshift = 1, y = yloc + inityincr; y < h; hshift++) { |
491 | 0 | yincr = (l_int32)(invangle * (hshift + 0.5) + 0.5) - (y - yloc); |
492 | 0 | if (yincr == 0) continue; |
493 | 0 | if (h - y < yincr) /* reduce for last one if req'd */ |
494 | 0 | yincr = h - y; |
495 | 0 | pixRasteropHip(pixs, y, yincr, -sign*hshift, incolor); |
496 | 0 | y += yincr; |
497 | 0 | } |
498 | |
|
499 | 0 | for (hshift = -1, y = yloc - inityincr; y > 0; hshift--) { |
500 | 0 | yincr = (y - yloc) - (l_int32)(invangle * (hshift - 0.5) + 0.5); |
501 | 0 | if (yincr == 0) continue; |
502 | 0 | if (y < yincr) /* reduce for last one if req'd */ |
503 | 0 | yincr = y; |
504 | 0 | pixRasteropHip(pixs, y - yincr, yincr, -sign*hshift, incolor); |
505 | 0 | y -= yincr; |
506 | 0 | } |
507 | |
|
508 | 0 | return 0; |
509 | 0 | } |
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 | 0 | { |
537 | 0 | l_int32 sign, w, h; |
538 | 0 | l_int32 x, xincr, initxincr, vshift; |
539 | 0 | l_float32 tanangle, invangle; |
540 | |
|
541 | 0 | if (!pixs) |
542 | 0 | return ERROR_INT("pixs not defined", __func__, 1); |
543 | 0 | if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) |
544 | 0 | return ERROR_INT("invalid incolor value", __func__, 1); |
545 | 0 | if (pixGetColormap(pixs)) |
546 | 0 | return ERROR_INT("pixs is colormapped", __func__, 1); |
547 | | |
548 | | /* Normalize angle */ |
549 | 0 | radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); |
550 | 0 | if (radang == 0.0 || tan(radang) == 0.0) |
551 | 0 | return 0; |
552 | | |
553 | 0 | sign = L_SIGN(radang); |
554 | 0 | pixGetDimensions(pixs, &w, &h, NULL); |
555 | 0 | tanangle = tan(radang); |
556 | 0 | invangle = L_ABS(1. / tanangle); |
557 | 0 | initxincr = (l_int32)(invangle / 2.); |
558 | 0 | xincr = (l_int32)invangle; |
559 | |
|
560 | 0 | if (initxincr > 0) |
561 | 0 | pixRasteropVip(pixs, xloc - initxincr, 2 * initxincr, 0, incolor); |
562 | |
|
563 | 0 | for (vshift = 1, x = xloc + initxincr; x < w; vshift++) { |
564 | 0 | xincr = (l_int32)(invangle * (vshift + 0.5) + 0.5) - (x - xloc); |
565 | 0 | if (xincr == 0) continue; |
566 | 0 | if (w - x < xincr) /* reduce for last one if req'd */ |
567 | 0 | xincr = w - x; |
568 | 0 | pixRasteropVip(pixs, x, xincr, sign*vshift, incolor); |
569 | 0 | x += xincr; |
570 | 0 | } |
571 | |
|
572 | 0 | for (vshift = -1, x = xloc - initxincr; x > 0; vshift--) { |
573 | 0 | xincr = (x - xloc) - (l_int32)(invangle * (vshift - 0.5) + 0.5); |
574 | 0 | if (xincr == 0) continue; |
575 | 0 | if (x < xincr) /* reduce for last one if req'd */ |
576 | 0 | xincr = x; |
577 | 0 | pixRasteropVip(pixs, x - xincr, xincr, sign*vshift, incolor); |
578 | 0 | x -= xincr; |
579 | 0 | } |
580 | |
|
581 | 0 | return 0; |
582 | 0 | } |
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 | 0 | { |
618 | 0 | l_int32 i, jd, x, xp, xf, w, h, d, wm, wpls, wpld, val, rval, gval, bval; |
619 | 0 | l_uint32 word0, word1; |
620 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
621 | 0 | l_float32 tanangle, xshift; |
622 | 0 | PIX *pix, *pixd; |
623 | |
|
624 | 0 | if (!pixs) |
625 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
626 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
627 | 0 | if (d != 8 && d != 32 && !pixGetColormap(pixs)) |
628 | 0 | return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL); |
629 | 0 | if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) |
630 | 0 | return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); |
631 | 0 | if (yloc < 0 || yloc >= h) |
632 | 0 | return (PIX *)ERROR_PTR("yloc not in [0 ... h-1]", __func__, NULL); |
633 | | |
634 | 0 | if (pixGetColormap(pixs)) |
635 | 0 | pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); |
636 | 0 | else |
637 | 0 | pix = pixClone(pixs); |
638 | | |
639 | | /* Normalize angle. If no rotation, return a copy */ |
640 | 0 | radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); |
641 | 0 | if (radang == 0.0 || tan(radang) == 0.0) { |
642 | 0 | pixDestroy(&pix); |
643 | 0 | return pixCopy(NULL, pixs); |
644 | 0 | } |
645 | | |
646 | | /* Initialize to value of incoming pixels */ |
647 | 0 | pixd = pixCreateTemplate(pix); |
648 | 0 | pixSetBlackOrWhite(pixd, incolor); |
649 | | |
650 | | /* Standard linear interp: subdivide each pixel into 64 parts */ |
651 | 0 | d = pixGetDepth(pixd); /* 8 or 32 */ |
652 | 0 | datas = pixGetData(pix); |
653 | 0 | datad = pixGetData(pixd); |
654 | 0 | wpls = pixGetWpl(pix); |
655 | 0 | wpld = pixGetWpl(pixd); |
656 | 0 | tanangle = tan(radang); |
657 | 0 | for (i = 0; i < h; i++) { |
658 | 0 | lines = datas + i * wpls; |
659 | 0 | lined = datad + i * wpld; |
660 | 0 | xshift = (yloc - i) * tanangle; |
661 | 0 | for (jd = 0; jd < w; jd++) { |
662 | 0 | x = (l_int32)(64.0 * (-xshift + jd) + 0.5); |
663 | 0 | xp = x / 64; |
664 | 0 | xf = x & 63; |
665 | 0 | wm = w - 1; |
666 | 0 | if (xp < 0 || xp > wm) continue; |
667 | 0 | if (d == 8) { |
668 | 0 | if (xp < wm) { |
669 | 0 | val = ((63 - xf) * GET_DATA_BYTE(lines, xp) + |
670 | 0 | xf * GET_DATA_BYTE(lines, xp + 1) + 31) / 63; |
671 | 0 | } else { /* xp == wm */ |
672 | 0 | val = GET_DATA_BYTE(lines, xp); |
673 | 0 | } |
674 | 0 | SET_DATA_BYTE(lined, jd, val); |
675 | 0 | } else { /* d == 32 */ |
676 | 0 | if (xp < wm) { |
677 | 0 | word0 = *(lines + xp); |
678 | 0 | word1 = *(lines + xp + 1); |
679 | 0 | rval = ((63 - xf) * ((word0 >> L_RED_SHIFT) & 0xff) + |
680 | 0 | xf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; |
681 | 0 | gval = ((63 - xf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + |
682 | 0 | xf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; |
683 | 0 | bval = ((63 - xf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + |
684 | 0 | xf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; |
685 | 0 | composeRGBPixel(rval, gval, bval, lined + jd); |
686 | 0 | } else { /* xp == wm */ |
687 | 0 | lined[jd] = lines[xp]; |
688 | 0 | } |
689 | 0 | } |
690 | 0 | } |
691 | 0 | } |
692 | |
|
693 | 0 | pixDestroy(&pix); |
694 | 0 | return pixd; |
695 | 0 | } |
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 | 0 | { |
728 | 0 | l_int32 id, y, yp, yf, j, w, h, d, hm, wpls, wpld, val, rval, gval, bval; |
729 | 0 | l_uint32 word0, word1; |
730 | 0 | l_uint32 *datas, *datad, *lines, *lined; |
731 | 0 | l_float32 tanangle, yshift; |
732 | 0 | PIX *pix, *pixd; |
733 | |
|
734 | 0 | if (!pixs) |
735 | 0 | return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); |
736 | 0 | pixGetDimensions(pixs, &w, &h, &d); |
737 | 0 | if (d != 8 && d != 32 && !pixGetColormap(pixs)) |
738 | 0 | return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL); |
739 | 0 | if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) |
740 | 0 | return (PIX *)ERROR_PTR("invalid incolor value", __func__, NULL); |
741 | 0 | if (xloc < 0 || xloc >= w) |
742 | 0 | return (PIX *)ERROR_PTR("xloc not in [0 ... w-1]", __func__, NULL); |
743 | | |
744 | 0 | if (pixGetColormap(pixs)) |
745 | 0 | pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); |
746 | 0 | else |
747 | 0 | pix = pixClone(pixs); |
748 | | |
749 | | /* Normalize angle. If no rotation, return a copy */ |
750 | 0 | radang = normalizeAngleForShear(radang, MinDiffFromHalfPi); |
751 | 0 | if (radang == 0.0 || tan(radang) == 0.0) { |
752 | 0 | pixDestroy(&pix); |
753 | 0 | return pixCopy(NULL, pixs); |
754 | 0 | } |
755 | | |
756 | | /* Initialize to value of incoming pixels */ |
757 | 0 | pixd = pixCreateTemplate(pix); |
758 | 0 | pixSetBlackOrWhite(pixd, incolor); |
759 | | |
760 | | /* Standard linear interp: subdivide each pixel into 64 parts */ |
761 | 0 | d = pixGetDepth(pixd); /* 8 or 32 */ |
762 | 0 | datas = pixGetData(pix); |
763 | 0 | datad = pixGetData(pixd); |
764 | 0 | wpls = pixGetWpl(pix); |
765 | 0 | wpld = pixGetWpl(pixd); |
766 | 0 | tanangle = tan(radang); |
767 | 0 | for (j = 0; j < w; j++) { |
768 | 0 | yshift = (j - xloc) * tanangle; |
769 | 0 | for (id = 0; id < h; id++) { |
770 | 0 | y = (l_int32)(64.0 * (-yshift + id) + 0.5); |
771 | 0 | yp = y / 64; |
772 | 0 | yf = y & 63; |
773 | 0 | hm = h - 1; |
774 | 0 | if (yp < 0 || yp > hm) continue; |
775 | 0 | lines = datas + yp * wpls; |
776 | 0 | lined = datad + id * wpld; |
777 | 0 | if (d == 8) { |
778 | 0 | if (yp < hm) { |
779 | 0 | val = ((63 - yf) * GET_DATA_BYTE(lines, j) + |
780 | 0 | yf * GET_DATA_BYTE(lines + wpls, j) + 31) / 63; |
781 | 0 | } else { /* yp == hm */ |
782 | 0 | val = GET_DATA_BYTE(lines, j); |
783 | 0 | } |
784 | 0 | SET_DATA_BYTE(lined, j, val); |
785 | 0 | } else { /* d == 32 */ |
786 | 0 | if (yp < hm) { |
787 | 0 | word0 = *(lines + j); |
788 | 0 | word1 = *(lines + wpls + j); |
789 | 0 | rval = ((63 - yf) * ((word0 >> L_RED_SHIFT) & 0xff) + |
790 | 0 | yf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; |
791 | 0 | gval = ((63 - yf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + |
792 | 0 | yf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; |
793 | 0 | bval = ((63 - yf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + |
794 | 0 | yf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; |
795 | 0 | composeRGBPixel(rval, gval, bval, lined + j); |
796 | 0 | } else { /* yp == hm */ |
797 | 0 | lined[j] = lines[j]; |
798 | 0 | } |
799 | 0 | } |
800 | 0 | } |
801 | 0 | } |
802 | |
|
803 | 0 | pixDestroy(&pix); |
804 | 0 | return pixd; |
805 | 0 | } |
806 | | |
807 | | |
808 | | /*-------------------------------------------------------------------------* |
809 | | * Angle normalization * |
810 | | *-------------------------------------------------------------------------*/ |
811 | | static l_float32 |
812 | | normalizeAngleForShear(l_float32 radang, |
813 | | l_float32 mindif) |
814 | 88.2k | { |
815 | 88.2k | l_float32 pi2; |
816 | | |
817 | | /* Bring angle into range [-pi/2, pi/2] */ |
818 | 88.2k | pi2 = 3.14159265f / 2.0f; |
819 | 88.2k | if (radang < -pi2 || radang > pi2) |
820 | 0 | radang = radang - (l_int32)(radang / pi2) * pi2; |
821 | | |
822 | | /* If angle is too close to pi/2 or -pi/2, move it */ |
823 | 88.2k | if (radang > pi2 - mindif) { |
824 | 0 | L_WARNING("angle close to pi/2; shifting away\n", __func__); |
825 | 0 | radang = pi2 - mindif; |
826 | 88.2k | } else if (radang < -pi2 + mindif) { |
827 | 0 | L_WARNING("angle close to -pi/2; shifting away\n", __func__); |
828 | 0 | radang = -pi2 + mindif; |
829 | 0 | } |
830 | | |
831 | 88.2k | return radang; |
832 | 88.2k | } |