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