Mercurial > hgrepos > Python2 > PyMuPDF
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 } |
