Leptonica 1.85.0
Image processing and image analysis suite
Loading...
Searching...
No Matches
rotate.c
Go to the documentation of this file.
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
55#ifdef HAVE_CONFIG_H
56#include <config_auto.h>
57#endif /* HAVE_CONFIG_H */
58
59#include <math.h>
60#include "allheaders.h"
61
62extern l_float32 AlphaMaskBorderVals[2];
63static const l_float32 MinAngleToRotate = 0.001f; /* radians; ~0.06 deg */
64static const l_float32 Max1BppShearAngle = 0.06f; /* radians; ~3 deg */
65static const l_float32 LimitShearAngle = 0.35f; /* radians; ~20 deg */
66
67/*------------------------------------------------------------------*
68 * General rotation about the center *
69 *------------------------------------------------------------------*/
100PIX *
102 l_float32 angle,
103 l_int32 type,
104 l_int32 incolor,
105 l_int32 width,
106 l_int32 height)
107{
108l_int32 w, h, d;
109l_uint32 fillval;
110PIX *pix1, *pix2, *pix3, *pixd;
111PIXCMAP *cmap;
112
113 if (!pixs)
114 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
115 if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP &&
116 type != L_ROTATE_SAMPLING)
117 return (PIX *)ERROR_PTR("invalid type", __func__, NULL);
118 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
119 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
120
121 if (L_ABS(angle) < MinAngleToRotate)
122 return pixClone(pixs);
123
124 /* Adjust rotation type if necessary:
125 * - If d == 1 bpp and the angle is more than about 6 degrees,
126 * rotate by sampling; otherwise rotate by shear.
127 * - If d > 1, only allow shear rotation up to about 20 degrees;
128 * beyond that, default a shear request to sampling. */
129 if (pixGetDepth(pixs) == 1) {
130 if (L_ABS(angle) > Max1BppShearAngle) {
131 if (type != L_ROTATE_SAMPLING)
132 L_INFO("1 bpp, large angle; rotate by sampling\n", __func__);
133 type = L_ROTATE_SAMPLING;
134 } else if (type != L_ROTATE_SHEAR) {
135 L_INFO("1 bpp; rotate by shear\n", __func__);
136 type = L_ROTATE_SHEAR;
137 }
138 } else if (L_ABS(angle) > LimitShearAngle && type == L_ROTATE_SHEAR) {
139 L_INFO("large angle; rotate by sampling\n", __func__);
140 type = L_ROTATE_SAMPLING;
141 }
142
143 /* Remove colormap if we rotate by area mapping. */
144 cmap = pixGetColormap(pixs);
145 if (cmap && type == L_ROTATE_AREA_MAP)
146 pix1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
147 else
148 pix1 = pixClone(pixs);
149 cmap = pixGetColormap(pix1);
150
151 /* Otherwise, if there is a colormap and we're not embedding,
152 * add white color if it doesn't exist. */
153 if (cmap && width == 0) { /* no embedding; generate %incolor */
154 if (incolor == L_BRING_IN_BLACK)
155 pixcmapAddBlackOrWhite(cmap, 0, NULL);
156 else /* L_BRING_IN_WHITE */
157 pixcmapAddBlackOrWhite(cmap, 1, NULL);
158 }
159
160 /* Request to embed in a larger image; do if necessary */
161 pix2 = pixEmbedForRotation(pix1, angle, incolor, width, height);
162
163 /* Area mapping requires 8 or 32 bpp. If less than 8 bpp and
164 * area map rotation is requested, convert to 8 bpp. */
165 d = pixGetDepth(pix2);
166 if (type == L_ROTATE_AREA_MAP && d < 8)
167 pix3 = pixConvertTo8(pix2, FALSE);
168 else
169 pix3 = pixClone(pix2);
170
171 /* Do the rotation: shear, sampling or area mapping */
172 pixGetDimensions(pix3, &w, &h, &d);
173 if (type == L_ROTATE_SHEAR) {
174 pixd = pixRotateShearCenter(pix3, angle, incolor);
175 } else if (type == L_ROTATE_SAMPLING) {
176 pixd = pixRotateBySampling(pix3, w / 2, h / 2, angle, incolor);
177 } else { /* rotate by area mapping */
178 fillval = 0;
179 if (incolor == L_BRING_IN_WHITE) {
180 if (d == 8)
181 fillval = 255;
182 else /* d == 32 */
183 fillval = 0xffffff00;
184 }
185 if (d == 8)
186 pixd = pixRotateAMGray(pix3, angle, fillval);
187 else /* d == 32 */
188 pixd = pixRotateAMColor(pix3, angle, fillval);
189 }
190
191 pixDestroy(&pix1);
192 pixDestroy(&pix2);
193 pixDestroy(&pix3);
194 return pixd;
195}
196
197
240PIX *
242 l_float32 angle,
243 l_int32 incolor,
244 l_int32 width,
245 l_int32 height)
246{
247l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
248l_float64 sina, cosa, fw, fh;
249PIX *pixd;
250
251 if (!pixs)
252 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
253 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
254 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
255 if (L_ABS(angle) < MinAngleToRotate)
256 return pixClone(pixs);
257
258 /* Test if big enough to hold any rotation of the original image */
259 pixGetDimensions(pixs, &w, &h, &d);
260 maxside = (l_int32)(sqrt((l_float64)(width * width) +
261 (l_float64)(height * height)) + 0.5);
262 if (w >= maxside && h >= maxside) /* big enough */
263 return pixClone(pixs);
264
265 /* Find the new sizes required to hold the image after rotation.
266 * Note that the new dimensions must be at least as large as those
267 * of pixs, because we're rasterop-ing into it before rotation. */
268 cosa = cos(angle);
269 sina = sin(angle);
270 fw = (l_float64)w;
271 fh = (l_float64)h;
272 w1 = (l_int32)(L_ABS(fw * cosa - fh * sina) + 0.5);
273 w2 = (l_int32)(L_ABS(-fw * cosa - fh * sina) + 0.5);
274 h1 = (l_int32)(L_ABS(fw * sina + fh * cosa) + 0.5);
275 h2 = (l_int32)(L_ABS(-fw * sina + fh * cosa) + 0.5);
276 wnew = L_MAX(w, L_MAX(w1, w2));
277 hnew = L_MAX(h, L_MAX(h1, h2));
278
279 if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
280 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
281 pixCopyResolution(pixd, pixs);
282 pixCopyColormap(pixd, pixs);
283 pixCopySpp(pixd, pixs);
284 pixCopyText(pixd, pixs);
285 xoff = (wnew - w) / 2;
286 yoff = (hnew - h) / 2;
287
288 /* Set background to color to be rotated in */
289 setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE;
290 pixSetBlackOrWhite(pixd, setcolor);
291
292 /* Rasterop automatically handles all 4 channels for rgba */
293 pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
294 return pixd;
295}
296
297
298/*------------------------------------------------------------------*
299 * General rotation by sampling *
300 *------------------------------------------------------------------*/
319PIX *
321 l_int32 xcen,
322 l_int32 ycen,
323 l_float32 angle,
324 l_int32 incolor)
325{
326l_int32 w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld;
327l_uint32 val;
328l_float32 sina, cosa;
329l_uint32 *datad, *lined;
330void **lines;
331PIX *pixd;
332
333 if (!pixs)
334 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
335 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
336 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
337 pixGetDimensions(pixs, &w, &h, &d);
338 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
339 return (PIX *)ERROR_PTR("invalid depth", __func__, NULL);
340
341 if (L_ABS(angle) < MinAngleToRotate)
342 return pixClone(pixs);
343
344 if ((pixd = pixCreateTemplate(pixs)) == NULL)
345 return (PIX *)ERROR_PTR("pixd not made", __func__, NULL);
346 pixSetBlackOrWhite(pixd, incolor);
347
348 sina = sin(angle);
349 cosa = cos(angle);
350 datad = pixGetData(pixd);
351 wpld = pixGetWpl(pixd);
352 wm1 = w - 1;
353 hm1 = h - 1;
354 lines = pixGetLinePtrs(pixs, NULL);
355
356 /* Treat 1 bpp case specially */
357 if (d == 1) {
358 for (i = 0; i < h; i++) { /* scan over pixd */
359 lined = datad + i * wpld;
360 ydif = ycen - i;
361 for (j = 0; j < w; j++) {
362 xdif = xcen - j;
363 x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
364 if (x < 0 || x > wm1) continue;
365 y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
366 if (y < 0 || y > hm1) continue;
367 if (incolor == L_BRING_IN_WHITE) {
368 if (GET_DATA_BIT(lines[y], x))
369 SET_DATA_BIT(lined, j);
370 } else {
371 if (!GET_DATA_BIT(lines[y], x))
372 CLEAR_DATA_BIT(lined, j);
373 }
374 }
375 }
376 LEPT_FREE(lines);
377 return pixd;
378 }
379
380 for (i = 0; i < h; i++) { /* scan over pixd */
381 lined = datad + i * wpld;
382 ydif = ycen - i;
383 for (j = 0; j < w; j++) {
384 xdif = xcen - j;
385 x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
386 if (x < 0 || x > wm1) continue;
387 y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
388 if (y < 0 || y > hm1) continue;
389 switch (d)
390 {
391 case 8:
392 val = GET_DATA_BYTE(lines[y], x);
393 SET_DATA_BYTE(lined, j, val);
394 break;
395 case 32:
396 val = GET_DATA_FOUR_BYTES(lines[y], x);
397 SET_DATA_FOUR_BYTES(lined, j, val);
398 break;
399 case 2:
400 val = GET_DATA_DIBIT(lines[y], x);
401 SET_DATA_DIBIT(lined, j, val);
402 break;
403 case 4:
404 val = GET_DATA_QBIT(lines[y], x);
405 SET_DATA_QBIT(lined, j, val);
406 break;
407 case 16:
408 val = GET_DATA_TWO_BYTES(lines[y], x);
409 SET_DATA_TWO_BYTES(lined, j, val);
410 break;
411 default:
412 return (PIX *)ERROR_PTR("invalid depth", __func__, NULL);
413 }
414 }
415 }
416
417 LEPT_FREE(lines);
418 return pixd;
419}
420
421
422/*------------------------------------------------------------------*
423 * Nice (slow) rotation of 1 bpp image *
424 *------------------------------------------------------------------*/
450PIX *
452 l_float32 angle,
453 l_int32 incolor)
454{
455PIX *pix1, *pix2, *pix3, *pix4, *pixd;
456
457 if (!pixs || pixGetDepth(pixs) != 1)
458 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", __func__, NULL);
459 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
460 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL);
461
462 pix1 = pixConvertTo8(pixs, 0);
463 pix2 = pixBlockconv(pix1, 1, 1); /* smallest blur allowed */
464 pix3 = pixRotateAM(pix2, angle, incolor);
465 pix4 = pixUnsharpMasking(pix3, 1, 1.0); /* sharpen a bit */
466 pixd = pixThresholdToBinary(pix4, 128);
467 pixDestroy(&pix1);
468 pixDestroy(&pix2);
469 pixDestroy(&pix3);
470 pixDestroy(&pix4);
471 return pixd;
472}
473
474
475/*------------------------------------------------------------------*
476 * Rotation including alpha (blend) component *
477 *------------------------------------------------------------------*/
526PIX *
528 l_float32 angle,
529 PIX *pixg,
530 l_float32 fract)
531{
532l_int32 ws, hs, d, spp;
533PIX *pixd, *pix32, *pixg2, *pixgr;
534
535 if (!pixs)
536 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
537 pixGetDimensions(pixs, &ws, &hs, &d);
538 if (d != 32 && pixGetColormap(pixs) == NULL)
539 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", __func__, NULL);
540 if (pixg && pixGetDepth(pixg) != 8) {
541 L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n",
542 __func__);
543 pixg = NULL;
544 }
545 if (!pixg && (fract < 0.0 || fract > 1.0)) {
546 L_WARNING("invalid fract; using fully opaque\n", __func__);
547 fract = 1.0;
548 }
549 if (!pixg && fract == 0.0)
550 L_WARNING("transparent alpha; image will not be blended\n", __func__);
551
552 /* Make sure input to rotation is 32 bpp rgb, and rotate it */
553 if (d != 32)
554 pix32 = pixConvertTo32(pixs);
555 else
556 pix32 = pixClone(pixs);
557 spp = pixGetSpp(pix32);
558 pixSetSpp(pix32, 3); /* ignore the alpha channel for the rotation */
559 pixd = pixRotate(pix32, angle, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, ws, hs);
560 pixSetSpp(pix32, spp); /* restore initial value in case it's a clone */
561 pixDestroy(&pix32);
562
563 /* Set up alpha layer with a fading border and rotate it */
564 if (!pixg) {
565 pixg2 = pixCreate(ws, hs, 8);
566 if (fract == 1.0)
567 pixSetAll(pixg2);
568 else if (fract > 0.0)
569 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract));
570 } else {
571 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs);
572 }
573 if (ws > 10 && hs > 10) { /* see note 8 */
574 pixSetBorderRingVal(pixg2, 1,
575 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0]));
576 pixSetBorderRingVal(pixg2, 2,
577 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1]));
578 }
579 pixgr = pixRotate(pixg2, angle, L_ROTATE_AREA_MAP,
580 L_BRING_IN_BLACK, ws, hs);
581
582 /* Combine into a 4 spp result */
583 pixSetRGBComponent(pixd, pixgr, L_ALPHA_CHANNEL);
584
585 pixDestroy(&pixg2);
586 pixDestroy(&pixgr);
587 return pixd;
588}
#define GET_DATA_QBIT(pdata, n)
#define GET_DATA_TWO_BYTES(pdata, n)
#define SET_DATA_BIT(pdata, n)
#define SET_DATA_DIBIT(pdata, n, val)
#define SET_DATA_TWO_BYTES(pdata, n, val)
#define SET_DATA_FOUR_BYTES(pdata, n, val)
#define GET_DATA_BYTE(pdata, n)
#define GET_DATA_FOUR_BYTES(pdata, n)
#define GET_DATA_DIBIT(pdata, n)
#define SET_DATA_BYTE(pdata, n, val)
#define CLEAR_DATA_BIT(pdata, n)
#define GET_DATA_BIT(pdata, n)
#define SET_DATA_QBIT(pdata, n, val)
@ L_ALPHA_CHANNEL
Definition pix.h:331
@ REMOVE_CMAP_BASED_ON_SRC
Definition pix.h:384
@ L_SET_WHITE
Definition pix.h:699
@ L_SET_BLACK
Definition pix.h:700
#define PIX_SRC
Definition pix.h:444
@ L_BRING_IN_BLACK
Definition pix.h:663
@ L_BRING_IN_WHITE
Definition pix.h:662
@ L_ROTATE_SAMPLING
Definition pix.h:657
@ L_ROTATE_SHEAR
Definition pix.h:656
@ L_ROTATE_AREA_MAP
Definition pix.h:655
PIX * pixEmbedForRotation(PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height)
pixEmbedForRotation()
Definition rotate.c:241
PIX * pixRotateBySampling(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor)
pixRotateBySampling()
Definition rotate.c:320
PIX * pixRotateWithAlpha(PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract)
pixRotateWithAlpha()
Definition rotate.c:527
PIX * pixRotateBinaryNice(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateBinaryNice()
Definition rotate.c:451
PIX * pixRotate(PIX *pixs, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height)
pixRotate()
Definition rotate.c:101