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 
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  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 
242 PIX *
244  l_float32 angle,
245  l_int32 incolor,
246  l_int32 width,
247  l_int32 height)
248 {
249 l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
250 l_float64 sina, cosa, fw, fh;
251 PIX *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  *------------------------------------------------------------------*/
323 PIX *
325  l_int32 xcen,
326  l_int32 ycen,
327  l_float32 angle,
328  l_int32 incolor)
329 {
330 l_int32 w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld;
331 l_uint32 val;
332 l_float32 sina, cosa;
333 l_uint32 *datad, *lined;
334 void **lines;
335 PIX *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  *------------------------------------------------------------------*/
456 PIX *
458  l_float32 angle,
459  l_int32 incolor)
460 {
461 PIX *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  *------------------------------------------------------------------*/
534 PIX *
536  l_float32 angle,
537  PIX *pixg,
538  l_float32 fract)
539 {
540 l_int32 ws, hs, d, spp;
541 PIX *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 */
593  pixSetRGBComponent(pixd, pixgr, L_ALPHA_CHANNEL);
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
l_uint32 * pixGetData(PIX *pix)
pixGetData()
Definition: pix1.c:1763
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 * pixCreateTemplate(const PIX *pixs)
pixCreateTemplate()
Definition: pix1.c:383
l_ok pixCopyColormap(PIX *pixd, const PIX *pixs)
pixCopyColormap()
Definition: pix1.c:816
PIX * pixCreate(l_int32 width, l_int32 height, l_int32 depth)
pixCreate()
Definition: pix1.c:315
PIX * pixClone(PIX *pixs)
pixClone()
Definition: pix1.c:593
void ** pixGetLinePtrs(PIX *pix, l_int32 *psize)
pixGetLinePtrs()
Definition: pix1.c:1949
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 * pixRemoveColormap(PIX *pixs, l_int32 type)
pixRemoveColormap()
Definition: pixconv.c:328
PIX * pixConvertTo8(PIX *pixs, l_int32 cmapflag)
pixConvertTo8()
Definition: pixconv.c:3133
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 * pixRotateBySampling(PIX *pixs, l_int32 xcen, l_int32 ycen, l_float32 angle, l_int32 incolor)
pixRotateBySampling()
Definition: rotate.c:324
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:457
PIX * pixRotateWithAlpha(PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract)
pixRotateWithAlpha()
Definition: rotate.c:535
PIX * pixEmbedForRotation(PIX *pixs, l_float32 angle, l_int32 incolor, l_int32 width, l_int32 height)
pixEmbedForRotation()
Definition: rotate.c:243
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 * pixRotateAMGray(PIX *pixs, l_float32 angle, l_uint8 grayval)
pixRotateAMGray()
Definition: rotateam.c:289
PIX * pixRotateShearCenter(PIX *pixs, l_float32 angle, l_int32 incolor)
pixRotateShearCenter()
Definition: rotateshear.c:464
Definition: pix.h:139