Leptonica 1.82.0
Image processing and image analysis suite
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.001; /* radians; ~0.06 deg */
64static const l_float32 Max1BppShearAngle = 0.06; /* radians; ~3 deg */
65static const l_float32 LimitShearAngle = 0.35; /* 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 PROCNAME("pixRotate");
114
115 if (!pixs)
116 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
117 if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP &&
118 type != L_ROTATE_SAMPLING)
119 return (PIX *)ERROR_PTR("invalid type", procName, NULL);
120 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
121 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
122
123 if (L_ABS(angle) < MinAngleToRotate)
124 return pixClone(pixs);
125
126 /* Adjust rotation type if necessary:
127 * - If d == 1 bpp and the angle is more than about 6 degrees,
128 * rotate by sampling; otherwise rotate by shear.
129 * - If d > 1, only allow shear rotation up to about 20 degrees;
130 * beyond that, default a shear request to sampling. */
131 if (pixGetDepth(pixs) == 1) {
132 if (L_ABS(angle) > Max1BppShearAngle) {
133 if (type != L_ROTATE_SAMPLING)
134 L_INFO("1 bpp, large angle; rotate by sampling\n", procName);
135 type = L_ROTATE_SAMPLING;
136 } else if (type != L_ROTATE_SHEAR) {
137 L_INFO("1 bpp; rotate by shear\n", procName);
138 type = L_ROTATE_SHEAR;
139 }
140 } else if (L_ABS(angle) > LimitShearAngle && type == L_ROTATE_SHEAR) {
141 L_INFO("large angle; rotate by sampling\n", procName);
142 type = L_ROTATE_SAMPLING;
143 }
144
145 /* Remove colormap if we rotate by area mapping. */
146 cmap = pixGetColormap(pixs);
147 if (cmap && type == L_ROTATE_AREA_MAP)
149 else
150 pix1 = pixClone(pixs);
151 cmap = pixGetColormap(pix1);
152
153 /* Otherwise, if there is a colormap and we're not embedding,
154 * add white color if it doesn't exist. */
155 if (cmap && width == 0) { /* no embedding; generate %incolor */
156 if (incolor == L_BRING_IN_BLACK)
157 pixcmapAddBlackOrWhite(cmap, 0, NULL);
158 else /* L_BRING_IN_WHITE */
159 pixcmapAddBlackOrWhite(cmap, 1, NULL);
160 }
161
162 /* Request to embed in a larger image; do if necessary */
163 pix2 = pixEmbedForRotation(pix1, angle, incolor, width, height);
164
165 /* Area mapping requires 8 or 32 bpp. If less than 8 bpp and
166 * area map rotation is requested, convert to 8 bpp. */
167 d = pixGetDepth(pix2);
168 if (type == L_ROTATE_AREA_MAP && d < 8)
169 pix3 = pixConvertTo8(pix2, FALSE);
170 else
171 pix3 = pixClone(pix2);
172
173 /* Do the rotation: shear, sampling or area mapping */
174 pixGetDimensions(pix3, &w, &h, &d);
175 if (type == L_ROTATE_SHEAR) {
176 pixd = pixRotateShearCenter(pix3, angle, incolor);
177 } else if (type == L_ROTATE_SAMPLING) {
178 pixd = pixRotateBySampling(pix3, w / 2, h / 2, angle, incolor);
179 } else { /* rotate by area mapping */
180 fillval = 0;
181 if (incolor == L_BRING_IN_WHITE) {
182 if (d == 8)
183 fillval = 255;
184 else /* d == 32 */
185 fillval = 0xffffff00;
186 }
187 if (d == 8)
188 pixd = pixRotateAMGray(pix3, angle, fillval);
189 else /* d == 32 */
190 pixd = pixRotateAMColor(pix3, angle, fillval);
191 }
192
193 pixDestroy(&pix1);
194 pixDestroy(&pix2);
195 pixDestroy(&pix3);
196 return pixd;
197}
198
199
242PIX *
244 l_float32 angle,
245 l_int32 incolor,
246 l_int32 width,
247 l_int32 height)
248{
249l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
250l_float64 sina, cosa, fw, fh;
251PIX *pixd;
252
253 PROCNAME("pixEmbedForRotation");
254
255 if (!pixs)
256 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
257 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
258 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
259 if (L_ABS(angle) < MinAngleToRotate)
260 return pixClone(pixs);
261
262 /* Test if big enough to hold any rotation of the original image */
263 pixGetDimensions(pixs, &w, &h, &d);
264 maxside = (l_int32)(sqrt((l_float64)(width * width) +
265 (l_float64)(height * height)) + 0.5);
266 if (w >= maxside && h >= maxside) /* big enough */
267 return pixClone(pixs);
268
269 /* Find the new sizes required to hold the image after rotation.
270 * Note that the new dimensions must be at least as large as those
271 * of pixs, because we're rasterop-ing into it before rotation. */
272 cosa = cos(angle);
273 sina = sin(angle);
274 fw = (l_float64)w;
275 fh = (l_float64)h;
276 w1 = (l_int32)(L_ABS(fw * cosa - fh * sina) + 0.5);
277 w2 = (l_int32)(L_ABS(-fw * cosa - fh * sina) + 0.5);
278 h1 = (l_int32)(L_ABS(fw * sina + fh * cosa) + 0.5);
279 h2 = (l_int32)(L_ABS(-fw * sina + fh * cosa) + 0.5);
280 wnew = L_MAX(w, L_MAX(w1, w2));
281 hnew = L_MAX(h, L_MAX(h1, h2));
282
283 if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
284 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
285 pixCopyResolution(pixd, pixs);
286 pixCopyColormap(pixd, pixs);
287 pixCopySpp(pixd, pixs);
288 pixCopyText(pixd, pixs);
289 xoff = (wnew - w) / 2;
290 yoff = (hnew - h) / 2;
291
292 /* Set background to color to be rotated in */
293 setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE;
294 pixSetBlackOrWhite(pixd, setcolor);
295
296 /* Rasterop automatically handles all 4 channels for rgba */
297 pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
298 return pixd;
299}
300
301
302/*------------------------------------------------------------------*
303 * General rotation by sampling *
304 *------------------------------------------------------------------*/
323PIX *
325 l_int32 xcen,
326 l_int32 ycen,
327 l_float32 angle,
328 l_int32 incolor)
329{
330l_int32 w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld;
331l_uint32 val;
332l_float32 sina, cosa;
333l_uint32 *datad, *lined;
334void **lines;
335PIX *pixd;
336
337 PROCNAME("pixRotateBySampling");
338
339 if (!pixs)
340 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
341 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
342 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
343 pixGetDimensions(pixs, &w, &h, &d);
344 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
345 return (PIX *)ERROR_PTR("invalid depth", procName, NULL);
346
347 if (L_ABS(angle) < MinAngleToRotate)
348 return pixClone(pixs);
349
350 if ((pixd = pixCreateTemplate(pixs)) == NULL)
351 return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
352 pixSetBlackOrWhite(pixd, incolor);
353
354 sina = sin(angle);
355 cosa = cos(angle);
356 datad = pixGetData(pixd);
357 wpld = pixGetWpl(pixd);
358 wm1 = w - 1;
359 hm1 = h - 1;
360 lines = pixGetLinePtrs(pixs, NULL);
361
362 /* Treat 1 bpp case specially */
363 if (d == 1) {
364 for (i = 0; i < h; i++) { /* scan over pixd */
365 lined = datad + i * wpld;
366 ydif = ycen - i;
367 for (j = 0; j < w; j++) {
368 xdif = xcen - j;
369 x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
370 if (x < 0 || x > wm1) continue;
371 y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
372 if (y < 0 || y > hm1) continue;
373 if (incolor == L_BRING_IN_WHITE) {
374 if (GET_DATA_BIT(lines[y], x))
375 SET_DATA_BIT(lined, j);
376 } else {
377 if (!GET_DATA_BIT(lines[y], x))
378 CLEAR_DATA_BIT(lined, j);
379 }
380 }
381 }
382 LEPT_FREE(lines);
383 return pixd;
384 }
385
386 for (i = 0; i < h; i++) { /* scan over pixd */
387 lined = datad + i * wpld;
388 ydif = ycen - i;
389 for (j = 0; j < w; j++) {
390 xdif = xcen - j;
391 x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
392 if (x < 0 || x > wm1) continue;
393 y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
394 if (y < 0 || y > hm1) continue;
395 switch (d)
396 {
397 case 8:
398 val = GET_DATA_BYTE(lines[y], x);
399 SET_DATA_BYTE(lined, j, val);
400 break;
401 case 32:
402 val = GET_DATA_FOUR_BYTES(lines[y], x);
403 SET_DATA_FOUR_BYTES(lined, j, val);
404 break;
405 case 2:
406 val = GET_DATA_DIBIT(lines[y], x);
407 SET_DATA_DIBIT(lined, j, val);
408 break;
409 case 4:
410 val = GET_DATA_QBIT(lines[y], x);
411 SET_DATA_QBIT(lined, j, val);
412 break;
413 case 16:
414 val = GET_DATA_TWO_BYTES(lines[y], x);
415 SET_DATA_TWO_BYTES(lined, j, val);
416 break;
417 default:
418 return (PIX *)ERROR_PTR("invalid depth", procName, NULL);
419 }
420 }
421 }
422
423 LEPT_FREE(lines);
424 return pixd;
425}
426
427
428/*------------------------------------------------------------------*
429 * Nice (slow) rotation of 1 bpp image *
430 *------------------------------------------------------------------*/
456PIX *
458 l_float32 angle,
459 l_int32 incolor)
460{
461PIX *pix1, *pix2, *pix3, *pix4, *pixd;
462
463 PROCNAME("pixRotateBinaryNice");
464
465 if (!pixs || pixGetDepth(pixs) != 1)
466 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
467 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
468 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
469
470 pix1 = pixConvertTo8(pixs, 0);
471 pix2 = pixBlockconv(pix1, 1, 1); /* smallest blur allowed */
472 pix3 = pixRotateAM(pix2, angle, incolor);
473 pix4 = pixUnsharpMasking(pix3, 1, 1.0); /* sharpen a bit */
474 pixd = pixThresholdToBinary(pix4, 128);
475 pixDestroy(&pix1);
476 pixDestroy(&pix2);
477 pixDestroy(&pix3);
478 pixDestroy(&pix4);
479 return pixd;
480}
481
482
483/*------------------------------------------------------------------*
484 * Rotation including alpha (blend) component *
485 *------------------------------------------------------------------*/
534PIX *
536 l_float32 angle,
537 PIX *pixg,
538 l_float32 fract)
539{
540l_int32 ws, hs, d, spp;
541PIX *pixd, *pix32, *pixg2, *pixgr;
542
543 PROCNAME("pixRotateWithAlpha");
544
545 if (!pixs)
546 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
547 pixGetDimensions(pixs, &ws, &hs, &d);
548 if (d != 32 && pixGetColormap(pixs) == NULL)
549 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL);
550 if (pixg && pixGetDepth(pixg) != 8) {
551 L_WARNING("pixg not 8 bpp; using 'fract' transparent alpha\n",
552 procName);
553 pixg = NULL;
554 }
555 if (!pixg && (fract < 0.0 || fract > 1.0)) {
556 L_WARNING("invalid fract; using fully opaque\n", procName);
557 fract = 1.0;
558 }
559 if (!pixg && fract == 0.0)
560 L_WARNING("transparent alpha; image will not be blended\n", procName);
561
562 /* Make sure input to rotation is 32 bpp rgb, and rotate it */
563 if (d != 32)
564 pix32 = pixConvertTo32(pixs);
565 else
566 pix32 = pixClone(pixs);
567 spp = pixGetSpp(pix32);
568 pixSetSpp(pix32, 3); /* ignore the alpha channel for the rotation */
569 pixd = pixRotate(pix32, angle, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, ws, hs);
570 pixSetSpp(pix32, spp); /* restore initial value in case it's a clone */
571 pixDestroy(&pix32);
572
573 /* Set up alpha layer with a fading border and rotate it */
574 if (!pixg) {
575 pixg2 = pixCreate(ws, hs, 8);
576 if (fract == 1.0)
577 pixSetAll(pixg2);
578 else if (fract > 0.0)
579 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract));
580 } else {
581 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs);
582 }
583 if (ws > 10 && hs > 10) { /* see note 8 */
584 pixSetBorderRingVal(pixg2, 1,
585 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0]));
586 pixSetBorderRingVal(pixg2, 2,
587 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1]));
588 }
589 pixgr = pixRotate(pixg2, angle, L_ROTATE_AREA_MAP,
590 L_BRING_IN_BLACK, ws, hs);
591
592 /* Combine into a 4 spp result */
594
595 pixDestroy(&pixg2);
596 pixDestroy(&pixgr);
597 return pixd;
598}
#define GET_DATA_QBIT(pdata, n)
Definition: arrayaccess.h:164
#define GET_DATA_TWO_BYTES(pdata, n)
Definition: arrayaccess.h:212
#define SET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:127
#define SET_DATA_DIBIT(pdata, n, val)
Definition: arrayaccess.h:149
#define SET_DATA_TWO_BYTES(pdata, n, val)
Definition: arrayaccess.h:222
#define SET_DATA_FOUR_BYTES(pdata, n, val)
Definition: arrayaccess.h:235
#define GET_DATA_BYTE(pdata, n)
Definition: arrayaccess.h:188
#define GET_DATA_FOUR_BYTES(pdata, n)
Definition: arrayaccess.h:231
#define GET_DATA_DIBIT(pdata, n)
Definition: arrayaccess.h:145
#define SET_DATA_BYTE(pdata, n, val)
Definition: arrayaccess.h:198
#define CLEAR_DATA_BIT(pdata, n)
Definition: arrayaccess.h:131
#define GET_DATA_BIT(pdata, n)
Definition: arrayaccess.h:123
#define SET_DATA_QBIT(pdata, n, val)
Definition: arrayaccess.h:168
l_ok pixcmapAddBlackOrWhite(PIXCMAP *cmap, l_int32 color, l_int32 *pindex)
pixcmapAddBlackOrWhite()
Definition: colormap.c:639
PIX * pixBlockconv(PIX *pix, l_int32 wc, l_int32 hc)
pixBlockconv()
Definition: convolve.c:132
PIX * pixUnsharpMasking(PIX *pixs, l_int32 halfwidth, l_float32 fract)
pixUnsharpMasking()
Definition: enhance.c:1001
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:447
void ** pixGetLinePtrs(PIX *pix, l_int32 *psize)
pixGetLinePtrs()
Definition: pix1.c:1949
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:621
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1113
l_ok pixCopySpp(PIX *pixd, const PIX *pixs)
pixCopySpp()
Definition: pix1.c:1236
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
l_ok pixCopyColormap(PIX *pixd, const PIX *pixs)
pixCopyColormap()
Definition: pix1.c:816
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
l_ok pixSetBorderRingVal(PIX *pixs, l_int32 dist, l_uint32 val)
pixSetBorderRingVal()
Definition: pix2.c:1667
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:817
l_ok pixSetBlackOrWhite(PIX *pixs, l_int32 op)
pixSetBlackOrWhite()
Definition: pix2.c:1021
l_ok pixSetRGBComponent(PIX *pixd, PIX *pixs, l_int32 comp)
pixSetRGBComponent()
Definition: pix2.c:2538
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:951
PIX * pixResizeToMatch(PIX *pixs, PIX *pixt, l_int32 w, l_int32 h)
pixResizeToMatch()
Definition: pix5.c:1321
@ L_ALPHA_CHANNEL
Definition: pix.h:207
@ REMOVE_CMAP_BASED_ON_SRC
Definition: pix.h:260
@ L_SET_WHITE
Definition: pix.h:906
@ L_SET_BLACK
Definition: pix.h:907
#define PIX_SRC
Definition: pix.h:330
@ L_BRING_IN_BLACK
Definition: pix.h:870
@ L_BRING_IN_WHITE
Definition: pix.h:869
@ L_ROTATE_SAMPLING
Definition: pix.h:864
@ L_ROTATE_SHEAR
Definition: pix.h:863
@ L_ROTATE_AREA_MAP
Definition: pix.h:862
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
PIX * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3332
l_ok pixRasterop(PIX *pixd, l_int32 dx, l_int32 dy, l_int32 dw, l_int32 dh, l_int32 op, PIX *pixs, l_int32 sx, l_int32 sy)
pixRasterop()
Definition: rop.c:204
PIX * pixEmbedForRotation(PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height)
pixEmbedForRotation()
Definition: rotate.c:243
PIX * pixRotateBySampling(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor)
pixRotateBySampling()
Definition: rotate.c:324
PIX * pixRotateWithAlpha(PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract)
pixRotateWithAlpha()
Definition: rotate.c:535
PIX * pixRotateBinaryNice(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateBinaryNice()
Definition: rotate.c:457
PIX * pixRotate(PIX *pixs, l_float32 angle, l_int32 type, l_int32 incolor, l_int32 width, l_int32 height)
pixRotate()
Definition: rotate.c:101
PIX * pixRotateAMGray(PIX *pixs, l_float32 angle, l_uint8 grayval)
pixRotateAMGray()
Definition: rotateam.c:289
PIX * pixRotateAM(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateAM()
Definition: rotateam.c:172
PIX * pixRotateAMColor(PIX *pixs, l_float32 angle, l_uint32 colorval)
pixRotateAMColor()
Definition: rotateam.c:235
PIX * pixRotateShearCenter(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateShearCenter()
Definition: rotateshear.c:464
Definition: pix.h:139