Leptonica  1.83.1
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 
62 extern l_float32 AlphaMaskBorderVals[2];
63 static const l_float32 MinAngleToRotate = 0.001; /* radians; ~0.06 deg */
64 static const l_float32 Max1BppShearAngle = 0.06; /* radians; ~3 deg */
65 static const l_float32 LimitShearAngle = 0.35; /* radians; ~20 deg */
66 
67 /*------------------------------------------------------------------*
68  * General rotation about the center *
69  *------------------------------------------------------------------*/
100 PIX *
102  l_float32 angle,
103  l_int32 type,
104  l_int32 incolor,
105  l_int32 width,
106  l_int32 height)
107 {
108 l_int32 w, h, d;
109 l_uint32 fillval;
110 PIX *pix1, *pix2, *pix3, *pixd;
111 PIXCMAP *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)
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 
240 PIX *
242  l_float32 angle,
243  l_int32 incolor,
244  l_int32 width,
245  l_int32 height)
246 {
247 l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
248 l_float64 sina, cosa, fw, fh;
249 PIX *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  *------------------------------------------------------------------*/
319 PIX *
321  l_int32 xcen,
322  l_int32 ycen,
323  l_float32 angle,
324  l_int32 incolor)
325 {
326 l_int32 w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld;
327 l_uint32 val;
328 l_float32 sina, cosa;
329 l_uint32 *datad, *lined;
330 void **lines;
331 PIX *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  *------------------------------------------------------------------*/
450 PIX *
452  l_float32 angle,
453  l_int32 incolor)
454 {
455 PIX *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  *------------------------------------------------------------------*/
526 PIX *
528  l_float32 angle,
529  PIX *pixg,
530  l_float32 fract)
531 {
532 l_int32 ws, hs, d, spp;
533 PIX *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)
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:618
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:979
PIX * pixThresholdToBinary(PIX *pixs, l_int32 thresh)
pixThresholdToBinary()
Definition: grayquant.c:443
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1642
void pixDestroy(PIX **ppix)
pixDestroy()
Definition: pix1.c:608
l_ok pixGetDimensions(const PIX *pix, l_int32 *pw, l_int32 *ph, l_int32 *pd)
pixGetDimensions()
Definition: pix1.c:1074
l_ok pixCopySpp(PIX *pixd, const PIX *pixs)
pixCopySpp()
Definition: pix1.c:1187
PIX * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:380
l_ok pixCopyColormap(PIX *pixd, const PIX *pixs)
pixCopyColormap()
Definition: pix1.c:795
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:582
void ** pixGetLinePtrs(PIX *pix, l_int32 *psize)
pixGetLinePtrs()
Definition: pix1.c:1844
l_ok pixSetBorderRingVal(PIX *pixs, l_int32 dist, l_uint32 val)
pixSetBorderRingVal()
Definition: pix2.c:1623
l_ok pixSetAll(PIX *pix)
pixSetAll()
Definition: pix2.c:799
l_ok pixSetBlackOrWhite(PIX *pixs, l_int32 op)
pixSetBlackOrWhite()
Definition: pix2.c:997
l_ok pixSetRGBComponent(PIX *pixd, PIX *pixs, l_int32 comp)
pixSetRGBComponent()
Definition: pix2.c:2521
l_ok pixSetAllArbitrary(PIX *pix, l_uint32 val)
pixSetAllArbitrary()
Definition: pix2.c:929
PIX * pixResizeToMatch(PIX *pixs, PIX *pixt, l_int32 w, l_int32 h)
pixResizeToMatch()
Definition: pix5.c:1279
@ 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 * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:324
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3055
PIX * pixConvertTo32(PIX *pixs)
pixConvertTo32()
Definition: pixconv.c:3246
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 * pixRotateBySampling(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor)
pixRotateBySampling()
Definition: rotate.c:320
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 * pixRotateBinaryNice(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateBinaryNice()
Definition: rotate.c:451
PIX * pixRotateWithAlpha(PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract)
pixRotateWithAlpha()
Definition: rotate.c:527
PIX * pixEmbedForRotation(PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height)
pixEmbedForRotation()
Definition: rotate.c:241
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:233
PIX * pixRotateAMGray(PIX *pixs, l_float32 angle, l_uint8 grayval)
pixRotateAMGray()
Definition: rotateam.c:285
PIX * pixRotateShearCenter(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateShearCenter()
Definition: rotateshear.c:456