1 /*====================================================================*
2  -  Copyright (C) 2001 Leptonica.  All rights reserved.
3  -  This software is distributed in the hope that it will be
4  -  useful, but with NO WARRANTY OF ANY KIND.
5  -  No author or distributor accepts responsibility to anyone for the
6  -  consequences of using this software, or for whether it serves any
7  -  particular purpose or works at all, unless he or she says so in
8  -  writing.  Everyone is granted permission to copy, modify and
9  -  redistribute this source code, for commercial or non-commercial
10  -  purposes, with the following restrictions: (1) the origin of this
11  -  source code must not be misrepresented; (2) modified versions must
12  -  be plainly marked as such; and (3) this notice may not be removed
13  -  or altered from any source or modified source distribution.
14  *====================================================================*/
15 
16 
17 /*
18  *  rotate.c
19  *
20  *     General rotation about image center
21  *              PIX     *pixRotate()
22  *              PIX     *pixEmbedForRotation()
23  *
24  *     General rotation by sampling
25  *              PIX     *pixRotateBySampling()
26  *
27  *     Nice (slow) rotation of 1 bpp image
28  *              PIX     *pixRotateBinaryNice()
29  *
30  *     Rotations are measured in radians; clockwise is positive.
31  *
32  *     The general rotation pixRotate() does the best job for
33  *     rotating about the image center.  For 1 bpp, it uses shear;
34  *     for others, it uses either shear or area mapping.
35  *     If requested, it expands the output image so that no pixels are lost
36  *     in the rotation, and this can be done on multiple successive shears
37  *     without expanding beyond the maximum necessary size.
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <math.h>
43 #include "allheaders.h"
44 
45 static const l_float32  VERY_SMALL_ANGLE = 0.001;  /* radians; ~0.06 degrees */
46 
47 
48 /*------------------------------------------------------------------*
49  *                  General rotation about the center               *
50  *------------------------------------------------------------------*/
51 /*!
52  *  pixRotate()
53  *
54  *      Input:  pixs (1, 2, 4, 8, 32 bpp rgb)
55  *              angle (radians; clockwise is positive)
56  *              type (L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING)
57  *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
58  *              width (original width; use 0 to avoid embedding)
59  *              height (original height; use 0 to avoid embedding)
60  *      Return: pixd, or null on error
61  *
62  *  Notes:
63  *      (1) Rotation is about the center of the image.
64  *      (2) For very small rotations, just return a clone.
65  *      (3) Rotation brings either white or black pixels in
66  *          from outside the image.
67  *      (4) Above 20 degrees, if rotation by shear is requested, we rotate
68  *          by sampling.
69  *      (5) Colormaps are removed for rotation by area map and shear.
70  *      (6) The dest can be expanded so that no image pixels
71  *          are lost.  To invoke expansion, input the original
72  *          width and height.  For repeated rotation, use of the
73  *          original width and height allows the expansion to
74  *          stop at the maximum required size, which is a square
75  *          with side = sqrt(w*w + h*h).
76  *
77  *  *** Warning: implicit assumption about RGB component ordering ***
78  */
79 PIX *
pixRotate(PIX * pixs,l_float32 angle,l_int32 type,l_int32 incolor,l_int32 width,l_int32 height)80 pixRotate(PIX       *pixs,
81           l_float32  angle,
82           l_int32    type,
83           l_int32    incolor,
84           l_int32    width,
85           l_int32    height)
86 {
87 l_int32    w, h, d;
88 l_uint32   fillval;
89 PIX       *pixt1, *pixt2, *pixt3, *pixd;
90 PIXCMAP   *cmap;
91 
92     PROCNAME("pixRotate");
93 
94     if (!pixs)
95         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
96     if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP &&
97         type != L_ROTATE_SAMPLING)
98         return (PIX *)ERROR_PTR("invalid type", procName, NULL);
99     if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
100         return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
101 
102     if (L_ABS(angle) < VERY_SMALL_ANGLE)
103         return pixClone(pixs);
104 
105         /* Don't rotate by shear more than 20 degrees */
106     if (L_ABS(angle) > 0.35 && type == L_ROTATE_SHEAR) {
107         L_WARNING("large angle; rotating by sampling", procName);
108         type = L_ROTATE_SAMPLING;
109     }
110 
111         /* If 1 bpp and area map is requested, rotate by sampling */
112     d = pixGetDepth(pixs);
113     if (d == 1 && type == L_ROTATE_AREA_MAP) {
114         L_WARNING("1 bpp; rotating by sampling", procName);
115         type = L_ROTATE_SAMPLING;
116     }
117 
118         /* Remove colormap if we're rotating by area mapping. */
119     cmap = pixGetColormap(pixs);
120     if (cmap && type == L_ROTATE_AREA_MAP)
121         pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
122     else
123         pixt1 = pixClone(pixs);
124     cmap = pixGetColormap(pixt1);
125 
126         /* Otherwise, if there is a colormap and we're not embedding,
127          * add white color if it doesn't exist. */
128     if (cmap && width == 0) {  /* no embedding; generate @incolor */
129         if (incolor == L_BRING_IN_BLACK)
130             pixcmapAddBlackOrWhite(cmap, 0, NULL);
131         else  /* L_BRING_IN_WHITE */
132             pixcmapAddBlackOrWhite(cmap, 1, NULL);
133     }
134 
135         /* Request to embed in a larger image; do if necessary */
136     pixt2 = pixEmbedForRotation(pixt1, angle, incolor, width, height);
137 
138         /* Area mapping requires 8 or 32 bpp.
139          * If 1 bpp, default to sampling. */
140     d = pixGetDepth(pixt2);
141     if (type == L_ROTATE_AREA_MAP && d < 8)
142         pixt3 = pixConvertTo8(pixt2, FALSE);
143     else
144         pixt3 = pixClone(pixt2);
145 
146         /* Rotate by shear or area mapping */
147     pixGetDimensions(pixt3, &w, &h, &d);
148     if (type == L_ROTATE_SHEAR)
149         pixd = pixRotateShearCenter(pixt3, angle, incolor);
150     else if (type == L_ROTATE_SAMPLING)
151         pixd = pixRotateBySampling(pixt3, w / 2, h / 2, angle, incolor);
152     else {  /* rotate by area mapping */
153         fillval = 0;
154         if (incolor == L_BRING_IN_WHITE) {
155             if (d == 8)
156                 fillval = 255;
157             else  /* d == 32 */
158                 fillval = 0xffffff00;
159         }
160         if (d == 8)
161             pixd = pixRotateAMGray(pixt3, angle, fillval);
162         else   /* d == 32 */
163             pixd = pixRotateAMColor(pixt3, angle, fillval);
164     }
165 
166     pixDestroy(&pixt1);
167     pixDestroy(&pixt2);
168     pixDestroy(&pixt3);
169     return pixd;
170 }
171 
172 
173 /*!
174  *  pixEmbedForRotation()
175  *
176  *      Input:  pixs (1, 2, 4, 8, 32 bpp rgb)
177  *              angle (radians; clockwise is positive)
178  *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
179  *              width (original width; use 0 to avoid embedding)
180  *              height (original height; use 0 to avoid embedding)
181  *      Return: pixd, or null on error
182  *
183  *  Notes:
184  *      (1) For very small rotations, just return a clone.
185  *      (2) Generate larger image to embed pixs if necessary, and
186  *          place in the center.
187  *      (3) Rotation brings either white or black pixels in
188  *          from outside the image.  For colormapped images where
189  *          there is no white or black, a new color is added if
190  *          possible for these pixels; otherwise, either the
191  *          lightest or darkest color is used.  In most cases,
192  *          the colormap will be removed prior to rotation.
193  *      (4) The dest is to be expanded so that no image pixels
194  *          are lost after rotation.  Input of the original width
195  *          and height allows the expansion to stop at the maximum
196  *          required size, which is a square with side equal to
197  *          sqrt(w*w + h*h).
198  *      (5) For an arbitrary angle, the expansion can be found by
199  *          considering the UL and UR corners.  As the image is
200  *          rotated, these move in an arc centered at the center of
201  *          the image.  Normalize to a unit circle by dividing by half
202  *          the image diagonal.  After a rotation of T radians, the UL
203  *          and UR corners are at points T radians along the unit
204  *          circle.  Compute the x and y coordinates of both these
205  *          points and take the max of absolute values; these represent
206  *          the half width and half height of the containing rectangle.
207  *          The arithmetic is done using formulas for sin(a+b) and cos(a+b),
208  *          where b = T.  For the UR corner, sin(a) = h/d and cos(a) = w/d.
209  *          For the UL corner, replace a by (pi - a), and you have
210  *          sin(pi - a) = h/d, cos(pi - a) = -w/d.  The equations
211  *          given below follow directly.
212  */
213 PIX *
pixEmbedForRotation(PIX * pixs,l_float32 angle,l_int32 incolor,l_int32 width,l_int32 height)214 pixEmbedForRotation(PIX       *pixs,
215                     l_float32  angle,
216                     l_int32    incolor,
217                     l_int32    width,
218                     l_int32    height)
219 {
220 l_int32    w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff;
221 l_float64  sina, cosa, fw, fh;
222 PIX       *pixd;
223 
224     PROCNAME("pixEmbedForRotation");
225 
226     if (!pixs)
227         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
228     if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
229         return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
230     if (L_ABS(angle) < VERY_SMALL_ANGLE)
231         return pixClone(pixs);
232 
233         /* Test if big enough to hold any rotation of the original image */
234     pixGetDimensions(pixs, &w, &h, &d);
235     maxside = (l_int32)(sqrt((l_float64)(width * width) +
236                              (l_float64)(height * height)) + 0.5);
237     if (w >= maxside && h >= maxside)  /* big enough */
238         return pixClone(pixs);
239 
240         /* Find the new sizes required to hold the image after rotation */
241     cosa = cos(angle);
242     sina = sin(angle);
243     fw = (l_float64)w;
244     fh = (l_float64)h;
245     w1 = (l_int32)L_ABS(fw * cosa - fh * sina);
246     w2 = (l_int32)L_ABS(-fw * cosa - fh * sina);
247     h1 = (l_int32)L_ABS(fw * sina + fh * cosa);
248     h2 = (l_int32)L_ABS(-fw * sina + fh * cosa);
249     wnew = L_MAX(w1, w2);
250     hnew = L_MAX(h1, h2);
251 
252     if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
253         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
254     pixCopyResolution(pixd, pixs);
255     pixCopyColormap(pixd, pixs);
256     pixCopyText(pixd, pixs);
257     xoff = (wnew - w) / 2;
258     yoff = (hnew - h) / 2;
259 
260         /* Set background to color to be rotated in */
261     pixSetBlackOrWhite(pixd, incolor);
262 
263     pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
264     return pixd;
265 }
266 
267 
268 /*------------------------------------------------------------------*
269  *                    General rotation by sampling                  *
270  *------------------------------------------------------------------*/
271 /*!
272  *  pixRotateBySampling()
273  *
274  *      Input:  pixs (1, 2, 4, 8, 16, 32 bpp rgb; can be cmapped)
275  *              xcen (x value of center of rotation)
276  *              ycen (y value of center of rotation)
277  *              angle (radians; clockwise is positive)
278  *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
279  *      Return: pixd, or null on error
280  *
281  *  Notes:
282  *      (1) For very small rotations, just return a clone.
283  *      (2) Rotation brings either white or black pixels in
284  *          from outside the image.
285  *      (3) Colormaps are retained.
286  */
287 PIX *
pixRotateBySampling(PIX * pixs,l_int32 xcen,l_int32 ycen,l_float32 angle,l_int32 incolor)288 pixRotateBySampling(PIX       *pixs,
289                     l_int32    xcen,
290                     l_int32    ycen,
291                     l_float32  angle,
292                     l_int32    incolor)
293 {
294 l_int32    w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld;
295 l_uint32   val;
296 l_float32  sina, cosa;
297 l_uint32  *datad, *lined;
298 void     **lines;
299 PIX       *pixd;
300 
301     PROCNAME("pixRotateBySampling");
302 
303     if (!pixs)
304         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
305     if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
306         return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
307     pixGetDimensions(pixs, &w, &h, &d);
308     if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32)
309         return (PIX *)ERROR_PTR("invalid depth", procName, NULL);
310 
311     if (L_ABS(angle) < VERY_SMALL_ANGLE)
312         return pixClone(pixs);
313 
314     if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL)
315         return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
316     pixSetBlackOrWhite(pixd, incolor);
317 
318     sina = sin(angle);
319     cosa = cos(angle);
320     datad = pixGetData(pixd);
321     wpld = pixGetWpl(pixd);
322     wm1 = w - 1;
323     hm1 = h - 1;
324     lines = pixGetLinePtrs(pixs, NULL);
325 
326         /* Treat 1 bpp case specially */
327     if (d == 1) {
328         for (i = 0; i < h; i++) {  /* scan over pixd */
329             lined = datad + i * wpld;
330             ydif = ycen - i;
331             for (j = 0; j < w; j++) {
332                 xdif = xcen - j;
333                 x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
334                 if (x < 0 || x > wm1) continue;
335                 y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
336                 if (y < 0 || y > hm1) continue;
337                 if (incolor == L_BRING_IN_WHITE) {
338                     if (GET_DATA_BIT(lines[y], x))
339                         SET_DATA_BIT(lined, j);
340                 }
341                 else {
342                     if (!GET_DATA_BIT(lines[y], x))
343                         CLEAR_DATA_BIT(lined, j);
344                 }
345             }
346         }
347         FREE(lines);
348         return pixd;
349     }
350 
351     for (i = 0; i < h; i++) {  /* scan over pixd */
352         lined = datad + i * wpld;
353         ydif = ycen - i;
354         for (j = 0; j < w; j++) {
355             xdif = xcen - j;
356             x = xcen + (l_int32)(-xdif * cosa - ydif * sina);
357             if (x < 0 || x > wm1) continue;
358             y = ycen + (l_int32)(-ydif * cosa + xdif * sina);
359             if (y < 0 || y > hm1) continue;
360             switch (d)
361             {
362             case 8:
363                 val = GET_DATA_BYTE(lines[y], x);
364                 SET_DATA_BYTE(lined, j, val);
365                 break;
366             case 32:
367                 val = GET_DATA_FOUR_BYTES(lines[y], x);
368                 SET_DATA_FOUR_BYTES(lined, j, val);
369                 break;
370             case 2:
371                 val = GET_DATA_DIBIT(lines[y], x);
372                 SET_DATA_DIBIT(lined, j, val);
373                 break;
374             case 4:
375                 val = GET_DATA_QBIT(lines[y], x);
376                 SET_DATA_QBIT(lined, j, val);
377                 break;
378             case 16:
379                 val = GET_DATA_TWO_BYTES(lines[y], x);
380                 SET_DATA_TWO_BYTES(lined, j, val);
381                 break;
382             default:
383                 return (PIX *)ERROR_PTR("invalid depth", procName, NULL);
384             }
385         }
386     }
387 
388     FREE(lines);
389     return pixd;
390 }
391 
392 
393 /*------------------------------------------------------------------*
394  *                 Nice (slow) rotation of 1 bpp image              *
395  *------------------------------------------------------------------*/
396 /*!
397  *  pixRotateBinaryNice()
398  *
399  *      Input:  pixs (1 bpp)
400  *              angle (radians; clockwise is positive; about the center)
401  *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
402  *      Return: pixd, or null on error
403  *
404  *  Notes:
405  *      (1) For very small rotations, just return a clone.
406  *      (2) This does a computationally expensive rotation of 1 bpp images.
407  *          The fastest rotators (using shears or subsampling) leave
408  *          visible horizontal and vertical shear lines across which
409  *          the image shear changes by one pixel.  To ameliorate the
410  *          visual effect one can introduce random dithering.  One
411  *          way to do this in a not-too-random fashion is given here.
412  *          We convert to 8 bpp, do a very small blur, rotate using
413  *          linear interpolation (same as area mapping), do a
414  *          small amount of sharpening to compensate for the initial
415  *          blur, and threshold back to binary.  The shear lines
416  *          are magically removed.
417  *      (3) This operation is about 5x slower than rotation by sampling.
418  */
419 PIX *
pixRotateBinaryNice(PIX * pixs,l_float32 angle,l_int32 incolor)420 pixRotateBinaryNice(PIX       *pixs,
421                     l_float32  angle,
422                     l_int32    incolor)
423 {
424 PIX  *pixt1, *pixt2, *pixt3, *pixt4, *pixd;
425 
426     PROCNAME("pixRotateBinaryNice");
427 
428     if (!pixs || pixGetDepth(pixs) != 1)
429         return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL);
430     if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
431         return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
432 
433     pixt1 = pixConvertTo8(pixs, 0);
434     pixt2 = pixBlockconv(pixt1, 1, 1);  /* smallest blur allowed */
435     pixt3 = pixRotateAM(pixt2, angle, incolor);
436     pixt4 = pixUnsharpMasking(pixt3, 1, 1.0);  /* sharpen a bit */
437     pixd = pixThresholdToBinary(pixt4, 128);
438     pixDestroy(&pixt1);
439     pixDestroy(&pixt2);
440     pixDestroy(&pixt3);
441     pixDestroy(&pixt4);
442     return pixd;
443 }
444 
445 
446