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  *  rotateam.c
19  *
20  *     Grayscale and color rotation for area mapping (== interpolation)
21  *
22  *         Rotation about the image center
23  *                  PIX     *pixRotateAM()
24  *                  PIX     *pixRotateAMColor()
25  *                  PIX     *pixRotateAMGray()
26  *
27  *         Rotation about the UL corner of the image
28  *                  PIX     *pixRotateAMCorner()
29  *                  PIX     *pixRotateAMColorCorner()
30  *                  PIX     *pixRotateAMGrayCorner()
31  *
32  *         Faster color rotation about the image center
33  *                  PIX     *pixRotateAMColorFast()
34  *
35  *     Rotations are measured in radians; clockwise is positive.
36  *
37  *     The basic area mapping grayscale rotation works on 8 bpp images.
38  *     For color, the same method is applied to each color separately.
39  *     This can be done in two ways: (1) as here, computing each dest
40  *     rgb pixel from the appropriate four src rgb pixels, or (2) separating
41  *     the color image into three 8 bpp images, rotate each of these,
42  *     and then combine the result.  Method (1) is about 2.5x faster.
43  *     We have also implemented a fast approximation for color area-mapping
44  *     rotation (pixRotateAMColorFast()), which is about 25% faster
45  *     than the standard color rotator.  If you need the extra speed,
46  *     use it.
47  *
48  *     Area mapping works as follows.  For each dest
49  *     pixel you find the 4 source pixels that it partially
50  *     covers.  You then compute the dest pixel value as
51  *     the area-weighted average of those 4 source pixels.
52  *     We make two simplifying approximations:
53  *
54  *       -  For simplicity, compute the areas as if the dest
55  *          pixel were translated but not rotated.
56  *
57  *       -  Compute area overlaps on a discrete sub-pixel grid.
58  *          Because we are using 8 bpp images with 256 levels,
59  *          it is convenient to break each pixel into a
60  *          16x16 sub-pixel grid, and count the number of
61  *          overlapped sub-pixels.
62  *
63  *     It is interesting to note that the digital filter that
64  *     implements the area mapping algorithm for rotation
65  *     is identical to the digital filter used for linear
66  *     interpolation when arbitrarily scaling grayscale images.
67  *
68  *     The advantage of area mapping over pixel sampling
69  *     in grayscale rotation is that the former naturally
70  *     blurs sharp edges ("anti-aliasing"), so that stair-step
71  *     artifacts are not introduced.  The disadvantage is that
72  *     it is significantly slower.
73  *
74  *     But it is still pretty fast.  With standard 3 GHz hardware,
75  *     the anti-aliased (area-mapped) color rotation speed is
76  *     about 15 million pixels/sec.
77  *
78  *     The function pixRotateAMColorFast() is about 10-20% faster
79  *     than pixRotateAMColor().  The quality is slightly worse,
80  *     and if you make many successive small rotations, with a
81  *     total angle of 360 degrees, it has been noted that the
82  *     center wanders -- it seems to be doing a 1 pixel translation
83  *     in addition to the rotation.
84  */
85 
86 #include <stdio.h>
87 #include <string.h>
88 #include "allheaders.h"
89 
90 static const l_float32  VERY_SMALL_ANGLE = 0.001;  /* radians; ~0.06 degrees */
91 
92 
93 /*------------------------------------------------------------------*
94  *                     Rotation about the center                    *
95  *------------------------------------------------------------------*/
96 /*!
97  *  pixRotateAM()
98  *
99  *      Input:  pixs (2, 4, 8 bpp gray or colormapped, or 32 bpp RGB)
100  *              angle (radians; clockwise is positive)
101  *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
102  *      Return: pixd, or null on error
103  *
104  *  Notes:
105  *      (1) Rotates about image center.
106  *      (2) A positive angle gives a clockwise rotation.
107  *      (3) Brings in either black or white pixels from the boundary.
108  */
109 PIX *
pixRotateAM(PIX * pixs,l_float32 angle,l_int32 incolor)110 pixRotateAM(PIX       *pixs,
111             l_float32  angle,
112             l_int32    incolor)
113 {
114 l_int32   d;
115 l_uint32  fillval;
116 PIX      *pixt1, *pixt2, *pixd;
117 
118     PROCNAME("pixRotateAM");
119 
120     if (!pixs)
121         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
122     if (pixGetDepth(pixs) == 1)
123         return (PIX *)ERROR_PTR("pixs is 1 bpp", procName, NULL);
124 
125     if (L_ABS(angle) < VERY_SMALL_ANGLE)
126         return pixClone(pixs);
127 
128         /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
129     pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
130     d = pixGetDepth(pixt1);
131     if (d < 8)
132         pixt2 = pixConvertTo8(pixt1, FALSE);
133     else
134         pixt2 = pixClone(pixt1);
135     d = pixGetDepth(pixt2);
136 
137         /* Compute actual incoming color */
138     fillval = 0;
139     if (incolor == L_BRING_IN_WHITE) {
140         if (d == 8)
141             fillval = 255;
142         else  /* d == 32 */
143             fillval = 0xffffff00;
144     }
145 
146     if (d == 8)
147         pixd = pixRotateAMGray(pixt2, angle, fillval);
148     else   /* d == 32 */
149         pixd = pixRotateAMColor(pixt2, angle, fillval);
150 
151     pixDestroy(&pixt1);
152     pixDestroy(&pixt2);
153     return pixd;
154 }
155 
156 
157 /*!
158  *  pixRotateAMColor()
159  *
160  *      Input:  pixs (32 bpp)
161  *              angle (radians; clockwise is positive)
162  *              colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE)
163  *      Return: pixd, or null on error
164  *
165  *  Notes:
166  *      (1) Rotates about image center.
167  *      (2) A positive angle gives a clockwise rotation.
168  *      (3) Specify the color to be brought in from outside the image.
169  */
170 PIX *
pixRotateAMColor(PIX * pixs,l_float32 angle,l_uint32 colorval)171 pixRotateAMColor(PIX       *pixs,
172                  l_float32  angle,
173                  l_uint32   colorval)
174 {
175 l_int32    w, h, wpls, wpld;
176 l_uint32  *datas, *datad;
177 PIX       *pixd;
178 
179     PROCNAME("pixRotateAMColor");
180 
181     if (!pixs)
182         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
183     if (pixGetDepth(pixs) != 32)
184         return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL);
185 
186     if (L_ABS(angle) < VERY_SMALL_ANGLE)
187         return pixClone(pixs);
188 
189     pixGetDimensions(pixs, &w, &h, NULL);
190     datas = pixGetData(pixs);
191     wpls = pixGetWpl(pixs);
192     pixd = pixCreateTemplate(pixs);
193     datad = pixGetData(pixd);
194     wpld = pixGetWpl(pixd);
195 
196     rotateAMColorLow(datad, w, h, wpld, datas, wpls, angle, colorval);
197 
198     return pixd;
199 }
200 
201 
202 /*!
203  *  pixRotateAMGray()
204  *
205  *      Input:  pixs (8 bpp)
206  *              angle (radians; clockwise is positive)
207  *              grayval (0 to bring in BLACK, 255 for WHITE)
208  *      Return: pixd, or null on error
209  *
210  *  Notes:
211  *      (1) Rotates about image center.
212  *      (2) A positive angle gives a clockwise rotation.
213  *      (3) Specify the grayvalue to be brought in from outside the image.
214  */
215 PIX *
pixRotateAMGray(PIX * pixs,l_float32 angle,l_uint8 grayval)216 pixRotateAMGray(PIX       *pixs,
217                 l_float32  angle,
218                 l_uint8    grayval)
219 {
220 l_int32    w, h, wpls, wpld;
221 l_uint32  *datas, *datad;
222 PIX        *pixd;
223 
224     PROCNAME("pixRotateAMGray");
225 
226     if (!pixs)
227         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
228     if (pixGetDepth(pixs) != 8)
229         return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL);
230 
231     if (L_ABS(angle) < VERY_SMALL_ANGLE)
232         return pixClone(pixs);
233 
234     pixGetDimensions(pixs, &w, &h, NULL);
235     datas = pixGetData(pixs);
236     wpls = pixGetWpl(pixs);
237     pixd = pixCreateTemplate(pixs);
238     datad = pixGetData(pixd);
239     wpld = pixGetWpl(pixd);
240 
241     rotateAMGrayLow(datad, w, h, wpld, datas, wpls, angle, grayval);
242 
243     return pixd;
244 }
245 
246 
247 /*------------------------------------------------------------------*
248  *                    Rotation about the UL corner                  *
249  *------------------------------------------------------------------*/
250 /*!
251  *  pixRotateAMCorner()
252  *
253  *      Input:  pixs (1, 2, 4, 8 bpp gray or colormapped, or 32 bpp RGB)
254  *              angle (radians; clockwise is positive)
255  *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
256  *      Return: pixd, or null on error
257  *
258  *  Notes:
259  *      (1) Rotates about the UL corner of the image.
260  *      (2) A positive angle gives a clockwise rotation.
261  *      (3) Brings in either black or white pixels from the boundary.
262  */
263 PIX *
pixRotateAMCorner(PIX * pixs,l_float32 angle,l_int32 incolor)264 pixRotateAMCorner(PIX       *pixs,
265                   l_float32  angle,
266                   l_int32    incolor)
267 {
268 l_int32   d;
269 l_uint32  fillval;
270 PIX      *pixt1, *pixt2, *pixd;
271 
272     PROCNAME("pixRotateAMCorner");
273 
274     if (!pixs)
275         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
276 
277     if (L_ABS(angle) < VERY_SMALL_ANGLE)
278         return pixClone(pixs);
279 
280         /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
281     pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
282     d = pixGetDepth(pixt1);
283     if (d < 8)
284         pixt2 = pixConvertTo8(pixt1, FALSE);
285     else
286         pixt2 = pixClone(pixt1);
287     d = pixGetDepth(pixt2);
288 
289         /* Compute actual incoming color */
290     fillval = 0;
291     if (incolor == L_BRING_IN_WHITE) {
292         if (d == 8)
293             fillval = 255;
294         else  /* d == 32 */
295             fillval = 0xffffff00;
296     }
297 
298     if (d == 8)
299         pixd = pixRotateAMGrayCorner(pixt2, angle, fillval);
300     else   /* d == 32 */
301         pixd = pixRotateAMColorCorner(pixt2, angle, fillval);
302 
303     pixDestroy(&pixt1);
304     pixDestroy(&pixt2);
305     return pixd;
306 }
307 
308 
309 /*!
310  *  pixRotateAMColorCorner()
311  *
312  *      Input:  pixs
313  *              angle (radians; clockwise is positive)
314  *              colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE)
315  *      Return: pixd, or null on error
316  *
317  *  Notes:
318  *      (1) Rotates the image about the UL corner.
319  *      (2) A positive angle gives a clockwise rotation.
320  *      (3) Specify the color to be brought in from outside the image.
321  */
322 PIX *
pixRotateAMColorCorner(PIX * pixs,l_float32 angle,l_uint32 fillval)323 pixRotateAMColorCorner(PIX       *pixs,
324                        l_float32  angle,
325                        l_uint32   fillval)
326 {
327 l_int32    w, h, wpls, wpld;
328 l_uint32  *datas, *datad;
329 PIX       *pixd;
330 
331     PROCNAME("pixRotateAMColorCorner");
332 
333     if (!pixs)
334         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
335     if (pixGetDepth(pixs) != 32)
336         return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL);
337 
338     if (L_ABS(angle) < VERY_SMALL_ANGLE)
339         return pixClone(pixs);
340 
341     pixGetDimensions(pixs, &w, &h, NULL);
342     datas = pixGetData(pixs);
343     wpls = pixGetWpl(pixs);
344     pixd = pixCreateTemplate(pixs);
345     datad = pixGetData(pixd);
346     wpld = pixGetWpl(pixd);
347 
348     rotateAMColorCornerLow(datad, w, h, wpld, datas, wpls, angle, fillval);
349 
350     return pixd;
351 }
352 
353 
354 /*!
355  *  pixRotateAMGrayCorner()
356  *
357  *      Input:  pixs
358  *              angle (radians; clockwise is positive)
359  *              grayval (0 to bring in BLACK, 255 for WHITE)
360  *      Return: pixd, or null on error
361  *
362  *  Notes:
363  *      (1) Rotates the image about the UL corner.
364  *      (2) A positive angle gives a clockwise rotation.
365  *      (3) Specify the grayvalue to be brought in from outside the image.
366  */
367 PIX *
pixRotateAMGrayCorner(PIX * pixs,l_float32 angle,l_uint8 grayval)368 pixRotateAMGrayCorner(PIX       *pixs,
369                       l_float32  angle,
370                       l_uint8    grayval)
371 {
372 l_int32    w, h, wpls, wpld;
373 l_uint32  *datas, *datad;
374 PIX       *pixd;
375 
376     PROCNAME("pixRotateAMGrayCorner");
377 
378     if (!pixs)
379         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
380     if (pixGetDepth(pixs) != 8)
381         return (PIX *)ERROR_PTR("pixs must be 8 bpp", procName, NULL);
382 
383     if (L_ABS(angle) < VERY_SMALL_ANGLE)
384         return pixClone(pixs);
385 
386     pixGetDimensions(pixs, &w, &h, NULL);
387     datas = pixGetData(pixs);
388     wpls = pixGetWpl(pixs);
389     pixd = pixCreateTemplate(pixs);
390     datad = pixGetData(pixd);
391     wpld = pixGetWpl(pixd);
392 
393     rotateAMGrayCornerLow(datad, w, h, wpld, datas, wpls, angle, grayval);
394 
395     return pixd;
396 }
397 
398 
399 /*------------------------------------------------------------------*
400  *                    Fast rotation about the center                *
401  *------------------------------------------------------------------*/
402 /*!
403  *  pixRotateAMColorFast()
404  *
405  *      Input:  pixs
406  *              angle (radians; clockwise is positive)
407  *              colorval (e.g., 0 to bring in BLACK, 0xffffff00 for WHITE)
408  *      Return: pixd, or null on error
409  *
410  *  Notes:
411  *      (1) This rotates a color image about the image center.
412  *      (2) A positive angle gives a clockwise rotation.
413  *      (3) It uses area mapping, dividing each pixel into
414  *          16 subpixels.
415  *      (4) It is about 10% to 20% faster than the more accurate linear
416  *          interpolation function pixRotateAMColor(),
417  *          which uses 256 subpixels.
418  *
419  *  *** Warning: implicit assumption about RGB component ordering ***
420  */
421 PIX *
pixRotateAMColorFast(PIX * pixs,l_float32 angle,l_uint32 colorval)422 pixRotateAMColorFast(PIX       *pixs,
423                      l_float32  angle,
424                      l_uint32   colorval)
425 {
426 l_int32    w, h, wpls, wpld;
427 l_uint32  *datas, *datad;
428 PIX       *pixd;
429 
430     PROCNAME("pixRotateAMColorFast");
431 
432     if (!pixs)
433         return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
434     if (pixGetDepth(pixs) != 32)
435         return (PIX *)ERROR_PTR("pixs must be 32 bpp", procName, NULL);
436 
437     if (L_ABS(angle) < VERY_SMALL_ANGLE)
438         return pixClone(pixs);
439 
440     pixGetDimensions(pixs, &w, &h, NULL);
441     datas = pixGetData(pixs);
442     wpls = pixGetWpl(pixs);
443     pixd = pixCreateTemplate(pixs);
444     datad = pixGetData(pixd);
445     wpld = pixGetWpl(pixd);
446 
447     rotateAMColorFastLow(datad, w, h, wpld, datas, wpls, angle, colorval);
448 
449     return pixd;
450 }
451 
452