Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/leptonica/src/warper.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 * \file warper.c | |
| 29 * <pre> | |
| 30 * | |
| 31 * High-level captcha interface | |
| 32 * PIX *pixSimpleCaptcha() | |
| 33 * | |
| 34 * Random sinusoidal warping | |
| 35 * PIX *pixRandomHarmonicWarp() | |
| 36 * | |
| 37 * Helper functions | |
| 38 * static l_float64 *generateRandomNumberArray() | |
| 39 * static l_int32 applyWarpTransform() | |
| 40 * | |
| 41 * Version using a LUT for sin | |
| 42 * PIX *pixRandomHarmonicWarpLUT() | |
| 43 * static l_int32 applyWarpTransformLUT() | |
| 44 * static l_int32 makeSinLUT() | |
| 45 * static l_float32 getSinFromLUT() | |
| 46 * | |
| 47 * Stereoscopic warping | |
| 48 * PIX *pixWarpStereoscopic() | |
| 49 * | |
| 50 * Linear and quadratic horizontal stretching | |
| 51 * PIX *pixStretchHorizontal() | |
| 52 * PIX *pixStretchHorizontalSampled() | |
| 53 * PIX *pixStretchHorizontalLI() | |
| 54 * | |
| 55 * Quadratic vertical shear | |
| 56 * PIX *pixQuadraticVShear() | |
| 57 * PIX *pixQuadraticVShearSampled() | |
| 58 * PIX *pixQuadraticVShearLI() | |
| 59 * | |
| 60 * Stereo from a pair of images | |
| 61 * PIX *pixStereoFromPair() | |
| 62 * </pre> | |
| 63 */ | |
| 64 | |
| 65 #ifdef HAVE_CONFIG_H | |
| 66 #include <config_auto.h> | |
| 67 #endif /* HAVE_CONFIG_H */ | |
| 68 | |
| 69 #include <math.h> | |
| 70 #include "allheaders.h" | |
| 71 | |
| 72 static l_float64 *generateRandomNumberArray(l_int32 size); | |
| 73 static l_int32 applyWarpTransform(l_float32 xmag, l_float32 ymag, | |
| 74 l_float32 xfreq, l_float32 yfreq, | |
| 75 l_float64 *randa, l_int32 nx, l_int32 ny, | |
| 76 l_int32 xp, l_int32 yp, | |
| 77 l_float32 *px, l_float32 *py); | |
| 78 | |
| 79 #define USE_SIN_TABLE 0 | |
| 80 | |
| 81 /* Suggested input to pixStereoFromPair(). These are weighting | |
| 82 * factors for input to the red channel from the left image. */ | |
| 83 static const l_float32 DefaultRedWeight = 0.0f; | |
| 84 static const l_float32 DefaultGreenWeight = 0.7f; | |
| 85 static const l_float32 DefaultBlueWeight = 0.3f; | |
| 86 | |
| 87 | |
| 88 /*----------------------------------------------------------------------* | |
| 89 * High-level example captcha interface * | |
| 90 *----------------------------------------------------------------------*/ | |
| 91 /*! | |
| 92 * \brief pixSimpleCaptcha() | |
| 93 * | |
| 94 * \param[in] pixs 8 bpp; no colormap | |
| 95 * \param[in] border added white pixels on each side | |
| 96 * \param[in] nterms number of x and y harmonic terms | |
| 97 * \param[in] seed of random number generator | |
| 98 * \param[in] color for colorizing; in 0xrrggbb00 format; use 0 for black | |
| 99 * \param[in] cmapflag 1 for colormap output; 0 for rgb | |
| 100 * \return pixd 8 bpp cmap or 32 bpp rgb, or NULL on error | |
| 101 * | |
| 102 * <pre> | |
| 103 * Notes: | |
| 104 * (1) This uses typical default values for generating captchas. | |
| 105 * The magnitudes of the harmonic warp are typically to be | |
| 106 * smaller when more terms are used, even though the phases | |
| 107 * are random. See, for example, prog/warptest.c. | |
| 108 * </pre> | |
| 109 */ | |
| 110 PIX * | |
| 111 pixSimpleCaptcha(PIX *pixs, | |
| 112 l_int32 border, | |
| 113 l_int32 nterms, | |
| 114 l_uint32 seed, | |
| 115 l_uint32 color, | |
| 116 l_int32 cmapflag) | |
| 117 { | |
| 118 l_int32 k; | |
| 119 l_float32 xmag[] = {7.0f, 5.0f, 4.0f, 3.0f}; | |
| 120 l_float32 ymag[] = {10.0f, 8.0f, 6.0f, 5.0f}; | |
| 121 l_float32 xfreq[] = {0.12f, 0.10f, 0.10f, 0.11f}; | |
| 122 l_float32 yfreq[] = {0.15f, 0.13f, 0.13f, 0.11f}; | |
| 123 PIX *pixg, *pixgb, *pixw, *pixd; | |
| 124 | |
| 125 if (!pixs) | |
| 126 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 127 if (nterms < 1 || nterms > 4) | |
| 128 return (PIX *)ERROR_PTR("nterms must be in {1,2,3,4}", __func__, NULL); | |
| 129 | |
| 130 k = nterms - 1; | |
| 131 pixg = pixConvertTo8(pixs, 0); | |
| 132 pixgb = pixAddBorder(pixg, border, 255); | |
| 133 pixw = pixRandomHarmonicWarp(pixgb, xmag[k], ymag[k], xfreq[k], yfreq[k], | |
| 134 nterms, nterms, seed, 255); | |
| 135 pixd = pixColorizeGray(pixw, color, cmapflag); | |
| 136 | |
| 137 pixDestroy(&pixg); | |
| 138 pixDestroy(&pixgb); | |
| 139 pixDestroy(&pixw); | |
| 140 return pixd; | |
| 141 } | |
| 142 | |
| 143 | |
| 144 /*----------------------------------------------------------------------* | |
| 145 * Random sinusoidal warping * | |
| 146 *----------------------------------------------------------------------*/ | |
| 147 /*! | |
| 148 * \brief pixRandomHarmonicWarp() | |
| 149 * | |
| 150 * \param[in] pixs 8 bpp; no colormap | |
| 151 * \param[in] xmag, ymag maximum magnitude of x and y distortion | |
| 152 * \param[in] xfreq, yfreq maximum magnitude of x and y frequency | |
| 153 * \param[in] nx, ny number of x and y harmonic terms | |
| 154 * \param[in] seed of random number generator | |
| 155 * \param[in] grayval color brought in from the outside; | |
| 156 * 0 for black, 255 for white | |
| 157 * \return pixd 8 bpp; no colormap, or NULL on error | |
| 158 * | |
| 159 * <pre> | |
| 160 * Notes: | |
| 161 * (1) To generate the warped image p(x',y'), set up the transforms | |
| 162 * that are in getWarpTransform(). For each (x',y') in the | |
| 163 * dest, the warp function computes the originating location | |
| 164 * (x, y) in the src. The differences (x - x') and (y - y') | |
| 165 * are given as a sum of products of sinusoidal terms. Each | |
| 166 * term is multiplied by a maximum amplitude (in pixels), and the | |
| 167 * angle is determined by a frequency and phase, and depends | |
| 168 * on the (x', y') value of the dest. Random numbers with | |
| 169 * a variable input seed are used to allow the warping to be | |
| 170 * unpredictable. A linear interpolation is used to find | |
| 171 * the value for the source at (x, y); this value is written | |
| 172 * into the dest. | |
| 173 * (2) This can be used to generate 'captcha's, which are somewhat | |
| 174 * randomly distorted images of text. A typical set of parameters | |
| 175 * for a captcha are: | |
| 176 * xmag = 4.0 ymag = 6.0 | |
| 177 * xfreq = 0.10 yfreq = 0.13 | |
| 178 * nx = 3 ny = 3 | |
| 179 * Other examples can be found in prog/warptest.c. | |
| 180 * </pre> | |
| 181 */ | |
| 182 PIX * | |
| 183 pixRandomHarmonicWarp(PIX *pixs, | |
| 184 l_float32 xmag, | |
| 185 l_float32 ymag, | |
| 186 l_float32 xfreq, | |
| 187 l_float32 yfreq, | |
| 188 l_int32 nx, | |
| 189 l_int32 ny, | |
| 190 l_uint32 seed, | |
| 191 l_int32 grayval) | |
| 192 { | |
| 193 l_int32 w, h, d, i, j, wpls, wpld, val; | |
| 194 l_uint32 *datas, *datad, *lined; | |
| 195 l_float32 x, y; | |
| 196 l_float64 *randa; | |
| 197 PIX *pixd; | |
| 198 | |
| 199 if (!pixs) | |
| 200 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 201 pixGetDimensions(pixs, &w, &h, &d); | |
| 202 if (d != 8) | |
| 203 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 204 | |
| 205 /* Compute filter output at each location. We iterate over | |
| 206 * the destination pixels. For each dest pixel, use the | |
| 207 * warp function to compute the four source pixels that | |
| 208 * contribute, at the location (x, y). Each source pixel | |
| 209 * is divided into 16 x 16 subpixels to get an approximate value. */ | |
| 210 srand(seed); | |
| 211 randa = generateRandomNumberArray(5 * (nx + ny)); | |
| 212 pixd = pixCreateTemplate(pixs); | |
| 213 datas = pixGetData(pixs); | |
| 214 wpls = pixGetWpl(pixs); | |
| 215 datad = pixGetData(pixd); | |
| 216 wpld = pixGetWpl(pixd); | |
| 217 | |
| 218 for (i = 0; i < h; i++) { | |
| 219 lined = datad + i * wpld; | |
| 220 for (j = 0; j < w; j++) { | |
| 221 applyWarpTransform(xmag, ymag, xfreq, yfreq, randa, nx, ny, | |
| 222 j, i, &x, &y); | |
| 223 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); | |
| 224 SET_DATA_BYTE(lined, j, val); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 LEPT_FREE(randa); | |
| 229 return pixd; | |
| 230 } | |
| 231 | |
| 232 | |
| 233 /*----------------------------------------------------------------------* | |
| 234 * Static helper functions * | |
| 235 *----------------------------------------------------------------------*/ | |
| 236 static l_float64 * | |
| 237 generateRandomNumberArray(l_int32 size) | |
| 238 { | |
| 239 l_int32 i; | |
| 240 l_float64 *randa; | |
| 241 | |
| 242 if ((randa = (l_float64 *)LEPT_CALLOC(size, sizeof(l_float64))) == NULL) | |
| 243 return (l_float64 *)ERROR_PTR("calloc fail for randa", __func__, NULL); | |
| 244 | |
| 245 /* Return random values between 0.5 and 1.0 */ | |
| 246 for (i = 0; i < size; i++) | |
| 247 randa[i] = 0.5 * (1.0 + (l_float64)rand() / (l_float64)RAND_MAX); | |
| 248 return randa; | |
| 249 } | |
| 250 | |
| 251 | |
| 252 /*! | |
| 253 * \brief applyWarpTransform() | |
| 254 * | |
| 255 * Notes: | |
| 256 * (1) Uses the internal sin function. | |
| 257 */ | |
| 258 static l_int32 | |
| 259 applyWarpTransform(l_float32 xmag, | |
| 260 l_float32 ymag, | |
| 261 l_float32 xfreq, | |
| 262 l_float32 yfreq, | |
| 263 l_float64 *randa, | |
| 264 l_int32 nx, | |
| 265 l_int32 ny, | |
| 266 l_int32 xp, | |
| 267 l_int32 yp, | |
| 268 l_float32 *px, | |
| 269 l_float32 *py) | |
| 270 { | |
| 271 l_int32 i; | |
| 272 l_float64 twopi, x, y, anglex, angley; | |
| 273 | |
| 274 twopi = 6.283185; | |
| 275 for (i = 0, x = xp; i < nx; i++) { | |
| 276 anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2]; | |
| 277 angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4]; | |
| 278 x += xmag * randa[3 * i] * sin(anglex) * sin(angley); | |
| 279 } | |
| 280 for (i = nx, y = yp; i < nx + ny; i++) { | |
| 281 angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2]; | |
| 282 anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4]; | |
| 283 y += ymag * randa[3 * i] * sin(angley) * sin(anglex); | |
| 284 } | |
| 285 | |
| 286 *px = (l_float32)x; | |
| 287 *py = (l_float32)y; | |
| 288 return 0; | |
| 289 } | |
| 290 | |
| 291 | |
| 292 #if USE_SIN_TABLE | |
| 293 /*----------------------------------------------------------------------* | |
| 294 * Version using a LUT for sin * | |
| 295 *----------------------------------------------------------------------*/ | |
| 296 static l_int32 applyWarpTransformLUT(l_float32 xmag, l_float32 ymag, | |
| 297 l_float32 xfreq, l_float32 yfreq, | |
| 298 l_float64 *randa, l_int32 nx, l_int32 ny, | |
| 299 l_int32 xp, l_int32 yp, l_float32 *lut, | |
| 300 l_int32 npts, l_float32 *px, l_float32 *py); | |
| 301 static l_int32 makeSinLUT(l_int32 npts, NUMA **pna); | |
| 302 static l_float32 getSinFromLUT(l_float32 *tab, l_int32 npts, | |
| 303 l_float32 radang); | |
| 304 | |
| 305 /*! | |
| 306 * \brief pixRandomHarmonicWarpLUT() | |
| 307 * | |
| 308 * \param[in] pixs 8 bpp; no colormap | |
| 309 * \param[in] xmag, ymag maximum magnitude of x and y distortion | |
| 310 * \param[in] xfreq, yfreq maximum magnitude of x and y frequency | |
| 311 * \param[in] nx, ny number of x and y harmonic terms | |
| 312 * \param[in] seed of random number generator | |
| 313 * \param[in] grayval color brought in from the outside; | |
| 314 * 0 for black, 255 for white | |
| 315 * \return pixd 8 bpp; no colormap, or NULL on error | |
| 316 * | |
| 317 * <pre> | |
| 318 * Notes: | |
| 319 * (1) See notes and inline comments in pixRandomHarmonicWarp(). | |
| 320 * This version uses a LUT for the sin function. It is not | |
| 321 * appreciably faster than using the built-in sin function, | |
| 322 * and is here for comparison only. | |
| 323 * </pre> | |
| 324 */ | |
| 325 PIX * | |
| 326 pixRandomHarmonicWarpLUT(PIX *pixs, | |
| 327 l_float32 xmag, | |
| 328 l_float32 ymag, | |
| 329 l_float32 xfreq, | |
| 330 l_float32 yfreq, | |
| 331 l_int32 nx, | |
| 332 l_int32 ny, | |
| 333 l_uint32 seed, | |
| 334 l_int32 grayval) | |
| 335 { | |
| 336 l_int32 w, h, d, i, j, wpls, wpld, val, npts; | |
| 337 l_uint32 *datas, *datad, *lined; | |
| 338 l_float32 x, y; | |
| 339 l_float32 *lut; | |
| 340 l_float64 *randa; | |
| 341 NUMA *na; | |
| 342 PIX *pixd; | |
| 343 | |
| 344 if (!pixs) | |
| 345 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 346 pixGetDimensions(pixs, &w, &h, &d); | |
| 347 if (d != 8) | |
| 348 return (PIX *)ERROR_PTR("pixs not 8 bpp", __func__, NULL); | |
| 349 | |
| 350 /* Compute filter output at each location. We iterate over | |
| 351 * the destination pixels. For each dest pixel, use the | |
| 352 * warp function to compute the four source pixels that | |
| 353 * contribute, at the location (x, y). Each source pixel | |
| 354 * is divided into 16 x 16 subpixels to get an approximate value. */ | |
| 355 srand(seed); | |
| 356 randa = generateRandomNumberArray(5 * (nx + ny)); | |
| 357 pixd = pixCreateTemplate(pixs); | |
| 358 datas = pixGetData(pixs); | |
| 359 wpls = pixGetWpl(pixs); | |
| 360 datad = pixGetData(pixd); | |
| 361 wpld = pixGetWpl(pixd); | |
| 362 | |
| 363 npts = 100; | |
| 364 makeSinLUT(npts, &na); | |
| 365 lut = numaGetFArray(na, L_NOCOPY); | |
| 366 for (i = 0; i < h; i++) { | |
| 367 lined = datad + i * wpld; | |
| 368 for (j = 0; j < w; j++) { | |
| 369 applyWarpTransformLUT(xmag, ymag, xfreq, yfreq, randa, nx, ny, | |
| 370 j, i, lut, npts, &x, &y); | |
| 371 linearInterpolatePixelGray(datas, wpls, w, h, x, y, grayval, &val); | |
| 372 SET_DATA_BYTE(lined, j, val); | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 numaDestroy(&na); | |
| 377 LEPT_FREE(randa); | |
| 378 return pixd; | |
| 379 } | |
| 380 | |
| 381 | |
| 382 /*! | |
| 383 * \brief applyWarpTransformLUT() | |
| 384 * | |
| 385 * Notes: | |
| 386 * (1) Uses an LUT for computing sin(theta). There is little speed | |
| 387 * advantage to using the LUT. | |
| 388 */ | |
| 389 static l_int32 | |
| 390 applyWarpTransformLUT(l_float32 xmag, | |
| 391 l_float32 ymag, | |
| 392 l_float32 xfreq, | |
| 393 l_float32 yfreq, | |
| 394 l_float64 *randa, | |
| 395 l_int32 nx, | |
| 396 l_int32 ny, | |
| 397 l_int32 xp, | |
| 398 l_int32 yp, | |
| 399 l_float32 *lut, | |
| 400 l_int32 npts, | |
| 401 l_float32 *px, | |
| 402 l_float32 *py) | |
| 403 { | |
| 404 l_int32 i; | |
| 405 l_float64 twopi, x, y, anglex, angley, sanglex, sangley; | |
| 406 | |
| 407 twopi = 6.283185; | |
| 408 for (i = 0, x = xp; i < nx; i++) { | |
| 409 anglex = xfreq * randa[3 * i + 1] * xp + twopi * randa[3 * i + 2]; | |
| 410 angley = yfreq * randa[3 * i + 3] * yp + twopi * randa[3 * i + 4]; | |
| 411 sanglex = getSinFromLUT(lut, npts, anglex); | |
| 412 sangley = getSinFromLUT(lut, npts, angley); | |
| 413 x += xmag * randa[3 * i] * sanglex * sangley; | |
| 414 } | |
| 415 for (i = nx, y = yp; i < nx + ny; i++) { | |
| 416 angley = yfreq * randa[3 * i + 1] * yp + twopi * randa[3 * i + 2]; | |
| 417 anglex = xfreq * randa[3 * i + 3] * xp + twopi * randa[3 * i + 4]; | |
| 418 sanglex = getSinFromLUT(lut, npts, anglex); | |
| 419 sangley = getSinFromLUT(lut, npts, angley); | |
| 420 y += ymag * randa[3 * i] * sangley * sanglex; | |
| 421 } | |
| 422 | |
| 423 *px = (l_float32)x; | |
| 424 *py = (l_float32)y; | |
| 425 return 0; | |
| 426 } | |
| 427 | |
| 428 | |
| 429 static l_int32 | |
| 430 makeSinLUT(l_int32 npts, | |
| 431 NUMA **pna) | |
| 432 { | |
| 433 l_int32 i, n; | |
| 434 l_float32 delx, fval; | |
| 435 NUMA *na; | |
| 436 | |
| 437 if (!pna) | |
| 438 return ERROR_INT("&na not defined", __func__, 1); | |
| 439 *pna = NULL; | |
| 440 if (npts < 2) | |
| 441 return ERROR_INT("npts < 2", __func__, 1); | |
| 442 n = 2 * npts + 1; | |
| 443 na = numaCreate(n); | |
| 444 *pna = na; | |
| 445 delx = 3.14159265 / (l_float32)npts; | |
| 446 numaSetParameters(na, 0.0, delx); | |
| 447 for (i = 0; i < n / 2; i++) | |
| 448 numaAddNumber(na, (l_float32)sin((l_float64)i * delx)); | |
| 449 for (i = 0; i < n / 2; i++) { | |
| 450 numaGetFValue(na, i, &fval); | |
| 451 numaAddNumber(na, -fval); | |
| 452 } | |
| 453 numaAddNumber(na, 0); | |
| 454 | |
| 455 return 0; | |
| 456 } | |
| 457 | |
| 458 | |
| 459 static l_float32 | |
| 460 getSinFromLUT(l_float32 *tab, | |
| 461 l_int32 npts, | |
| 462 l_float32 radang) | |
| 463 { | |
| 464 l_int32 index; | |
| 465 l_float32 twopi, invtwopi, findex, diff; | |
| 466 | |
| 467 /* Restrict radang to [0, 2pi] */ | |
| 468 twopi = 6.283185; | |
| 469 invtwopi = 0.1591549; | |
| 470 if (radang < 0.0) | |
| 471 radang += twopi * (1.0 - (l_int32)(-radang * invtwopi)); | |
| 472 else if (radang > 0.0) | |
| 473 radang -= twopi * (l_int32)(radang * invtwopi); | |
| 474 | |
| 475 /* Interpolate */ | |
| 476 findex = (2.0 * (l_float32)npts) * (radang * invtwopi); | |
| 477 index = (l_int32)findex; | |
| 478 if (index == 2 * npts) | |
| 479 return tab[index]; | |
| 480 diff = findex - index; | |
| 481 return (1.0 - diff) * tab[index] + diff * tab[index + 1]; | |
| 482 } | |
| 483 #endif /* USE_SIN_TABLE */ | |
| 484 | |
| 485 | |
| 486 | |
| 487 /*---------------------------------------------------------------------------* | |
| 488 * Stereoscopic warping * | |
| 489 *---------------------------------------------------------------------------*/ | |
| 490 /*! | |
| 491 * \brief pixWarpStereoscopic() | |
| 492 * | |
| 493 * \param[in] pixs any depth, colormap ok | |
| 494 * \param[in] zbend horizontal separation in pixels of red and cyan | |
| 495 * at the left and right sides, that gives rise to | |
| 496 * quadratic curvature out of the image plane | |
| 497 * \param[in] zshiftt uniform pixel translation difference between | |
| 498 * red and cyan, that pushes the top of the image | |
| 499 * plane away from the viewer (zshiftt > 0) or | |
| 500 * towards the viewer (zshiftt < 0) | |
| 501 * \param[in] zshiftb uniform pixel translation difference between | |
| 502 * red and cyan, that pushes the bottom of the image | |
| 503 * plane away from the viewer (zshiftb > 0) or | |
| 504 * towards the viewer (zshiftb < 0) | |
| 505 * \param[in] ybendt multiplicative parameter for in-plane vertical | |
| 506 * displacement at the left or right edge at the top: | |
| 507 * y = ybendt * (2x/w - 1)^2 | |
| 508 * \param[in] ybendb same as ybendt, except at the left or right edge | |
| 509 * at the bottom | |
| 510 * \param[in] redleft 1 if the red filter is on the left; 0 otherwise | |
| 511 * \return pixd 32 bpp, or NULL on error | |
| 512 * | |
| 513 * <pre> | |
| 514 * Notes: | |
| 515 * (1) This function splits out the red channel, mucks around with | |
| 516 * it, then recombines with the unmolested cyan channel. | |
| 517 * (2) By using a quadratically increasing shift of the red | |
| 518 * pixels horizontally and away from the vertical centerline, | |
| 519 * the image appears to bend quadratically out of the image | |
| 520 * plane, symmetrically with respect to the vertical center | |
| 521 * line. A positive value of %zbend causes the plane to be | |
| 522 * curved away from the viewer. We use linearly interpolated | |
| 523 * stretching to avoid the appearance of kinks in the curve. | |
| 524 * (3) The parameters %zshiftt and %zshiftb tilt the image plane | |
| 525 * about a horizontal line through the center, and at the | |
| 526 * same time move that line either in toward the viewer or away. | |
| 527 * This is implemented by a combination of horizontal shear | |
| 528 * about the center line (for the tilt) and horizontal | |
| 529 * translation (to move the entire plane in or out). | |
| 530 * A positive value of %zshiftt moves the top of the plane | |
| 531 * away from the viewer, and a positive value of %zshiftb | |
| 532 * moves the bottom of the plane away. We use linear interpolated | |
| 533 * shear to avoid visible vertical steps in the tilted image. | |
| 534 * (4) The image can be bent in the plane and about the vertical | |
| 535 * centerline. The centerline does not shift, and the | |
| 536 * parameter %ybend gives the relative shift at left and right | |
| 537 * edges, with a downward shift for positive values of %ybend. | |
| 538 * (6) When writing out a steroscopic (red/cyan) image in jpeg, | |
| 539 * first call pixSetChromaSampling(pix, 0) to get sufficient | |
| 540 * resolution in the red channel. | |
| 541 * (7) Typical values are: | |
| 542 * zbend = 20 | |
| 543 * zshiftt = 15 | |
| 544 * zshiftb = -15 | |
| 545 * ybendt = 30 | |
| 546 * ybendb = 0 | |
| 547 * If the disparity z-values are too large, it is difficult for | |
| 548 * the brain to register the two images. | |
| 549 * (8) This function has been cleverly reimplemented by Jeff Breidenbach. | |
| 550 * The original implementation used two 32 bpp rgb images, | |
| 551 * and merged them at the end. The result is somewhat faded, | |
| 552 * and has a parameter "thresh" that controls the amount of | |
| 553 * color in the result. (The present implementation avoids these | |
| 554 * two problems, skipping both the colorization and the alpha | |
| 555 * blending at the end, and is about 3x faster) | |
| 556 * The basic operations with 32 bpp are as follows: | |
| 557 * // Immediate conversion to 32 bpp | |
| 558 * Pix *pixt1 = pixConvertTo32(pixs); | |
| 559 * // Do vertical shear | |
| 560 * Pix *pixr = pixQuadraticVerticalShear(pixt1, L_WARP_TO_RIGHT, | |
| 561 * ybendt, ybendb, | |
| 562 * L_BRING_IN_WHITE); | |
| 563 * // Colorize two versions, toward red and cyan | |
| 564 * Pix *pixc = pixCopy(NULL, pixr); | |
| 565 * l_int32 thresh = 150; // if higher, get less original color | |
| 566 * pixColorGray(pixr, NULL, L_PAINT_DARK, thresh, 255, 0, 0); | |
| 567 * pixColorGray(pixc, NULL, L_PAINT_DARK, thresh, 0, 255, 255); | |
| 568 * // Shift the red pixels; e.g., by stretching | |
| 569 * Pix *pixrs = pixStretchHorizontal(pixr, L_WARP_TO_RIGHT, | |
| 570 * L_QUADRATIC_WARP, zbend, | |
| 571 * L_INTERPOLATED, | |
| 572 * L_BRING_IN_WHITE); | |
| 573 * // Blend the shifted red and unshifted cyan 50:50 | |
| 574 * Pix *pixg = pixCreate(w, h, 8); | |
| 575 * pixSetAllArbitrary(pixg, 128); | |
| 576 * pixd = pixBlendWithGrayMask(pixrs, pixc, pixg, 0, 0); | |
| 577 * </pre> | |
| 578 */ | |
| 579 PIX * | |
| 580 pixWarpStereoscopic(PIX *pixs, | |
| 581 l_int32 zbend, | |
| 582 l_int32 zshiftt, | |
| 583 l_int32 zshiftb, | |
| 584 l_int32 ybendt, | |
| 585 l_int32 ybendb, | |
| 586 l_int32 redleft) | |
| 587 { | |
| 588 l_int32 w, h, zshift; | |
| 589 l_float32 angle; | |
| 590 BOX *boxleft, *boxright; | |
| 591 PIX *pix1, *pix2, *pix3, *pix4, *pixr, *pixg, *pixb; | |
| 592 PIX *pixv1, *pixv2, *pixv3, *pixv4; | |
| 593 PIX *pixrs, *pixrss; | |
| 594 PIX *pixd; | |
| 595 | |
| 596 if (!pixs) | |
| 597 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 598 | |
| 599 /* Convert to the output depth, 32 bpp. */ | |
| 600 pix1 = pixConvertTo32(pixs); | |
| 601 | |
| 602 /* If requested, do a quad vertical shearing, pushing pixels up | |
| 603 * or down, depending on their distance from the centerline. */ | |
| 604 pixGetDimensions(pixs, &w, &h, NULL); | |
| 605 boxleft = boxCreate(0, 0, w / 2, h); | |
| 606 boxright = boxCreate(w / 2, 0, w - w / 2, h); | |
| 607 if (ybendt != 0 || ybendb != 0) { | |
| 608 pixv1 = pixClipRectangle(pix1, boxleft, NULL); | |
| 609 pixv2 = pixClipRectangle(pix1, boxright, NULL); | |
| 610 pixv3 = pixQuadraticVShear(pixv1, L_WARP_TO_LEFT, ybendt, | |
| 611 ybendb, L_INTERPOLATED, | |
| 612 L_BRING_IN_WHITE); | |
| 613 pixv4 = pixQuadraticVShear(pixv2, L_WARP_TO_RIGHT, ybendt, | |
| 614 ybendb, L_INTERPOLATED, | |
| 615 L_BRING_IN_WHITE); | |
| 616 pix2 = pixCreate(w, h, 32); | |
| 617 pixRasterop(pix2, 0, 0, w / 2, h, PIX_SRC, pixv3, 0, 0); | |
| 618 pixRasterop(pix2, w / 2, 0, w - w / 2, h, PIX_SRC, pixv4, 0, 0); | |
| 619 pixDestroy(&pixv1); | |
| 620 pixDestroy(&pixv2); | |
| 621 pixDestroy(&pixv3); | |
| 622 pixDestroy(&pixv4); | |
| 623 } else { | |
| 624 pix2 = pixClone(pix1); | |
| 625 } | |
| 626 pixDestroy(&pix1); | |
| 627 | |
| 628 /* Split out the 3 components */ | |
| 629 pixr = pixGetRGBComponent(pix2, COLOR_RED); | |
| 630 pixg = pixGetRGBComponent(pix2, COLOR_GREEN); | |
| 631 pixb = pixGetRGBComponent(pix2, COLOR_BLUE); | |
| 632 pixDestroy(&pix2); | |
| 633 | |
| 634 /* The direction of the stereo disparity below is set | |
| 635 * for the red filter to be over the left eye. If the red | |
| 636 * filter is over the right eye, invert the horizontal shifts. */ | |
| 637 if (redleft) { | |
| 638 zbend = -zbend; | |
| 639 zshiftt = -zshiftt; | |
| 640 zshiftb = -zshiftb; | |
| 641 } | |
| 642 | |
| 643 /* Shift the red pixels horizontally by an amount that | |
| 644 * increases quadratically from the centerline. */ | |
| 645 if (zbend == 0) { | |
| 646 pixrs = pixClone(pixr); | |
| 647 } else { | |
| 648 pix1 = pixClipRectangle(pixr, boxleft, NULL); | |
| 649 pix2 = pixClipRectangle(pixr, boxright, NULL); | |
| 650 pix3 = pixStretchHorizontal(pix1, L_WARP_TO_LEFT, L_QUADRATIC_WARP, | |
| 651 zbend, L_INTERPOLATED, L_BRING_IN_WHITE); | |
| 652 pix4 = pixStretchHorizontal(pix2, L_WARP_TO_RIGHT, L_QUADRATIC_WARP, | |
| 653 zbend, L_INTERPOLATED, L_BRING_IN_WHITE); | |
| 654 pixrs = pixCreate(w, h, 8); | |
| 655 pixRasterop(pixrs, 0, 0, w / 2, h, PIX_SRC, pix3, 0, 0); | |
| 656 pixRasterop(pixrs, w / 2, 0, w - w / 2, h, PIX_SRC, pix4, 0, 0); | |
| 657 pixDestroy(&pix1); | |
| 658 pixDestroy(&pix2); | |
| 659 pixDestroy(&pix3); | |
| 660 pixDestroy(&pix4); | |
| 661 } | |
| 662 | |
| 663 /* Perform a combination of horizontal shift and shear of | |
| 664 * red pixels. The causes the plane of the image to tilt and | |
| 665 * also move forward or backward. */ | |
| 666 if (zshiftt == 0 && zshiftb == 0) { | |
| 667 pixrss = pixClone(pixrs); | |
| 668 } else if (zshiftt == zshiftb) { | |
| 669 pixrss = pixTranslate(NULL, pixrs, zshiftt, 0, L_BRING_IN_WHITE); | |
| 670 } else { | |
| 671 angle = (l_float32)(zshiftb - zshiftt) / | |
| 672 L_MAX(1.0f, (l_float32)pixGetHeight(pixrs)); | |
| 673 zshift = (zshiftt + zshiftb) / 2; | |
| 674 pix1 = pixTranslate(NULL, pixrs, zshift, 0, L_BRING_IN_WHITE); | |
| 675 pixrss = pixHShearLI(pix1, h / 2, angle, L_BRING_IN_WHITE); | |
| 676 pixDestroy(&pix1); | |
| 677 } | |
| 678 | |
| 679 /* Combine the unchanged cyan (g,b) image with the shifted red */ | |
| 680 pixd = pixCreateRGBImage(pixrss, pixg, pixb); | |
| 681 | |
| 682 boxDestroy(&boxleft); | |
| 683 boxDestroy(&boxright); | |
| 684 pixDestroy(&pixrs); | |
| 685 pixDestroy(&pixrss); | |
| 686 pixDestroy(&pixr); | |
| 687 pixDestroy(&pixg); | |
| 688 pixDestroy(&pixb); | |
| 689 return pixd; | |
| 690 } | |
| 691 | |
| 692 | |
| 693 /*----------------------------------------------------------------------* | |
| 694 * Linear and quadratic horizontal stretching * | |
| 695 *----------------------------------------------------------------------*/ | |
| 696 /*! | |
| 697 * \brief pixStretchHorizontal() | |
| 698 * | |
| 699 * \param[in] pixs 1, 8 or 32 bpp | |
| 700 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT | |
| 701 * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP | |
| 702 * \param[in] hmax horizontal displacement at edge | |
| 703 * \param[in] operation L_SAMPLED or L_INTERPOLATED | |
| 704 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK | |
| 705 * \return pixd stretched/compressed, or NULL on error | |
| 706 * | |
| 707 * <pre> | |
| 708 * Notes: | |
| 709 * (1) If %hmax > 0, this is an increase in the coordinate value of | |
| 710 * pixels in pixd, relative to the same pixel in pixs. | |
| 711 * (2) If %dir == L_WARP_TO_LEFT, the pixels on the right edge of | |
| 712 * the image are not moved. So, for example, if %hmax > 0 | |
| 713 * and %dir == L_WARP_TO_LEFT, the pixels in pixd are | |
| 714 * contracted toward the right edge of the image, relative | |
| 715 * to those in pixs. | |
| 716 * (3) If %type == L_LINEAR_WARP, the pixel positions are moved | |
| 717 * to the left or right by an amount that varies linearly with | |
| 718 * the horizontal location. | |
| 719 * (4) If %operation == L_SAMPLED, the dest pixels are taken from | |
| 720 * the nearest src pixel. Otherwise, we use linear interpolation | |
| 721 * between pairs of sampled pixels. | |
| 722 * </pre> | |
| 723 */ | |
| 724 PIX * | |
| 725 pixStretchHorizontal(PIX *pixs, | |
| 726 l_int32 dir, | |
| 727 l_int32 type, | |
| 728 l_int32 hmax, | |
| 729 l_int32 operation, | |
| 730 l_int32 incolor) | |
| 731 { | |
| 732 l_int32 d; | |
| 733 | |
| 734 if (!pixs) | |
| 735 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 736 d = pixGetDepth(pixs); | |
| 737 if (d != 1 && d != 8 && d != 32) | |
| 738 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL); | |
| 739 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) | |
| 740 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 741 if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP) | |
| 742 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 743 if (operation != L_SAMPLED && operation != L_INTERPOLATED) | |
| 744 return (PIX *)ERROR_PTR("invalid operation", __func__, NULL); | |
| 745 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 746 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 747 if (d == 1 && operation == L_INTERPOLATED) { | |
| 748 L_WARNING("Using sampling for 1 bpp\n", __func__); | |
| 749 operation = L_INTERPOLATED; | |
| 750 } | |
| 751 | |
| 752 if (operation == L_SAMPLED) | |
| 753 return pixStretchHorizontalSampled(pixs, dir, type, hmax, incolor); | |
| 754 else | |
| 755 return pixStretchHorizontalLI(pixs, dir, type, hmax, incolor); | |
| 756 } | |
| 757 | |
| 758 | |
| 759 /*! | |
| 760 * \brief pixStretchHorizontalSampled() | |
| 761 * | |
| 762 * \param[in] pixs 1, 8 or 32 bpp | |
| 763 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT | |
| 764 * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP | |
| 765 * \param[in] hmax horizontal displacement at edge | |
| 766 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK | |
| 767 * \return pixd stretched/compressed, or NULL on error | |
| 768 * | |
| 769 * <pre> | |
| 770 * Notes: | |
| 771 * (1) See pixStretchHorizontal() for details. | |
| 772 * </pre> | |
| 773 */ | |
| 774 PIX * | |
| 775 pixStretchHorizontalSampled(PIX *pixs, | |
| 776 l_int32 dir, | |
| 777 l_int32 type, | |
| 778 l_int32 hmax, | |
| 779 l_int32 incolor) | |
| 780 { | |
| 781 l_int32 i, j, jd, w, wm, h, d, wpls, wpld, val; | |
| 782 l_uint32 *datas, *datad, *lines, *lined; | |
| 783 PIX *pixd; | |
| 784 | |
| 785 if (!pixs) | |
| 786 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 787 pixGetDimensions(pixs, &w, &h, &d); | |
| 788 if (d != 1 && d != 8 && d != 32) | |
| 789 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL); | |
| 790 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) | |
| 791 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 792 if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP) | |
| 793 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 794 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 795 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 796 | |
| 797 pixd = pixCreateTemplate(pixs); | |
| 798 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); | |
| 799 datas = pixGetData(pixs); | |
| 800 datad = pixGetData(pixd); | |
| 801 wpls = pixGetWpl(pixs); | |
| 802 wpld = pixGetWpl(pixd); | |
| 803 wm = w - 1; | |
| 804 for (jd = 0; jd < w; jd++) { | |
| 805 if (dir == L_WARP_TO_LEFT) { | |
| 806 if (type == L_LINEAR_WARP) | |
| 807 j = jd - (hmax * (wm - jd)) / wm; | |
| 808 else /* L_QUADRATIC_WARP */ | |
| 809 j = jd - (hmax * (wm - jd) * (wm - jd)) / (wm * wm); | |
| 810 } else if (dir == L_WARP_TO_RIGHT) { | |
| 811 if (type == L_LINEAR_WARP) | |
| 812 j = jd - (hmax * jd) / wm; | |
| 813 else /* L_QUADRATIC_WARP */ | |
| 814 j = jd - (hmax * jd * jd) / (wm * wm); | |
| 815 } | |
| 816 if (j < 0 || j > w - 1) continue; | |
| 817 | |
| 818 switch (d) | |
| 819 { | |
| 820 case 1: | |
| 821 for (i = 0; i < h; i++) { | |
| 822 lines = datas + i * wpls; | |
| 823 lined = datad + i * wpld; | |
| 824 val = GET_DATA_BIT(lines, j); | |
| 825 if (val) | |
| 826 SET_DATA_BIT(lined, jd); | |
| 827 } | |
| 828 break; | |
| 829 case 8: | |
| 830 for (i = 0; i < h; i++) { | |
| 831 lines = datas + i * wpls; | |
| 832 lined = datad + i * wpld; | |
| 833 val = GET_DATA_BYTE(lines, j); | |
| 834 SET_DATA_BYTE(lined, jd, val); | |
| 835 } | |
| 836 break; | |
| 837 case 32: | |
| 838 for (i = 0; i < h; i++) { | |
| 839 lines = datas + i * wpls; | |
| 840 lined = datad + i * wpld; | |
| 841 lined[jd] = lines[j]; | |
| 842 } | |
| 843 break; | |
| 844 default: | |
| 845 L_ERROR("invalid depth: %d\n", __func__, d); | |
| 846 pixDestroy(&pixd); | |
| 847 return NULL; | |
| 848 } | |
| 849 } | |
| 850 | |
| 851 return pixd; | |
| 852 } | |
| 853 | |
| 854 | |
| 855 /*! | |
| 856 * \brief pixStretchHorizontalLI() | |
| 857 * | |
| 858 * \param[in] pixs 1, 8 or 32 bpp | |
| 859 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT | |
| 860 * \param[in] type L_LINEAR_WARP or L_QUADRATIC_WARP | |
| 861 * \param[in] hmax horizontal displacement at edge | |
| 862 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK | |
| 863 * \return pixd stretched/compressed, or NULL on error | |
| 864 * | |
| 865 * <pre> | |
| 866 * Notes: | |
| 867 * (1) See pixStretchHorizontal() for details. | |
| 868 * </pre> | |
| 869 */ | |
| 870 PIX * | |
| 871 pixStretchHorizontalLI(PIX *pixs, | |
| 872 l_int32 dir, | |
| 873 l_int32 type, | |
| 874 l_int32 hmax, | |
| 875 l_int32 incolor) | |
| 876 { | |
| 877 l_int32 i, j, jd, jp, jf, w, wm, h, d, wpls, wpld, val, rval, gval, bval; | |
| 878 l_uint32 word0, word1; | |
| 879 l_uint32 *datas, *datad, *lines, *lined; | |
| 880 PIX *pixd; | |
| 881 | |
| 882 if (!pixs) | |
| 883 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 884 pixGetDimensions(pixs, &w, &h, &d); | |
| 885 if (d != 8 && d != 32) | |
| 886 return (PIX *)ERROR_PTR("pixs not 8 or 32 bpp", __func__, NULL); | |
| 887 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) | |
| 888 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 889 if (type != L_LINEAR_WARP && type != L_QUADRATIC_WARP) | |
| 890 return (PIX *)ERROR_PTR("invalid type", __func__, NULL); | |
| 891 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 892 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 893 | |
| 894 /* Standard linear interpolation, subdividing each pixel into 64 */ | |
| 895 pixd = pixCreateTemplate(pixs); | |
| 896 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); | |
| 897 datas = pixGetData(pixs); | |
| 898 datad = pixGetData(pixd); | |
| 899 wpls = pixGetWpl(pixs); | |
| 900 wpld = pixGetWpl(pixd); | |
| 901 wm = w - 1; | |
| 902 for (jd = 0; jd < w; jd++) { | |
| 903 if (dir == L_WARP_TO_LEFT) { | |
| 904 if (type == L_LINEAR_WARP) | |
| 905 j = 64 * jd - 64 * (hmax * (wm - jd)) / wm; | |
| 906 else /* L_QUADRATIC_WARP */ | |
| 907 j = 64 * jd - 64 * (hmax * (wm - jd) * (wm - jd)) / (wm * wm); | |
| 908 } else if (dir == L_WARP_TO_RIGHT) { | |
| 909 if (type == L_LINEAR_WARP) | |
| 910 j = 64 * jd - 64 * (hmax * jd) / wm; | |
| 911 else /* L_QUADRATIC_WARP */ | |
| 912 j = 64 * jd - 64 * (hmax * jd * jd) / (wm * wm); | |
| 913 } | |
| 914 jp = j / 64; | |
| 915 jf = j & 0x3f; | |
| 916 if (jp < 0 || jp > wm) continue; | |
| 917 | |
| 918 switch (d) | |
| 919 { | |
| 920 case 8: | |
| 921 if (jp < wm) { | |
| 922 for (i = 0; i < h; i++) { | |
| 923 lines = datas + i * wpls; | |
| 924 lined = datad + i * wpld; | |
| 925 val = ((63 - jf) * GET_DATA_BYTE(lines, jp) + | |
| 926 jf * GET_DATA_BYTE(lines, jp + 1) + 31) / 63; | |
| 927 SET_DATA_BYTE(lined, jd, val); | |
| 928 } | |
| 929 } else { /* jp == wm */ | |
| 930 for (i = 0; i < h; i++) { | |
| 931 lines = datas + i * wpls; | |
| 932 lined = datad + i * wpld; | |
| 933 val = GET_DATA_BYTE(lines, jp); | |
| 934 SET_DATA_BYTE(lined, jd, val); | |
| 935 } | |
| 936 } | |
| 937 break; | |
| 938 case 32: | |
| 939 if (jp < wm) { | |
| 940 for (i = 0; i < h; i++) { | |
| 941 lines = datas + i * wpls; | |
| 942 lined = datad + i * wpld; | |
| 943 word0 = *(lines + jp); | |
| 944 word1 = *(lines + jp + 1); | |
| 945 rval = ((63 - jf) * ((word0 >> L_RED_SHIFT) & 0xff) + | |
| 946 jf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; | |
| 947 gval = ((63 - jf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + | |
| 948 jf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; | |
| 949 bval = ((63 - jf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + | |
| 950 jf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; | |
| 951 composeRGBPixel(rval, gval, bval, lined + jd); | |
| 952 } | |
| 953 } else { /* jp == wm */ | |
| 954 for (i = 0; i < h; i++) { | |
| 955 lines = datas + i * wpls; | |
| 956 lined = datad + i * wpld; | |
| 957 lined[jd] = lines[jp]; | |
| 958 } | |
| 959 } | |
| 960 break; | |
| 961 default: | |
| 962 L_ERROR("invalid depth: %d\n", __func__, d); | |
| 963 pixDestroy(&pixd); | |
| 964 return NULL; | |
| 965 } | |
| 966 } | |
| 967 | |
| 968 return pixd; | |
| 969 } | |
| 970 | |
| 971 | |
| 972 /*----------------------------------------------------------------------* | |
| 973 * Quadratic vertical shear * | |
| 974 *----------------------------------------------------------------------*/ | |
| 975 /*! | |
| 976 * \brief pixQuadraticVShear() | |
| 977 * | |
| 978 * \param[in] pixs 1, 8 or 32 bpp | |
| 979 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT | |
| 980 * \param[in] vmaxt max vertical displacement at edge and at top | |
| 981 * \param[in] vmaxb max vertical displacement at edge and at bottom | |
| 982 * \param[in] operation L_SAMPLED or L_INTERPOLATED | |
| 983 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK | |
| 984 * \return pixd stretched, or NULL on error | |
| 985 * | |
| 986 * <pre> | |
| 987 * Notes: | |
| 988 * (1) This gives a quadratic bending, upward or downward, as you | |
| 989 * move to the left or right. | |
| 990 * (2) If %dir == L_WARP_TO_LEFT, the right edge is unchanged, and | |
| 991 * the left edge pixels are moved maximally up or down. | |
| 992 * (3) Parameters %vmaxt and %vmaxb control the maximum amount of | |
| 993 * vertical pixel shear at the top and bottom, respectively. | |
| 994 * If %vmaxt > 0, the vertical displacement of pixels at the | |
| 995 * top is downward. Likewise, if %vmaxb > 0, the vertical | |
| 996 * displacement of pixels at the bottom is downward. | |
| 997 * (4) If %operation == L_SAMPLED, the dest pixels are taken from | |
| 998 * the nearest src pixel. Otherwise, we use linear interpolation | |
| 999 * between pairs of sampled pixels. | |
| 1000 * (5) This is for quadratic shear. For uniform (linear) shear, | |
| 1001 * use the standard shear operators. | |
| 1002 * </pre> | |
| 1003 */ | |
| 1004 PIX * | |
| 1005 pixQuadraticVShear(PIX *pixs, | |
| 1006 l_int32 dir, | |
| 1007 l_int32 vmaxt, | |
| 1008 l_int32 vmaxb, | |
| 1009 l_int32 operation, | |
| 1010 l_int32 incolor) | |
| 1011 { | |
| 1012 l_int32 w, h, d; | |
| 1013 | |
| 1014 if (!pixs) | |
| 1015 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1016 pixGetDimensions(pixs, &w, &h, &d); | |
| 1017 if (d != 1 && d != 8 && d != 32) | |
| 1018 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL); | |
| 1019 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) | |
| 1020 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 1021 if (operation != L_SAMPLED && operation != L_INTERPOLATED) | |
| 1022 return (PIX *)ERROR_PTR("invalid operation", __func__, NULL); | |
| 1023 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 1024 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 1025 | |
| 1026 if (vmaxt == 0 && vmaxb == 0) | |
| 1027 return pixCopy(NULL, pixs); | |
| 1028 | |
| 1029 if (operation == L_INTERPOLATED && d == 1) { | |
| 1030 L_WARNING("no interpolation for 1 bpp; using sampling\n", __func__); | |
| 1031 operation = L_SAMPLED; | |
| 1032 } | |
| 1033 | |
| 1034 if (operation == L_SAMPLED) | |
| 1035 return pixQuadraticVShearSampled(pixs, dir, vmaxt, vmaxb, incolor); | |
| 1036 else /* operation == L_INTERPOLATED */ | |
| 1037 return pixQuadraticVShearLI(pixs, dir, vmaxt, vmaxb, incolor); | |
| 1038 } | |
| 1039 | |
| 1040 | |
| 1041 /*! | |
| 1042 * \brief pixQuadraticVShearSampled() | |
| 1043 * | |
| 1044 * \param[in] pixs 1, 8 or 32 bpp | |
| 1045 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT | |
| 1046 * \param[in] vmaxt max vertical displacement at edge and at top | |
| 1047 * \param[in] vmaxb max vertical displacement at edge and at bottom | |
| 1048 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK | |
| 1049 * \return pixd stretched, or NULL on error | |
| 1050 * | |
| 1051 * <pre> | |
| 1052 * Notes: | |
| 1053 * (1) See pixQuadraticVShear() for details. | |
| 1054 * </pre> | |
| 1055 */ | |
| 1056 PIX * | |
| 1057 pixQuadraticVShearSampled(PIX *pixs, | |
| 1058 l_int32 dir, | |
| 1059 l_int32 vmaxt, | |
| 1060 l_int32 vmaxb, | |
| 1061 l_int32 incolor) | |
| 1062 { | |
| 1063 l_int32 i, j, id, w, h, d, wm, hm, wpls, wpld, val; | |
| 1064 l_uint32 *datas, *datad, *lines, *lined; | |
| 1065 l_float32 delrowt, delrowb, denom1, denom2, dely; | |
| 1066 PIX *pixd; | |
| 1067 | |
| 1068 if (!pixs) | |
| 1069 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1070 pixGetDimensions(pixs, &w, &h, &d); | |
| 1071 if (d != 1 && d != 8 && d != 32) | |
| 1072 return (PIX *)ERROR_PTR("pixs not 1, 8 or 32 bpp", __func__, NULL); | |
| 1073 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) | |
| 1074 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 1075 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 1076 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 1077 | |
| 1078 if (vmaxt == 0 && vmaxb == 0) | |
| 1079 return pixCopy(NULL, pixs); | |
| 1080 | |
| 1081 pixd = pixCreateTemplate(pixs); | |
| 1082 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); | |
| 1083 datas = pixGetData(pixs); | |
| 1084 datad = pixGetData(pixd); | |
| 1085 wpls = pixGetWpl(pixs); | |
| 1086 wpld = pixGetWpl(pixd); | |
| 1087 wm = w - 1; | |
| 1088 hm = h - 1; | |
| 1089 denom1 = 1.f / (l_float32)h; | |
| 1090 denom2 = 1.f / (l_float32)(wm * wm); | |
| 1091 for (j = 0; j < w; j++) { | |
| 1092 if (dir == L_WARP_TO_LEFT) { | |
| 1093 delrowt = (l_float32)(vmaxt * (wm - j) * (wm - j)) * denom2; | |
| 1094 delrowb = (l_float32)(vmaxb * (wm - j) * (wm - j)) * denom2; | |
| 1095 } else if (dir == L_WARP_TO_RIGHT) { | |
| 1096 delrowt = (l_float32)(vmaxt * j * j) * denom2; | |
| 1097 delrowb = (l_float32)(vmaxb * j * j) * denom2; | |
| 1098 } | |
| 1099 switch (d) | |
| 1100 { | |
| 1101 case 1: | |
| 1102 for (id = 0; id < h; id++) { | |
| 1103 dely = (delrowt * (hm - id) + delrowb * id) * denom1; | |
| 1104 i = id - (l_int32)(dely + 0.5); | |
| 1105 if (i < 0 || i > hm) continue; | |
| 1106 lines = datas + i * wpls; | |
| 1107 lined = datad + id * wpld; | |
| 1108 val = GET_DATA_BIT(lines, j); | |
| 1109 if (val) | |
| 1110 SET_DATA_BIT(lined, j); | |
| 1111 } | |
| 1112 break; | |
| 1113 case 8: | |
| 1114 for (id = 0; id < h; id++) { | |
| 1115 dely = (delrowt * (hm - id) + delrowb * id) * denom1; | |
| 1116 i = id - (l_int32)(dely + 0.5); | |
| 1117 if (i < 0 || i > hm) continue; | |
| 1118 lines = datas + i * wpls; | |
| 1119 lined = datad + id * wpld; | |
| 1120 val = GET_DATA_BYTE(lines, j); | |
| 1121 SET_DATA_BYTE(lined, j, val); | |
| 1122 } | |
| 1123 break; | |
| 1124 case 32: | |
| 1125 for (id = 0; id < h; id++) { | |
| 1126 dely = (delrowt * (hm - id) + delrowb * id) * denom1; | |
| 1127 i = id - (l_int32)(dely + 0.5); | |
| 1128 if (i < 0 || i > hm) continue; | |
| 1129 lines = datas + i * wpls; | |
| 1130 lined = datad + id * wpld; | |
| 1131 lined[j] = lines[j]; | |
| 1132 } | |
| 1133 break; | |
| 1134 default: | |
| 1135 L_ERROR("invalid depth: %d\n", __func__, d); | |
| 1136 pixDestroy(&pixd); | |
| 1137 return NULL; | |
| 1138 } | |
| 1139 } | |
| 1140 | |
| 1141 return pixd; | |
| 1142 } | |
| 1143 | |
| 1144 | |
| 1145 /*! | |
| 1146 * \brief pixQuadraticVShearLI() | |
| 1147 * | |
| 1148 * \param[in] pixs 8 or 32 bpp, or colormapped | |
| 1149 * \param[in] dir L_WARP_TO_LEFT or L_WARP_TO_RIGHT | |
| 1150 * \param[in] vmaxt max vertical displacement at edge and at top | |
| 1151 * \param[in] vmaxb max vertical displacement at edge and at bottom | |
| 1152 * \param[in] incolor L_BRING_IN_WHITE or L_BRING_IN_BLACK | |
| 1153 * \return pixd stretched, or NULL on error | |
| 1154 * | |
| 1155 * <pre> | |
| 1156 * Notes: | |
| 1157 * (1) See pixQuadraticVShear() for details. | |
| 1158 * </pre> | |
| 1159 */ | |
| 1160 PIX * | |
| 1161 pixQuadraticVShearLI(PIX *pixs, | |
| 1162 l_int32 dir, | |
| 1163 l_int32 vmaxt, | |
| 1164 l_int32 vmaxb, | |
| 1165 l_int32 incolor) | |
| 1166 { | |
| 1167 l_int32 i, j, id, yp, yf, w, h, d, wm, hm, wpls, wpld; | |
| 1168 l_int32 val, rval, gval, bval; | |
| 1169 l_uint32 word0, word1; | |
| 1170 l_uint32 *datas, *datad, *lines, *lined; | |
| 1171 l_float32 delrowt, delrowb, denom1, denom2, dely; | |
| 1172 PIX *pix, *pixd; | |
| 1173 PIXCMAP *cmap; | |
| 1174 | |
| 1175 if (!pixs) | |
| 1176 return (PIX *)ERROR_PTR("pixs not defined", __func__, NULL); | |
| 1177 pixGetDimensions(pixs, &w, &h, &d); | |
| 1178 if (d == 1) | |
| 1179 return (PIX *)ERROR_PTR("pixs is 1 bpp", __func__, NULL); | |
| 1180 cmap = pixGetColormap(pixs); | |
| 1181 if (d != 8 && d != 32 && !cmap) | |
| 1182 return (PIX *)ERROR_PTR("pixs not 8, 32 bpp, or cmap", __func__, NULL); | |
| 1183 if (dir != L_WARP_TO_LEFT && dir != L_WARP_TO_RIGHT) | |
| 1184 return (PIX *)ERROR_PTR("invalid direction", __func__, NULL); | |
| 1185 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) | |
| 1186 return (PIX *)ERROR_PTR("invalid incolor", __func__, NULL); | |
| 1187 | |
| 1188 if (vmaxt == 0 && vmaxb == 0) | |
| 1189 return pixCopy(NULL, pixs); | |
| 1190 | |
| 1191 /* Remove any existing colormap */ | |
| 1192 if (cmap) | |
| 1193 pix = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); | |
| 1194 else | |
| 1195 pix = pixClone(pixs); | |
| 1196 d = pixGetDepth(pix); | |
| 1197 if (d != 8 && d != 32) { | |
| 1198 pixDestroy(&pix); | |
| 1199 return (PIX *)ERROR_PTR("invalid depth", __func__, NULL); | |
| 1200 } | |
| 1201 | |
| 1202 /* Standard linear interp: subdivide each pixel into 64 parts */ | |
| 1203 pixd = pixCreateTemplate(pix); | |
| 1204 pixSetBlackOrWhite(pixd, L_BRING_IN_WHITE); | |
| 1205 datas = pixGetData(pix); | |
| 1206 datad = pixGetData(pixd); | |
| 1207 wpls = pixGetWpl(pix); | |
| 1208 wpld = pixGetWpl(pixd); | |
| 1209 wm = w - 1; | |
| 1210 hm = h - 1; | |
| 1211 denom1 = 1.0f / (l_float32)h; | |
| 1212 denom2 = 1.0f / (l_float32)(wm * wm); | |
| 1213 for (j = 0; j < w; j++) { | |
| 1214 if (dir == L_WARP_TO_LEFT) { | |
| 1215 delrowt = (l_float32)(vmaxt * (wm - j) * (wm - j)) * denom2; | |
| 1216 delrowb = (l_float32)(vmaxb * (wm - j) * (wm - j)) * denom2; | |
| 1217 } else if (dir == L_WARP_TO_RIGHT) { | |
| 1218 delrowt = (l_float32)(vmaxt * j * j) * denom2; | |
| 1219 delrowb = (l_float32)(vmaxb * j * j) * denom2; | |
| 1220 } | |
| 1221 switch (d) | |
| 1222 { | |
| 1223 case 8: | |
| 1224 for (id = 0; id < h; id++) { | |
| 1225 dely = (delrowt * (hm - id) + delrowb * id) * denom1; | |
| 1226 i = 64 * id - (l_int32)(64.0 * dely); | |
| 1227 yp = i / 64; | |
| 1228 yf = i & 63; | |
| 1229 if (yp < 0 || yp > hm) continue; | |
| 1230 lines = datas + yp * wpls; | |
| 1231 lined = datad + id * wpld; | |
| 1232 if (yp < hm) { | |
| 1233 val = ((63 - yf) * GET_DATA_BYTE(lines, j) + | |
| 1234 yf * GET_DATA_BYTE(lines + wpls, j) + 31) / 63; | |
| 1235 } else { /* yp == hm */ | |
| 1236 val = GET_DATA_BYTE(lines, j); | |
| 1237 } | |
| 1238 SET_DATA_BYTE(lined, j, val); | |
| 1239 } | |
| 1240 break; | |
| 1241 case 32: | |
| 1242 for (id = 0; id < h; id++) { | |
| 1243 dely = (delrowt * (hm - id) + delrowb * id) * denom1; | |
| 1244 i = 64 * id - (l_int32)(64.0 * dely); | |
| 1245 yp = i / 64; | |
| 1246 yf = i & 63; | |
| 1247 if (yp < 0 || yp > hm) continue; | |
| 1248 lines = datas + yp * wpls; | |
| 1249 lined = datad + id * wpld; | |
| 1250 if (yp < hm) { | |
| 1251 word0 = *(lines + j); | |
| 1252 word1 = *(lines + wpls + j); | |
| 1253 rval = ((63 - yf) * ((word0 >> L_RED_SHIFT) & 0xff) + | |
| 1254 yf * ((word1 >> L_RED_SHIFT) & 0xff) + 31) / 63; | |
| 1255 gval = ((63 - yf) * ((word0 >> L_GREEN_SHIFT) & 0xff) + | |
| 1256 yf * ((word1 >> L_GREEN_SHIFT) & 0xff) + 31) / 63; | |
| 1257 bval = ((63 - yf) * ((word0 >> L_BLUE_SHIFT) & 0xff) + | |
| 1258 yf * ((word1 >> L_BLUE_SHIFT) & 0xff) + 31) / 63; | |
| 1259 composeRGBPixel(rval, gval, bval, lined + j); | |
| 1260 } else { /* yp == hm */ | |
| 1261 lined[j] = lines[j]; | |
| 1262 } | |
| 1263 } | |
| 1264 break; | |
| 1265 default: | |
| 1266 L_ERROR("invalid depth: %d\n", __func__, d); | |
| 1267 pixDestroy(&pix); | |
| 1268 pixDestroy(&pixd); | |
| 1269 return NULL; | |
| 1270 } | |
| 1271 } | |
| 1272 | |
| 1273 pixDestroy(&pix); | |
| 1274 return pixd; | |
| 1275 } | |
| 1276 | |
| 1277 | |
| 1278 /*----------------------------------------------------------------------* | |
| 1279 * Stereo from a pair of images * | |
| 1280 *----------------------------------------------------------------------*/ | |
| 1281 /*! | |
| 1282 * \brief pixStereoFromPair() | |
| 1283 * | |
| 1284 * \param[in] pix1 32 bpp rgb | |
| 1285 * \param[in] pix2 32 bpp rgb | |
| 1286 * \param[in] rwt, gwt, bwt weighting factors used for each component in | |
| 1287 pix1 to determine the output red channel | |
| 1288 * \return pixd stereo enhanced, or NULL on error | |
| 1289 * | |
| 1290 * <pre> | |
| 1291 * Notes: | |
| 1292 * (1) pix1 and pix2 are a pair of stereo images, ideally taken | |
| 1293 * concurrently in the same plane, with some lateral translation. | |
| 1294 * (2) The output red channel is determined from %pix1. | |
| 1295 * The output green and blue channels are taken from the green | |
| 1296 * and blue channels, respectively, of %pix2. | |
| 1297 * (3) The weights determine how much of each component in %pix1 | |
| 1298 * goes into the output red channel. The sum of weights | |
| 1299 * must be 1.0. If it's not, we scale the weights to | |
| 1300 * satisfy this criterion. | |
| 1301 * (4) The most general pixel mapping allowed here is: | |
| 1302 * rval = rwt * r1 + gwt * g1 + bwt * b1 (from pix1) | |
| 1303 * gval = g2 (from pix2) | |
| 1304 * bval = b2 (from pix2) | |
| 1305 * (5) The simplest method is to use rwt = 1.0, gwt = 0.0, bwt = 0.0, | |
| 1306 * but this causes unpleasant visual artifacts with red in the image. | |
| 1307 * Use of green and blue from %pix1 in the red channel, | |
| 1308 * instead of red, tends to fix that problem. | |
| 1309 * </pre> | |
| 1310 */ | |
| 1311 PIX * | |
| 1312 pixStereoFromPair(PIX *pix1, | |
| 1313 PIX *pix2, | |
| 1314 l_float32 rwt, | |
| 1315 l_float32 gwt, | |
| 1316 l_float32 bwt) | |
| 1317 { | |
| 1318 l_int32 i, j, w, h, wpl1, wpl2, rval, gval, bval; | |
| 1319 l_uint32 word1, word2; | |
| 1320 l_uint32 *data1, *data2, *datad, *line1, *line2, *lined; | |
| 1321 l_float32 sum; | |
| 1322 PIX *pixd; | |
| 1323 | |
| 1324 if (!pix1 || !pix2) | |
| 1325 return (PIX *)ERROR_PTR("pix1, pix2 not both defined", __func__, NULL); | |
| 1326 if (pixGetDepth(pix1) != 32 || pixGetDepth(pix2) != 32) | |
| 1327 return (PIX *)ERROR_PTR("pix1, pix2 not both 32 bpp", __func__, NULL); | |
| 1328 | |
| 1329 /* Make sure the sum of weights is 1.0; otherwise, you can get | |
| 1330 * overflow in the gray value. */ | |
| 1331 if (rwt == 0.0 && gwt == 0.0 && bwt == 0.0) { | |
| 1332 rwt = DefaultRedWeight; | |
| 1333 gwt = DefaultGreenWeight; | |
| 1334 bwt = DefaultBlueWeight; | |
| 1335 } | |
| 1336 sum = rwt + gwt + bwt; | |
| 1337 if (L_ABS(sum - 1.0) > 0.0001) { /* maintain ratios with sum == 1.0 */ | |
| 1338 L_WARNING("weights don't sum to 1; maintaining ratios\n", __func__); | |
| 1339 rwt = rwt / sum; | |
| 1340 gwt = gwt / sum; | |
| 1341 bwt = bwt / sum; | |
| 1342 } | |
| 1343 | |
| 1344 pixGetDimensions(pix1, &w, &h, NULL); | |
| 1345 pixd = pixCreateTemplate(pix1); | |
| 1346 data1 = pixGetData(pix1); | |
| 1347 data2 = pixGetData(pix2); | |
| 1348 datad = pixGetData(pixd); | |
| 1349 wpl1 = pixGetWpl(pix1); | |
| 1350 wpl2 = pixGetWpl(pix2); | |
| 1351 for (i = 0; i < h; i++) { | |
| 1352 line1 = data1 + i * wpl1; | |
| 1353 line2 = data2 + i * wpl2; | |
| 1354 lined = datad + i * wpl1; /* wpl1 works for pixd */ | |
| 1355 for (j = 0; j < w; j++) { | |
| 1356 word1 = *(line1 + j); | |
| 1357 word2 = *(line2 + j); | |
| 1358 rval = (l_int32)(rwt * ((word1 >> L_RED_SHIFT) & 0xff) + | |
| 1359 gwt * ((word1 >> L_GREEN_SHIFT) & 0xff) + | |
| 1360 bwt * ((word1 >> L_BLUE_SHIFT) & 0xff) + 0.5); | |
| 1361 gval = (word2 >> L_GREEN_SHIFT) & 0xff; | |
| 1362 bval = (word2 >> L_BLUE_SHIFT) & 0xff; | |
| 1363 composeRGBPixel(rval, gval, bval, lined + j); | |
| 1364 } | |
| 1365 } | |
| 1366 | |
| 1367 return pixd; | |
| 1368 } |
