comparison mupdf-source/thirdparty/leptonica/src/rotateam.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
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
27
28 /*!
29 * \file rotateam.c
30 * <pre>
31 *
32 * Grayscale and color rotation for area mapping (== interpolation)
33 *
34 * Rotation about the image center
35 * PIX *pixRotateAM()
36 * PIX *pixRotateAMColor()
37 * PIX *pixRotateAMGray()
38 * static void rotateAMColorLow()
39 * static void rotateAMGrayLow()
40 *
41 * Rotation about the UL corner of the image
42 * PIX *pixRotateAMCorner()
43 * PIX *pixRotateAMColorCorner()
44 * PIX *pixRotateAMGrayCorner()
45 * static void rotateAMColorCornerLow()
46 * static void rotateAMGrayCornerLow()
47 *
48 * Faster color rotation about the image center
49 * PIX *pixRotateAMColorFast()
50 * static void rotateAMColorFastLow()
51 *
52 * Rotations are measured in radians; clockwise is positive.
53 *
54 * The basic area mapping grayscale rotation works on 8 bpp images.
55 * For color, the same method is applied to each color separately.
56 * This can be done in two ways: (1) as here, computing each dest
57 * rgb pixel from the appropriate four src rgb pixels, or (2) separating
58 * the color image into three 8 bpp images, rotate each of these,
59 * and then combine the result. Method (1) is about 2.5x faster.
60 * We have also implemented a fast approximation for color area-mapping
61 * rotation (pixRotateAMColorFast()), which is about 25% faster
62 * than the standard color rotator. If you need the extra speed,
63 * use it.
64 *
65 * Area mapping works as follows. For each dest
66 * pixel you find the 4 source pixels that it partially
67 * covers. You then compute the dest pixel value as
68 * the area-weighted average of those 4 source pixels.
69 * We make two simplifying approximations:
70 *
71 * ~ For simplicity, compute the areas as if the dest
72 * pixel were translated but not rotated.
73 *
74 * ~ Compute area overlaps on a discrete sub-pixel grid.
75 * Because we are using 8 bpp images with 256 levels,
76 * it is convenient to break each pixel into a
77 * 16x16 sub-pixel grid, and count the number of
78 * overlapped sub-pixels.
79 *
80 * It is interesting to note that the digital filter that
81 * implements the area mapping algorithm for rotation
82 * is identical to the digital filter used for linear
83 * interpolation when arbitrarily scaling grayscale images.
84 *
85 * The advantage of area mapping over pixel sampling
86 * in grayscale rotation is that the former naturally
87 * blurs sharp edges ("anti-aliasing"), so that stair-step
88 * artifacts are not introduced. The disadvantage is that
89 * it is significantly slower.
90 *
91 * But it is still pretty fast. With standard 3 GHz hardware,
92 * the anti-aliased (area-mapped) color rotation speed is
93 * about 15 million pixels/sec.
94 *
95 * The function pixRotateAMColorFast() is about 10-20% faster
96 * than pixRotateAMColor(). The quality is slightly worse,
97 * and if you make many successive small rotations, with a
98 * total angle of 360 degrees, it has been noted that the
99 * center wanders -- it seems to be doing a 1 pixel translation
100 * in addition to the rotation.
101 *
102 * Consider again the comparison of image quality between sampling
103 * and area mapping. With sampling, sharp edges such as found in
104 * text images remain sharp. However, sampling artifacts such as
105 * characters randomly bouncing up and down by one pixel, or
106 * one pixel horizontal shear lines going through a line of text
107 * (causing the characters to look like badly rendered italic),
108 * are highly visible. It does not help to sample the source pixel
109 * with the largest area covering each dest pixel; the result has
110 * the same ugly sampling artifacts.
111 *
112 * With area mapping, these annoying artifacts are avoided, but the
113 * blurring of edges makes small text a bit more difficult to read.
114 * However, if you are willing to do more computation, you can have
115 * the best of both worlds: no sampling artifacts and sharp edges.
116 * Use area mapping to avoid sampling issues, and follow it with
117 * unsharp masking. Experiment with the sharpening parameters.
118 * I have found that a small amount of sharpening is sufficient to
119 * restore the sharp edges in text; e.g.,
120 * pix2 = pixUnsharpMasking(pix1, 1, 0.3);
121 * </pre>
122 */
123
124 #ifdef HAVE_CONFIG_H
125 #include <config_auto.h>
126 #endif /* HAVE_CONFIG_H */
127
128 #include <string.h>
129 #include <math.h> /* required for sin and tan */
130 #include "allheaders.h"
131
132 static void rotateAMColorLow(l_uint32 *datad, l_int32 w, l_int32 h,
133 l_int32 wpld, l_uint32 *datas, l_int32 wpls,
134 l_float32 angle, l_uint32 colorval);
135 static void rotateAMGrayLow(l_uint32 *datad, l_int32 w, l_int32 h,
136 l_int32 wpld, l_uint32 *datas, l_int32 wpls,
137 l_float32 angle, l_uint8 grayval);
138 static void rotateAMColorCornerLow(l_uint32 *datad, l_int32 w, l_int32 h,
139 l_int32 wpld, l_uint32 *datas,
140 l_int32 wpls, l_float32 angle,
141 l_uint32 colorval);
142 static void rotateAMGrayCornerLow(l_uint32 *datad, l_int32 w, l_int32 h,
143 l_int32 wpld, l_uint32 *datas, l_int32 wpls,
144 l_float32 angle, l_uint8 grayval);
145
146 static void rotateAMColorFastLow(l_uint32 *datad, l_int32 w, l_int32 h,
147 l_int32 wpld, l_uint32 *datas, l_int32 wpls,
148 l_float32 angle, l_uint32 colorval);
149
150 static const l_float32 MinAngleToRotate = 0.001f; /* radians; ~0.06 deg */
151
152
153 /*------------------------------------------------------------------*
154 * Rotation about the center *
155 *------------------------------------------------------------------*/
156 /*!
157 * \brief pixRotateAM()
158 *
159 * \param[in] pixs 2, 4, 8 bpp gray or colormapped, or 32 bpp RGB
160 * \param[in] angle radians; clockwise is positive
161 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
162 * \return pixd, or NULL on error
163 *
164 * <pre>
165 * Notes:
166 * (1) Rotates about image center.
167 * (2) A positive angle gives a clockwise rotation.
168 * (3) Brings in either black or white pixels from the boundary.
169 * </pre>
170 */
171 PIX *
172 pixRotateAM(PIX *pixs,
173 l_float32 angle,
174 l_int32 incolor)
175 {
176 l_int32 d;
177 l_uint32 fillval;
178 PIX *pixt1, *pixt2, *pixd;
179
180 if (!pixs)
181 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
182 if (pixGetDepth(pixs) == 1)
183 return (PIX *)ERROR_PTR("pixs is 1 bpp", __func__, NULL);
184
185 if (L_ABS(angle) < MinAngleToRotate)
186 return pixClone(pixs);
187
188 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
189 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
190 d = pixGetDepth(pixt1);
191 if (d < 8)
192 pixt2 = pixConvertTo8(pixt1, FALSE);
193 else
194 pixt2 = pixClone(pixt1);
195 d = pixGetDepth(pixt2);
196
197 /* Compute actual incoming color */
198 fillval = 0;
199 if (incolor == L_BRING_IN_WHITE) {
200 if (d == 8)
201 fillval = 255;
202 else /* d == 32 */
203 fillval = 0xffffff00;
204 }
205
206 if (d == 8)
207 pixd = pixRotateAMGray(pixt2, angle, fillval);
208 else /* d == 32 */
209 pixd = pixRotateAMColor(pixt2, angle, fillval);
210
211 pixDestroy(&pixt1);
212 pixDestroy(&pixt2);
213 return pixd;
214 }
215
216
217 /*!
218 * \brief pixRotateAMColor()
219 *
220 * \param[in] pixs 32 bpp
221 * \param[in] angle radians; clockwise is positive
222 * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
223 * \return pixd, or NULL on error
224 *
225 * <pre>
226 * Notes:
227 * (1) Rotates about image center.
228 * (2) A positive angle gives a clockwise rotation.
229 * (3) Specify the color to be brought in from outside the image.
230 * </pre>
231 */
232 PIX *
233 pixRotateAMColor(PIX *pixs,
234 l_float32 angle,
235 l_uint32 colorval)
236 {
237 l_int32 w, h, wpls, wpld;
238 l_uint32 *datas, *datad;
239 PIX *pix1, *pix2, *pixd;
240
241 if (!pixs)
242 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
243 if (pixGetDepth(pixs) != 32)
244 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
245
246 if (L_ABS(angle) < MinAngleToRotate)
247 return pixClone(pixs);
248
249 pixGetDimensions(pixs, &w, &h, NULL);
250 datas = pixGetData(pixs);
251 wpls = pixGetWpl(pixs);
252 pixd = pixCreateTemplate(pixs);
253 datad = pixGetData(pixd);
254 wpld = pixGetWpl(pixd);
255
256 rotateAMColorLow(datad, w, h, wpld, datas, wpls, angle, colorval);
257 if (pixGetSpp(pixs) == 4) {
258 pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
259 pix2 = pixRotateAMGray(pix1, angle, 255); /* bring in opaque */
260 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
261 pixDestroy(&pix1);
262 pixDestroy(&pix2);
263 }
264
265 return pixd;
266 }
267
268
269 /*!
270 * \brief pixRotateAMGray()
271 *
272 * \param[in] pixs 8 bpp
273 * \param[in] angle radians; clockwise is positive
274 * \param[in] grayval 0 to bring in BLACK, 255 for WHITE
275 * \return pixd, or NULL on error
276 *
277 * <pre>
278 * Notes:
279 * (1) Rotates about image center.
280 * (2) A positive angle gives a clockwise rotation.
281 * (3) Specify the grayvalue to be brought in from outside the image.
282 * </pre>
283 */
284 PIX *
285 pixRotateAMGray(PIX *pixs,
286 l_float32 angle,
287 l_uint8 grayval)
288 {
289 l_int32 w, h, wpls, wpld;
290 l_uint32 *datas, *datad;
291 PIX *pixd;
292
293 if (!pixs)
294 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
295 if (pixGetDepth(pixs) != 8)
296 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
297
298 if (L_ABS(angle) < MinAngleToRotate)
299 return pixClone(pixs);
300
301 pixGetDimensions(pixs, &w, &h, NULL);
302 datas = pixGetData(pixs);
303 wpls = pixGetWpl(pixs);
304 pixd = pixCreateTemplate(pixs);
305 datad = pixGetData(pixd);
306 wpld = pixGetWpl(pixd);
307
308 rotateAMGrayLow(datad, w, h, wpld, datas, wpls, angle, grayval);
309
310 return pixd;
311 }
312
313
314 static void
315 rotateAMColorLow(l_uint32 *datad,
316 l_int32 w,
317 l_int32 h,
318 l_int32 wpld,
319 l_uint32 *datas,
320 l_int32 wpls,
321 l_float32 angle,
322 l_uint32 colorval)
323 {
324 l_int32 i, j, xcen, ycen, wm2, hm2;
325 l_int32 xdif, ydif, xpm, ypm, xp, yp, xf, yf;
326 l_int32 rval, gval, bval;
327 l_uint32 word00, word01, word10, word11;
328 l_uint32 *lines, *lined;
329 l_float32 sina, cosa;
330
331 xcen = w / 2;
332 wm2 = w - 2;
333 ycen = h / 2;
334 hm2 = h - 2;
335 sina = 16.f * sin(angle);
336 cosa = 16.f * cos(angle);
337
338 for (i = 0; i < h; i++) {
339 ydif = ycen - i;
340 lined = datad + i * wpld;
341 for (j = 0; j < w; j++) {
342 xdif = xcen - j;
343 xpm = (l_int32)(-xdif * cosa - ydif * sina);
344 ypm = (l_int32)(-ydif * cosa + xdif * sina);
345 xp = xcen + (xpm >> 4);
346 yp = ycen + (ypm >> 4);
347 xf = xpm & 0x0f;
348 yf = ypm & 0x0f;
349
350 /* if off the edge, write input colorval */
351 if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
352 *(lined + j) = colorval;
353 continue;
354 }
355
356 lines = datas + yp * wpls;
357
358 /* do area weighting. Without this, we would
359 * simply do:
360 * *(lined + j) = *(lines + xp);
361 * which is faster but gives lousy results!
362 */
363 word00 = *(lines + xp);
364 word10 = *(lines + xp + 1);
365 word01 = *(lines + wpls + xp);
366 word11 = *(lines + wpls + xp + 1);
367 rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) +
368 xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) +
369 (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) +
370 xf * yf * ((word11 >> L_RED_SHIFT) & 0xff) + 128) / 256;
371 gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) +
372 xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) +
373 (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) +
374 xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff) + 128) / 256;
375 bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) +
376 xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) +
377 (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) +
378 xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff) + 128) / 256;
379 composeRGBPixel(rval, gval, bval, lined + j);
380 }
381 }
382 }
383
384
385 static void
386 rotateAMGrayLow(l_uint32 *datad,
387 l_int32 w,
388 l_int32 h,
389 l_int32 wpld,
390 l_uint32 *datas,
391 l_int32 wpls,
392 l_float32 angle,
393 l_uint8 grayval)
394 {
395 l_int32 i, j, xcen, ycen, wm2, hm2;
396 l_int32 xdif, ydif, xpm, ypm, xp, yp, xf, yf;
397 l_int32 v00, v01, v10, v11;
398 l_uint8 val;
399 l_uint32 *lines, *lined;
400 l_float32 sina, cosa;
401
402 xcen = w / 2;
403 wm2 = w - 2;
404 ycen = h / 2;
405 hm2 = h - 2;
406 sina = 16.f * sin(angle);
407 cosa = 16.f * cos(angle);
408
409 for (i = 0; i < h; i++) {
410 ydif = ycen - i;
411 lined = datad + i * wpld;
412 for (j = 0; j < w; j++) {
413 xdif = xcen - j;
414 xpm = (l_int32)(-xdif * cosa - ydif * sina);
415 ypm = (l_int32)(-ydif * cosa + xdif * sina);
416 xp = xcen + (xpm >> 4);
417 yp = ycen + (ypm >> 4);
418 xf = xpm & 0x0f;
419 yf = ypm & 0x0f;
420
421 /* if off the edge, write input grayval */
422 if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
423 SET_DATA_BYTE(lined, j, grayval);
424 continue;
425 }
426
427 lines = datas + yp * wpls;
428
429 /* do area weighting. Without this, we would
430 * simply do:
431 * SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp));
432 * which is faster but gives lousy results!
433 */
434 v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp);
435 v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp + 1);
436 v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp);
437 v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp + 1);
438 val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256);
439 SET_DATA_BYTE(lined, j, val);
440 }
441 }
442 }
443
444
445 /*------------------------------------------------------------------*
446 * Rotation about the UL corner *
447 *------------------------------------------------------------------*/
448 /*!
449 * \brief pixRotateAMCorner()
450 *
451 * \param[in] pixs 1, 2, 4, 8 bpp gray or colormapped, or 32 bpp RGB
452 * \param[in] angle radians; clockwise is positive
453 * \param[in] incolor L_BRING_IN_WHITE, L_BRING_IN_BLACK
454 * \return pixd, or NULL on error
455 *
456 * <pre>
457 * Notes:
458 * (1) Rotates about the UL corner of the image.
459 * (2) A positive angle gives a clockwise rotation.
460 * (3) Brings in either black or white pixels from the boundary.
461 * </pre>
462 */
463 PIX *
464 pixRotateAMCorner(PIX *pixs,
465 l_float32 angle,
466 l_int32 incolor)
467 {
468 l_int32 d;
469 l_uint32 fillval;
470 PIX *pixt1, *pixt2, *pixd;
471
472 if (!pixs)
473 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
474
475 if (L_ABS(angle) < MinAngleToRotate)
476 return pixClone(pixs);
477
478 /* Remove cmap if it exists, and unpack to 8 bpp if necessary */
479 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC);
480 d = pixGetDepth(pixt1);
481 if (d < 8)
482 pixt2 = pixConvertTo8(pixt1, FALSE);
483 else
484 pixt2 = pixClone(pixt1);
485 d = pixGetDepth(pixt2);
486
487 /* Compute actual incoming color */
488 fillval = 0;
489 if (incolor == L_BRING_IN_WHITE) {
490 if (d == 8)
491 fillval = 255;
492 else /* d == 32 */
493 fillval = 0xffffff00;
494 }
495
496 if (d == 8)
497 pixd = pixRotateAMGrayCorner(pixt2, angle, fillval);
498 else /* d == 32 */
499 pixd = pixRotateAMColorCorner(pixt2, angle, fillval);
500
501 pixDestroy(&pixt1);
502 pixDestroy(&pixt2);
503 return pixd;
504 }
505
506
507 /*!
508 * \brief pixRotateAMColorCorner()
509 *
510 * \param[in] pixs
511 * \param[in] angle radians; clockwise is positive
512 * \param[in] fillval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
513 * \return pixd, or NULL on error
514 *
515 * <pre>
516 * Notes:
517 * (1) Rotates the image about the UL corner.
518 * (2) A positive angle gives a clockwise rotation.
519 * (3) Specify the color to be brought in from outside the image.
520 * </pre>
521 */
522 PIX *
523 pixRotateAMColorCorner(PIX *pixs,
524 l_float32 angle,
525 l_uint32 fillval)
526 {
527 l_int32 w, h, wpls, wpld;
528 l_uint32 *datas, *datad;
529 PIX *pix1, *pix2, *pixd;
530
531 if (!pixs)
532 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
533 if (pixGetDepth(pixs) != 32)
534 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
535
536 if (L_ABS(angle) < MinAngleToRotate)
537 return pixClone(pixs);
538
539 pixGetDimensions(pixs, &w, &h, NULL);
540 datas = pixGetData(pixs);
541 wpls = pixGetWpl(pixs);
542 pixd = pixCreateTemplate(pixs);
543 datad = pixGetData(pixd);
544 wpld = pixGetWpl(pixd);
545
546 rotateAMColorCornerLow(datad, w, h, wpld, datas, wpls, angle, fillval);
547 if (pixGetSpp(pixs) == 4) {
548 pix1 = pixGetRGBComponent(pixs, L_ALPHA_CHANNEL);
549 pix2 = pixRotateAMGrayCorner(pix1, angle, 255); /* bring in opaque */
550 pixSetRGBComponent(pixd, pix2, L_ALPHA_CHANNEL);
551 pixDestroy(&pix1);
552 pixDestroy(&pix2);
553 }
554
555 return pixd;
556 }
557
558
559 /*!
560 * \brief pixRotateAMGrayCorner()
561 *
562 * \param[in] pixs
563 * \param[in] angle radians; clockwise is positive
564 * \param[in] grayval 0 to bring in BLACK, 255 for WHITE
565 * \return pixd, or NULL on error
566 *
567 * <pre>
568 * Notes:
569 * (1) Rotates the image about the UL corner.
570 * (2) A positive angle gives a clockwise rotation.
571 * (3) Specify the grayvalue to be brought in from outside the image.
572 * </pre>
573 */
574 PIX *
575 pixRotateAMGrayCorner(PIX *pixs,
576 l_float32 angle,
577 l_uint8 grayval)
578 {
579 l_int32 w, h, wpls, wpld;
580 l_uint32 *datas, *datad;
581 PIX *pixd;
582
583 if (!pixs)
584 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
585 if (pixGetDepth(pixs) != 8)
586 return (PIX *)ERROR_PTR("pixs must be 8 bpp", __func__, NULL);
587
588 if (L_ABS(angle) < MinAngleToRotate)
589 return pixClone(pixs);
590
591 pixGetDimensions(pixs, &w, &h, NULL);
592 datas = pixGetData(pixs);
593 wpls = pixGetWpl(pixs);
594 pixd = pixCreateTemplate(pixs);
595 datad = pixGetData(pixd);
596 wpld = pixGetWpl(pixd);
597
598 rotateAMGrayCornerLow(datad, w, h, wpld, datas, wpls, angle, grayval);
599
600 return pixd;
601 }
602
603
604 static void
605 rotateAMColorCornerLow(l_uint32 *datad,
606 l_int32 w,
607 l_int32 h,
608 l_int32 wpld,
609 l_uint32 *datas,
610 l_int32 wpls,
611 l_float32 angle,
612 l_uint32 colorval)
613 {
614 l_int32 i, j, wm2, hm2;
615 l_int32 xpm, ypm, xp, yp, xf, yf;
616 l_int32 rval, gval, bval;
617 l_uint32 word00, word01, word10, word11;
618 l_uint32 *lines, *lined;
619 l_float32 sina, cosa;
620
621 wm2 = w - 2;
622 hm2 = h - 2;
623 sina = 16.f * sin(angle);
624 cosa = 16.f * cos(angle);
625
626 for (i = 0; i < h; i++) {
627 lined = datad + i * wpld;
628 for (j = 0; j < w; j++) {
629 xpm = (l_int32)(j * cosa + i * sina);
630 ypm = (l_int32)(i * cosa - j * sina);
631 xp = xpm >> 4;
632 yp = ypm >> 4;
633 xf = xpm & 0x0f;
634 yf = ypm & 0x0f;
635
636 /* if off the edge, write input colorval */
637 if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
638 *(lined + j) = colorval;
639 continue;
640 }
641
642 lines = datas + yp * wpls;
643
644 /* do area weighting. Without this, we would
645 * simply do:
646 * *(lined + j) = *(lines + xp);
647 * which is faster but gives lousy results!
648 */
649 word00 = *(lines + xp);
650 word10 = *(lines + xp + 1);
651 word01 = *(lines + wpls + xp);
652 word11 = *(lines + wpls + xp + 1);
653 rval = ((16 - xf) * (16 - yf) * ((word00 >> L_RED_SHIFT) & 0xff) +
654 xf * (16 - yf) * ((word10 >> L_RED_SHIFT) & 0xff) +
655 (16 - xf) * yf * ((word01 >> L_RED_SHIFT) & 0xff) +
656 xf * yf * ((word11 >> L_RED_SHIFT) & 0xff) + 128) / 256;
657 gval = ((16 - xf) * (16 - yf) * ((word00 >> L_GREEN_SHIFT) & 0xff) +
658 xf * (16 - yf) * ((word10 >> L_GREEN_SHIFT) & 0xff) +
659 (16 - xf) * yf * ((word01 >> L_GREEN_SHIFT) & 0xff) +
660 xf * yf * ((word11 >> L_GREEN_SHIFT) & 0xff) + 128) / 256;
661 bval = ((16 - xf) * (16 - yf) * ((word00 >> L_BLUE_SHIFT) & 0xff) +
662 xf * (16 - yf) * ((word10 >> L_BLUE_SHIFT) & 0xff) +
663 (16 - xf) * yf * ((word01 >> L_BLUE_SHIFT) & 0xff) +
664 xf * yf * ((word11 >> L_BLUE_SHIFT) & 0xff) + 128) / 256;
665 composeRGBPixel(rval, gval, bval, lined + j);
666 }
667 }
668 }
669
670
671 static void
672 rotateAMGrayCornerLow(l_uint32 *datad,
673 l_int32 w,
674 l_int32 h,
675 l_int32 wpld,
676 l_uint32 *datas,
677 l_int32 wpls,
678 l_float32 angle,
679 l_uint8 grayval)
680 {
681 l_int32 i, j, wm2, hm2;
682 l_int32 xpm, ypm, xp, yp, xf, yf;
683 l_int32 v00, v01, v10, v11;
684 l_uint8 val;
685 l_uint32 *lines, *lined;
686 l_float32 sina, cosa;
687
688 wm2 = w - 2;
689 hm2 = h - 2;
690 sina = 16.f * sin(angle);
691 cosa = 16.f * cos(angle);
692
693 for (i = 0; i < h; i++) {
694 lined = datad + i * wpld;
695 for (j = 0; j < w; j++) {
696 xpm = (l_int32)(j * cosa + i * sina);
697 ypm = (l_int32)(i * cosa - j * sina);
698 xp = xpm >> 4;
699 yp = ypm >> 4;
700 xf = xpm & 0x0f;
701 yf = ypm & 0x0f;
702
703 /* if off the edge, write input grayval */
704 if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
705 SET_DATA_BYTE(lined, j, grayval);
706 continue;
707 }
708
709 lines = datas + yp * wpls;
710
711 /* do area weighting. Without this, we would
712 * simply do:
713 * SET_DATA_BYTE(lined, j, GET_DATA_BYTE(lines, xp));
714 * which is faster but gives lousy results!
715 */
716 v00 = (16 - xf) * (16 - yf) * GET_DATA_BYTE(lines, xp);
717 v10 = xf * (16 - yf) * GET_DATA_BYTE(lines, xp + 1);
718 v01 = (16 - xf) * yf * GET_DATA_BYTE(lines + wpls, xp);
719 v11 = xf * yf * GET_DATA_BYTE(lines + wpls, xp + 1);
720 val = (l_uint8)((v00 + v01 + v10 + v11 + 128) / 256);
721 SET_DATA_BYTE(lined, j, val);
722 }
723 }
724 }
725
726
727 /*------------------------------------------------------------------*
728 * Fast RGB color rotation about center *
729 *------------------------------------------------------------------*/
730 /*!
731 * \brief pixRotateAMColorFast()
732 *
733 * \param[in] pixs
734 * \param[in] angle radians; clockwise is positive
735 * \param[in] colorval e.g., 0 to bring in BLACK, 0xffffff00 for WHITE
736 * \return pixd, or NULL on error
737 *
738 * <pre>
739 * Notes:
740 * (1) This rotates a color image about the image center.
741 * (2) A positive angle gives a clockwise rotation.
742 * (3) It uses area mapping, dividing each pixel into
743 * 16 subpixels.
744 * (4) It is about 10% to 20% faster than the more accurate linear
745 * interpolation function pixRotateAMColor(),
746 * which uses 256 subpixels.
747 * (5) For some reason it shifts the image center.
748 * No attempt is made to rotate the alpha component.
749 * </pre>
750 */
751 PIX *
752 pixRotateAMColorFast(PIX *pixs,
753 l_float32 angle,
754 l_uint32 colorval)
755 {
756 l_int32 w, h, wpls, wpld;
757 l_uint32 *datas, *datad;
758 PIX *pixd;
759
760 if (!pixs)
761 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL);
762 if (pixGetDepth(pixs) != 32)
763 return (PIX *)ERROR_PTR("pixs must be 32 bpp", __func__, NULL);
764
765 if (L_ABS(angle) < MinAngleToRotate)
766 return pixClone(pixs);
767
768 pixGetDimensions(pixs, &w, &h, NULL);
769 datas = pixGetData(pixs);
770 wpls = pixGetWpl(pixs);
771 pixd = pixCreateTemplate(pixs);
772 datad = pixGetData(pixd);
773 wpld = pixGetWpl(pixd);
774
775 rotateAMColorFastLow(datad, w, h, wpld, datas, wpls, angle, colorval);
776 return pixd;
777 }
778
779
780 /*!
781 * \brief rotateAMColorFastLow()
782 *
783 * This is a special simplification of area mapping with division
784 * of each pixel into 16 sub-pixels. The exact coefficients that
785 * should be used are the same as for the 4x linear interpolation
786 * scaling case, and are given there. I tried to approximate these
787 * as weighted coefficients with a maximum sum of 4, which
788 * allows us to do the arithmetic in parallel for the R, G and B
789 * components in a 32 bit pixel. However, there are three reasons
790 * for not doing that:
791 * (1) the loss of accuracy in the parallel implementation
792 * is visually significant
793 * (2) the parallel implementation (described below) is slower
794 * (3) the parallel implementation requires allocation of
795 * a temporary color image
796 *
797 * There are 16 cases for the choice of the subpixel, and
798 * for each, the mapping to the relevant source
799 * pixels is as follows:
800 *
801 * subpixel src pixel weights
802 * -------- -----------------
803 * 0 sp1
804 * 1 (3 * sp1 + sp2) / 4
805 * 2 (sp1 + sp2) / 2
806 * 3 (sp1 + 3 * sp2) / 4
807 * 4 (3 * sp1 + sp3) / 4
808 * 5 (9 * sp1 + 3 * sp2 + 3 * sp3 + sp4) / 16
809 * 6 (3 * sp1 + 3 * sp2 + sp3 + sp4) / 8
810 * 7 (3 * sp1 + 9 * sp2 + sp3 + 3 * sp4) / 16
811 * 8 (sp1 + sp3) / 2
812 * 9 (3 * sp1 + sp2 + 3 * sp3 + sp4) / 8
813 * 10 (sp1 + sp2 + sp3 + sp4) / 4
814 * 11 (sp1 + 3 * sp2 + sp3 + 3 * sp4) / 8
815 * 12 (sp1 + 3 * sp3) / 4
816 * 13 (3 * sp1 + sp2 + 9 * sp3 + 3 * sp4) / 16
817 * 14 (sp1 + sp2 + 3 * sp3 + 3 * sp4) / 8
818 * 15 (sp1 + 3 * sp2 + 3 * sp3 + 9 * sp4) / 16
819 *
820 * Another way to visualize this is to consider the area mapping
821 * (or linear interpolation) coefficients for the pixel sp1.
822 * Expressed in fourths, they can be written as asymmetric matrix:
823 *
824 * 4 3 2 1
825 * 3 2.25 1.5 0.75
826 * 2 1.5 1 0.5
827 * 1 0.75 0.5 0.25
828 *
829 * The coefficients for the three neighboring pixels can be
830 * similarly written.
831 *
832 * This is implemented here, where, for each color component,
833 * we inline its extraction from each participating word,
834 * construct the linear combination, and combine the results
835 * into the destination 32 bit RGB pixel, using the appropriate shifts.
836 *
837 * It is interesting to note that an alternative method, where
838 * we do the arithmetic on the 32 bit pixels directly (after
839 * shifting the components so they won't overflow into each other)
840 * is significantly inferior. Because we have only 8 bits for
841 * internal overflows, which can be distributed as 2, 3, 3, it
842 * is impossible to add these with the correct linear
843 * interpolation coefficients, which require a sum of up to 16.
844 * Rounding off to a sum of 4 causes appreciable visual artifacts
845 * in the rotated image. The code for the inferior method
846 * can be found in prog/rotatefastalt.c, for reference.
847 */
848 static void
849 rotateAMColorFastLow(l_uint32 *datad,
850 l_int32 w,
851 l_int32 h,
852 l_int32 wpld,
853 l_uint32 *datas,
854 l_int32 wpls,
855 l_float32 angle,
856 l_uint32 colorval)
857 {
858 l_int32 i, j, xcen, ycen, wm2, hm2;
859 l_int32 xdif, ydif, xpm, ypm, xp, yp, xf, yf;
860 l_uint32 word1, word2, word3, word4, red, blue, green;
861 l_uint32 *pword, *lines, *lined;
862 l_float32 sina, cosa;
863
864 xcen = w / 2;
865 wm2 = w - 2;
866 ycen = h / 2;
867 hm2 = h - 2;
868 sina = 4.f * sin(angle);
869 cosa = 4.f * cos(angle);
870
871 for (i = 0; i < h; i++) {
872 ydif = ycen - i;
873 lined = datad + i * wpld;
874 for (j = 0; j < w; j++) {
875 xdif = xcen - j;
876 xpm = (l_int32)(-xdif * cosa - ydif * sina);
877 ypm = (l_int32)(-ydif * cosa + xdif * sina);
878 xp = xcen + (xpm >> 2);
879 yp = ycen + (ypm >> 2);
880 xf = xpm & 0x03;
881 yf = ypm & 0x03;
882
883 /* if off the edge, write input grayval */
884 if (xp < 0 || yp < 0 || xp > wm2 || yp > hm2) {
885 *(lined + j) = colorval;
886 continue;
887 }
888
889 lines = datas + yp * wpls;
890 pword = lines + xp;
891
892 switch (xf + 4 * yf)
893 {
894 case 0:
895 *(lined + j) = *pword;
896 break;
897 case 1:
898 word1 = *pword;
899 word2 = *(pword + 1);
900 red = 3 * (word1 >> 24) + (word2 >> 24);
901 green = 3 * ((word1 >> 16) & 0xff) +
902 ((word2 >> 16) & 0xff);
903 blue = 3 * ((word1 >> 8) & 0xff) +
904 ((word2 >> 8) & 0xff);
905 *(lined + j) = ((red << 22) & 0xff000000) |
906 ((green << 14) & 0x00ff0000) |
907 ((blue << 6) & 0x0000ff00);
908 break;
909 case 2:
910 word1 = *pword;
911 word2 = *(pword + 1);
912 red = (word1 >> 24) + (word2 >> 24);
913 green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff);
914 blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff);
915 *(lined + j) = ((red << 23) & 0xff000000) |
916 ((green << 15) & 0x00ff0000) |
917 ((blue << 7) & 0x0000ff00);
918 break;
919 case 3:
920 word1 = *pword;
921 word2 = *(pword + 1);
922 red = (word1 >> 24) + 3 * (word2 >> 24);
923 green = ((word1 >> 16) & 0xff) +
924 3 * ((word2 >> 16) & 0xff);
925 blue = ((word1 >> 8) & 0xff) +
926 3 * ((word2 >> 8) & 0xff);
927 *(lined + j) = ((red << 22) & 0xff000000) |
928 ((green << 14) & 0x00ff0000) |
929 ((blue << 6) & 0x0000ff00);
930 break;
931 case 4:
932 word1 = *pword;
933 word3 = *(pword + wpls);
934 red = 3 * (word1 >> 24) + (word3 >> 24);
935 green = 3 * ((word1 >> 16) & 0xff) +
936 ((word3 >> 16) & 0xff);
937 blue = 3 * ((word1 >> 8) & 0xff) +
938 ((word3 >> 8) & 0xff);
939 *(lined + j) = ((red << 22) & 0xff000000) |
940 ((green << 14) & 0x00ff0000) |
941 ((blue << 6) & 0x0000ff00);
942 break;
943 case 5:
944 word1 = *pword;
945 word2 = *(pword + 1);
946 word3 = *(pword + wpls);
947 word4 = *(pword + wpls + 1);
948 red = 9 * (word1 >> 24) + 3 * (word2 >> 24) +
949 3 * (word3 >> 24) + (word4 >> 24);
950 green = 9 * ((word1 >> 16) & 0xff) +
951 3 * ((word2 >> 16) & 0xff) +
952 3 * ((word3 >> 16) & 0xff) +
953 ((word4 >> 16) & 0xff);
954 blue = 9 * ((word1 >> 8) & 0xff) +
955 3 * ((word2 >> 8) & 0xff) +
956 3 * ((word3 >> 8) & 0xff) +
957 ((word4 >> 8) & 0xff);
958 *(lined + j) = ((red << 20) & 0xff000000) |
959 ((green << 12) & 0x00ff0000) |
960 ((blue << 4) & 0x0000ff00);
961 break;
962 case 6:
963 word1 = *pword;
964 word2 = *(pword + 1);
965 word3 = *(pword + wpls);
966 word4 = *(pword + wpls + 1);
967 red = 3 * (word1 >> 24) + 3 * (word2 >> 24) +
968 (word3 >> 24) + (word4 >> 24);
969 green = 3 * ((word1 >> 16) & 0xff) +
970 3 * ((word2 >> 16) & 0xff) +
971 ((word3 >> 16) & 0xff) +
972 ((word4 >> 16) & 0xff);
973 blue = 3 * ((word1 >> 8) & 0xff) +
974 3 * ((word2 >> 8) & 0xff) +
975 ((word3 >> 8) & 0xff) +
976 ((word4 >> 8) & 0xff);
977 *(lined + j) = ((red << 21) & 0xff000000) |
978 ((green << 13) & 0x00ff0000) |
979 ((blue << 5) & 0x0000ff00);
980 break;
981 case 7:
982 word1 = *pword;
983 word2 = *(pword + 1);
984 word3 = *(pword + wpls);
985 word4 = *(pword + wpls + 1);
986 red = 3 * (word1 >> 24) + 9 * (word2 >> 24) +
987 (word3 >> 24) + 3 * (word4 >> 24);
988 green = 3 * ((word1 >> 16) & 0xff) +
989 9 * ((word2 >> 16) & 0xff) +
990 ((word3 >> 16) & 0xff) +
991 3 * ((word4 >> 16) & 0xff);
992 blue = 3 * ((word1 >> 8) & 0xff) +
993 9 * ((word2 >> 8) & 0xff) +
994 ((word3 >> 8) & 0xff) +
995 3 * ((word4 >> 8) & 0xff);
996 *(lined + j) = ((red << 20) & 0xff000000) |
997 ((green << 12) & 0x00ff0000) |
998 ((blue << 4) & 0x0000ff00);
999 break;
1000 case 8:
1001 word1 = *pword;
1002 word3 = *(pword + wpls);
1003 red = (word1 >> 24) + (word3 >> 24);
1004 green = ((word1 >> 16) & 0xff) + ((word3 >> 16) & 0xff);
1005 blue = ((word1 >> 8) & 0xff) + ((word3 >> 8) & 0xff);
1006 *(lined + j) = ((red << 23) & 0xff000000) |
1007 ((green << 15) & 0x00ff0000) |
1008 ((blue << 7) & 0x0000ff00);
1009 break;
1010 case 9:
1011 word1 = *pword;
1012 word2 = *(pword + 1);
1013 word3 = *(pword + wpls);
1014 word4 = *(pword + wpls + 1);
1015 red = 3 * (word1 >> 24) + (word2 >> 24) +
1016 3 * (word3 >> 24) + (word4 >> 24);
1017 green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) +
1018 3 * ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff);
1019 blue = 3 * ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1020 3 * ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff);
1021 *(lined + j) = ((red << 21) & 0xff000000) |
1022 ((green << 13) & 0x00ff0000) |
1023 ((blue << 5) & 0x0000ff00);
1024 break;
1025 case 10:
1026 word1 = *pword;
1027 word2 = *(pword + 1);
1028 word3 = *(pword + wpls);
1029 word4 = *(pword + wpls + 1);
1030 red = (word1 >> 24) + (word2 >> 24) +
1031 (word3 >> 24) + (word4 >> 24);
1032 green = ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) +
1033 ((word3 >> 16) & 0xff) + ((word4 >> 16) & 0xff);
1034 blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1035 ((word3 >> 8) & 0xff) + ((word4 >> 8) & 0xff);
1036 *(lined + j) = ((red << 22) & 0xff000000) |
1037 ((green << 14) & 0x00ff0000) |
1038 ((blue << 6) & 0x0000ff00);
1039 break;
1040 case 11:
1041 word1 = *pword;
1042 word2 = *(pword + 1);
1043 word3 = *(pword + wpls);
1044 word4 = *(pword + wpls + 1);
1045 red = (word1 >> 24) + 3 * (word2 >> 24) +
1046 (word3 >> 24) + 3 * (word4 >> 24);
1047 green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) +
1048 ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff);
1049 blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) +
1050 ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff);
1051 *(lined + j) = ((red << 21) & 0xff000000) |
1052 ((green << 13) & 0x00ff0000) |
1053 ((blue << 5) & 0x0000ff00);
1054 break;
1055 case 12:
1056 word1 = *pword;
1057 word3 = *(pword + wpls);
1058 red = (word1 >> 24) + 3 * (word3 >> 24);
1059 green = ((word1 >> 16) & 0xff) +
1060 3 * ((word3 >> 16) & 0xff);
1061 blue = ((word1 >> 8) & 0xff) +
1062 3 * ((word3 >> 8) & 0xff);
1063 *(lined + j) = ((red << 22) & 0xff000000) |
1064 ((green << 14) & 0x00ff0000) |
1065 ((blue << 6) & 0x0000ff00);
1066 break;
1067 case 13:
1068 word1 = *pword;
1069 word2 = *(pword + 1);
1070 word3 = *(pword + wpls);
1071 word4 = *(pword + wpls + 1);
1072 red = 3 * (word1 >> 24) + (word2 >> 24) +
1073 9 * (word3 >> 24) + 3 * (word4 >> 24);
1074 green = 3 * ((word1 >> 16) & 0xff) + ((word2 >> 16) & 0xff) +
1075 9 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff);
1076 blue = 3 *((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1077 9 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff);
1078 *(lined + j) = ((red << 20) & 0xff000000) |
1079 ((green << 12) & 0x00ff0000) |
1080 ((blue << 4) & 0x0000ff00);
1081 break;
1082 case 14:
1083 word1 = *pword;
1084 word2 = *(pword + 1);
1085 word3 = *(pword + wpls);
1086 word4 = *(pword + wpls + 1);
1087 red = (word1 >> 24) + (word2 >> 24) +
1088 3 * (word3 >> 24) + 3 * (word4 >> 24);
1089 green = ((word1 >> 16) & 0xff) +((word2 >> 16) & 0xff) +
1090 3 * ((word3 >> 16) & 0xff) + 3 * ((word4 >> 16) & 0xff);
1091 blue = ((word1 >> 8) & 0xff) + ((word2 >> 8) & 0xff) +
1092 3 * ((word3 >> 8) & 0xff) + 3 * ((word4 >> 8) & 0xff);
1093 *(lined + j) = ((red << 21) & 0xff000000) |
1094 ((green << 13) & 0x00ff0000) |
1095 ((blue << 5) & 0x0000ff00);
1096 break;
1097 case 15:
1098 word1 = *pword;
1099 word2 = *(pword + 1);
1100 word3 = *(pword + wpls);
1101 word4 = *(pword + wpls + 1);
1102 red = (word1 >> 24) + 3 * (word2 >> 24) +
1103 3 * (word3 >> 24) + 9 * (word4 >> 24);
1104 green = ((word1 >> 16) & 0xff) + 3 * ((word2 >> 16) & 0xff) +
1105 3 * ((word3 >> 16) & 0xff) + 9 * ((word4 >> 16) & 0xff);
1106 blue = ((word1 >> 8) & 0xff) + 3 * ((word2 >> 8) & 0xff) +
1107 3 * ((word3 >> 8) & 0xff) + 9 * ((word4 >> 8) & 0xff);
1108 *(lined + j) = ((red << 20) & 0xff000000) |
1109 ((green << 12) & 0x00ff0000) |
1110 ((blue << 4) & 0x0000ff00);
1111 break;
1112 default:
1113 lept_stderr("shouldn't get here\n");
1114 break;
1115 }
1116 }
1117 }
1118 }