Leptonica 1.68
C Image Processing Library
|
00001 /*====================================================================* 00002 - Copyright (C) 2001 Leptonica. All rights reserved. 00003 - This software is distributed in the hope that it will be 00004 - useful, but with NO WARRANTY OF ANY KIND. 00005 - No author or distributor accepts responsibility to anyone for the 00006 - consequences of using this software, or for whether it serves any 00007 - particular purpose or works at all, unless he or she says so in 00008 - writing. Everyone is granted permission to copy, modify and 00009 - redistribute this source code, for commercial or non-commercial 00010 - purposes, with the following restrictions: (1) the origin of this 00011 - source code must not be misrepresented; (2) modified versions must 00012 - be plainly marked as such; and (3) this notice may not be removed 00013 - or altered from any source or modified source distribution. 00014 *====================================================================*/ 00015 00016 00017 /* 00018 * rotate.c 00019 * 00020 * General rotation about image center 00021 * PIX *pixRotate() 00022 * PIX *pixEmbedForRotation() 00023 * 00024 * General rotation by sampling 00025 * PIX *pixRotateBySampling() 00026 * 00027 * Nice (slow) rotation of 1 bpp image 00028 * PIX *pixRotateBinaryNice() 00029 * 00030 * Rotation including alpha (blend) component and gamma transform 00031 * PIX *pixRotateWithAlpha() 00032 * PIX *pixRotateGammaXform() 00033 * 00034 * Rotations are measured in radians; clockwise is positive. 00035 * 00036 * The general rotation pixRotate() does the best job for 00037 * rotating about the image center. For 1 bpp, it uses shear; 00038 * for others, it uses either shear or area mapping. 00039 * If requested, it expands the output image so that no pixels are lost 00040 * in the rotation, and this can be done on multiple successive shears 00041 * without expanding beyond the maximum necessary size. 00042 */ 00043 00044 #include <math.h> 00045 #include "allheaders.h" 00046 00047 extern l_float32 AlphaMaskBorderVals[2]; 00048 static const l_float32 VERY_SMALL_ANGLE = 0.001; /* radians; ~0.06 degrees */ 00049 00050 00051 /*------------------------------------------------------------------* 00052 * General rotation about the center * 00053 *------------------------------------------------------------------*/ 00054 /*! 00055 * pixRotate() 00056 * 00057 * Input: pixs (1, 2, 4, 8, 32 bpp rgb) 00058 * angle (radians; clockwise is positive) 00059 * type (L_ROTATE_AREA_MAP, L_ROTATE_SHEAR, L_ROTATE_SAMPLING) 00060 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00061 * width (original width; use 0 to avoid embedding) 00062 * height (original height; use 0 to avoid embedding) 00063 * Return: pixd, or null on error 00064 * 00065 * Notes: 00066 * (1) Rotation is about the center of the image. 00067 * (2) For very small rotations, just return a clone. 00068 * (3) Rotation brings either white or black pixels in 00069 * from outside the image. 00070 * (4) Above 20 degrees, if rotation by shear is requested, we rotate 00071 * by sampling. 00072 * (5) Colormaps are removed for rotation by area map and shear. 00073 * (6) The dest can be expanded so that no image pixels 00074 * are lost. To invoke expansion, input the original 00075 * width and height. For repeated rotation, use of the 00076 * original width and height allows the expansion to 00077 * stop at the maximum required size, which is a square 00078 * with side = sqrt(w*w + h*h). 00079 * 00080 * *** Warning: implicit assumption about RGB component ordering *** 00081 */ 00082 PIX * 00083 pixRotate(PIX *pixs, 00084 l_float32 angle, 00085 l_int32 type, 00086 l_int32 incolor, 00087 l_int32 width, 00088 l_int32 height) 00089 { 00090 l_int32 w, h, d; 00091 l_uint32 fillval; 00092 PIX *pixt1, *pixt2, *pixt3, *pixd; 00093 PIXCMAP *cmap; 00094 00095 PROCNAME("pixRotate"); 00096 00097 if (!pixs) 00098 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00099 if (type != L_ROTATE_SHEAR && type != L_ROTATE_AREA_MAP && 00100 type != L_ROTATE_SAMPLING) 00101 return (PIX *)ERROR_PTR("invalid type", procName, NULL); 00102 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00103 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00104 00105 if (L_ABS(angle) < VERY_SMALL_ANGLE) 00106 return pixClone(pixs); 00107 00108 /* Don't rotate by shear more than 20 degrees */ 00109 if (L_ABS(angle) > 0.35 && type == L_ROTATE_SHEAR) { 00110 L_WARNING("large angle; rotating by sampling", procName); 00111 type = L_ROTATE_SAMPLING; 00112 } 00113 00114 /* If 1 bpp and area map is requested, rotate by sampling */ 00115 d = pixGetDepth(pixs); 00116 if (d == 1 && type == L_ROTATE_AREA_MAP) { 00117 L_WARNING("1 bpp; rotating by sampling", procName); 00118 type = L_ROTATE_SAMPLING; 00119 } 00120 00121 /* Remove colormap if we're rotating by area mapping. */ 00122 cmap = pixGetColormap(pixs); 00123 if (cmap && type == L_ROTATE_AREA_MAP) 00124 pixt1 = pixRemoveColormap(pixs, REMOVE_CMAP_BASED_ON_SRC); 00125 else 00126 pixt1 = pixClone(pixs); 00127 cmap = pixGetColormap(pixt1); 00128 00129 /* Otherwise, if there is a colormap and we're not embedding, 00130 * add white color if it doesn't exist. */ 00131 if (cmap && width == 0) { /* no embedding; generate @incolor */ 00132 if (incolor == L_BRING_IN_BLACK) 00133 pixcmapAddBlackOrWhite(cmap, 0, NULL); 00134 else /* L_BRING_IN_WHITE */ 00135 pixcmapAddBlackOrWhite(cmap, 1, NULL); 00136 } 00137 00138 /* Request to embed in a larger image; do if necessary */ 00139 pixt2 = pixEmbedForRotation(pixt1, angle, incolor, width, height); 00140 00141 /* Area mapping requires 8 or 32 bpp. 00142 * If 1 bpp, default to sampling. */ 00143 d = pixGetDepth(pixt2); 00144 if (type == L_ROTATE_AREA_MAP && d < 8) 00145 pixt3 = pixConvertTo8(pixt2, FALSE); 00146 else 00147 pixt3 = pixClone(pixt2); 00148 00149 /* Rotate by shear or area mapping */ 00150 pixGetDimensions(pixt3, &w, &h, &d); 00151 if (type == L_ROTATE_SHEAR) 00152 pixd = pixRotateShearCenter(pixt3, angle, incolor); 00153 else if (type == L_ROTATE_SAMPLING) 00154 pixd = pixRotateBySampling(pixt3, w / 2, h / 2, angle, incolor); 00155 else { /* rotate by area mapping */ 00156 fillval = 0; 00157 if (incolor == L_BRING_IN_WHITE) { 00158 if (d == 8) 00159 fillval = 255; 00160 else /* d == 32 */ 00161 fillval = 0xffffff00; 00162 } 00163 if (d == 8) 00164 pixd = pixRotateAMGray(pixt3, angle, fillval); 00165 else /* d == 32 */ 00166 pixd = pixRotateAMColor(pixt3, angle, fillval); 00167 } 00168 00169 pixDestroy(&pixt1); 00170 pixDestroy(&pixt2); 00171 pixDestroy(&pixt3); 00172 return pixd; 00173 } 00174 00175 00176 /*! 00177 * pixEmbedForRotation() 00178 * 00179 * Input: pixs (1, 2, 4, 8, 32 bpp rgb) 00180 * angle (radians; clockwise is positive) 00181 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00182 * width (original width; use 0 to avoid embedding) 00183 * height (original height; use 0 to avoid embedding) 00184 * Return: pixd, or null on error 00185 * 00186 * Notes: 00187 * (1) For very small rotations, just return a clone. 00188 * (2) Generate larger image to embed pixs if necessary, and 00189 * place in the center. 00190 * (3) Rotation brings either white or black pixels in 00191 * from outside the image. For colormapped images where 00192 * there is no white or black, a new color is added if 00193 * possible for these pixels; otherwise, either the 00194 * lightest or darkest color is used. In most cases, 00195 * the colormap will be removed prior to rotation. 00196 * (4) The dest is to be expanded so that no image pixels 00197 * are lost after rotation. Input of the original width 00198 * and height allows the expansion to stop at the maximum 00199 * required size, which is a square with side equal to 00200 * sqrt(w*w + h*h). 00201 * (5) For an arbitrary angle, the expansion can be found by 00202 * considering the UL and UR corners. As the image is 00203 * rotated, these move in an arc centered at the center of 00204 * the image. Normalize to a unit circle by dividing by half 00205 * the image diagonal. After a rotation of T radians, the UL 00206 * and UR corners are at points T radians along the unit 00207 * circle. Compute the x and y coordinates of both these 00208 * points and take the max of absolute values; these represent 00209 * the half width and half height of the containing rectangle. 00210 * The arithmetic is done using formulas for sin(a+b) and cos(a+b), 00211 * where b = T. For the UR corner, sin(a) = h/d and cos(a) = w/d. 00212 * For the UL corner, replace a by (pi - a), and you have 00213 * sin(pi - a) = h/d, cos(pi - a) = -w/d. The equations 00214 * given below follow directly. 00215 */ 00216 PIX * 00217 pixEmbedForRotation(PIX *pixs, 00218 l_float32 angle, 00219 l_int32 incolor, 00220 l_int32 width, 00221 l_int32 height) 00222 { 00223 l_int32 w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor; 00224 l_float64 sina, cosa, fw, fh; 00225 PIX *pixd; 00226 00227 PROCNAME("pixEmbedForRotation"); 00228 00229 if (!pixs) 00230 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00231 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00232 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00233 if (L_ABS(angle) < VERY_SMALL_ANGLE) 00234 return pixClone(pixs); 00235 00236 /* Test if big enough to hold any rotation of the original image */ 00237 pixGetDimensions(pixs, &w, &h, &d); 00238 maxside = (l_int32)(sqrt((l_float64)(width * width) + 00239 (l_float64)(height * height)) + 0.5); 00240 if (w >= maxside && h >= maxside) /* big enough */ 00241 return pixClone(pixs); 00242 00243 /* Find the new sizes required to hold the image after rotation */ 00244 cosa = cos(angle); 00245 sina = sin(angle); 00246 fw = (l_float64)w; 00247 fh = (l_float64)h; 00248 w1 = (l_int32)L_ABS(fw * cosa - fh * sina); 00249 w2 = (l_int32)L_ABS(-fw * cosa - fh * sina); 00250 h1 = (l_int32)L_ABS(fw * sina + fh * cosa); 00251 h2 = (l_int32)L_ABS(-fw * sina + fh * cosa); 00252 wnew = L_MAX(w1, w2); 00253 hnew = L_MAX(h1, h2); 00254 00255 if ((pixd = pixCreate(wnew, hnew, d)) == NULL) 00256 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00257 pixCopyResolution(pixd, pixs); 00258 pixCopyColormap(pixd, pixs); 00259 pixCopyText(pixd, pixs); 00260 xoff = (wnew - w) / 2; 00261 yoff = (hnew - h) / 2; 00262 00263 /* Set background to color to be rotated in */ 00264 setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE; 00265 pixSetBlackOrWhite(pixd, setcolor); 00266 00267 pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0); 00268 return pixd; 00269 } 00270 00271 00272 /*------------------------------------------------------------------* 00273 * General rotation by sampling * 00274 *------------------------------------------------------------------*/ 00275 /*! 00276 * pixRotateBySampling() 00277 * 00278 * Input: pixs (1, 2, 4, 8, 16, 32 bpp rgb; can be cmapped) 00279 * xcen (x value of center of rotation) 00280 * ycen (y value of center of rotation) 00281 * angle (radians; clockwise is positive) 00282 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00283 * Return: pixd, or null on error 00284 * 00285 * Notes: 00286 * (1) For very small rotations, just return a clone. 00287 * (2) Rotation brings either white or black pixels in 00288 * from outside the image. 00289 * (3) Colormaps are retained. 00290 */ 00291 PIX * 00292 pixRotateBySampling(PIX *pixs, 00293 l_int32 xcen, 00294 l_int32 ycen, 00295 l_float32 angle, 00296 l_int32 incolor) 00297 { 00298 l_int32 w, h, d, i, j, x, y, xdif, ydif, wm1, hm1, wpld; 00299 l_uint32 val; 00300 l_float32 sina, cosa; 00301 l_uint32 *datad, *lined; 00302 void **lines; 00303 PIX *pixd; 00304 00305 PROCNAME("pixRotateBySampling"); 00306 00307 if (!pixs) 00308 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00309 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00310 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00311 pixGetDimensions(pixs, &w, &h, &d); 00312 if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16 && d != 32) 00313 return (PIX *)ERROR_PTR("invalid depth", procName, NULL); 00314 00315 if (L_ABS(angle) < VERY_SMALL_ANGLE) 00316 return pixClone(pixs); 00317 00318 if ((pixd = pixCreateTemplateNoInit(pixs)) == NULL) 00319 return (PIX *)ERROR_PTR("pixd not made", procName, NULL); 00320 pixSetBlackOrWhite(pixd, incolor); 00321 00322 sina = sin(angle); 00323 cosa = cos(angle); 00324 datad = pixGetData(pixd); 00325 wpld = pixGetWpl(pixd); 00326 wm1 = w - 1; 00327 hm1 = h - 1; 00328 lines = pixGetLinePtrs(pixs, NULL); 00329 00330 /* Treat 1 bpp case specially */ 00331 if (d == 1) { 00332 for (i = 0; i < h; i++) { /* scan over pixd */ 00333 lined = datad + i * wpld; 00334 ydif = ycen - i; 00335 for (j = 0; j < w; j++) { 00336 xdif = xcen - j; 00337 x = xcen + (l_int32)(-xdif * cosa - ydif * sina); 00338 if (x < 0 || x > wm1) continue; 00339 y = ycen + (l_int32)(-ydif * cosa + xdif * sina); 00340 if (y < 0 || y > hm1) continue; 00341 if (incolor == L_BRING_IN_WHITE) { 00342 if (GET_DATA_BIT(lines[y], x)) 00343 SET_DATA_BIT(lined, j); 00344 } 00345 else { 00346 if (!GET_DATA_BIT(lines[y], x)) 00347 CLEAR_DATA_BIT(lined, j); 00348 } 00349 } 00350 } 00351 FREE(lines); 00352 return pixd; 00353 } 00354 00355 for (i = 0; i < h; i++) { /* scan over pixd */ 00356 lined = datad + i * wpld; 00357 ydif = ycen - i; 00358 for (j = 0; j < w; j++) { 00359 xdif = xcen - j; 00360 x = xcen + (l_int32)(-xdif * cosa - ydif * sina); 00361 if (x < 0 || x > wm1) continue; 00362 y = ycen + (l_int32)(-ydif * cosa + xdif * sina); 00363 if (y < 0 || y > hm1) continue; 00364 switch (d) 00365 { 00366 case 8: 00367 val = GET_DATA_BYTE(lines[y], x); 00368 SET_DATA_BYTE(lined, j, val); 00369 break; 00370 case 32: 00371 val = GET_DATA_FOUR_BYTES(lines[y], x); 00372 SET_DATA_FOUR_BYTES(lined, j, val); 00373 break; 00374 case 2: 00375 val = GET_DATA_DIBIT(lines[y], x); 00376 SET_DATA_DIBIT(lined, j, val); 00377 break; 00378 case 4: 00379 val = GET_DATA_QBIT(lines[y], x); 00380 SET_DATA_QBIT(lined, j, val); 00381 break; 00382 case 16: 00383 val = GET_DATA_TWO_BYTES(lines[y], x); 00384 SET_DATA_TWO_BYTES(lined, j, val); 00385 break; 00386 default: 00387 return (PIX *)ERROR_PTR("invalid depth", procName, NULL); 00388 } 00389 } 00390 } 00391 00392 FREE(lines); 00393 return pixd; 00394 } 00395 00396 00397 /*------------------------------------------------------------------* 00398 * Nice (slow) rotation of 1 bpp image * 00399 *------------------------------------------------------------------*/ 00400 /*! 00401 * pixRotateBinaryNice() 00402 * 00403 * Input: pixs (1 bpp) 00404 * angle (radians; clockwise is positive; about the center) 00405 * incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK) 00406 * Return: pixd, or null on error 00407 * 00408 * Notes: 00409 * (1) For very small rotations, just return a clone. 00410 * (2) This does a computationally expensive rotation of 1 bpp images. 00411 * The fastest rotators (using shears or subsampling) leave 00412 * visible horizontal and vertical shear lines across which 00413 * the image shear changes by one pixel. To ameliorate the 00414 * visual effect one can introduce random dithering. One 00415 * way to do this in a not-too-random fashion is given here. 00416 * We convert to 8 bpp, do a very small blur, rotate using 00417 * linear interpolation (same as area mapping), do a 00418 * small amount of sharpening to compensate for the initial 00419 * blur, and threshold back to binary. The shear lines 00420 * are magically removed. 00421 * (3) This operation is about 5x slower than rotation by sampling. 00422 */ 00423 PIX * 00424 pixRotateBinaryNice(PIX *pixs, 00425 l_float32 angle, 00426 l_int32 incolor) 00427 { 00428 PIX *pixt1, *pixt2, *pixt3, *pixt4, *pixd; 00429 00430 PROCNAME("pixRotateBinaryNice"); 00431 00432 if (!pixs || pixGetDepth(pixs) != 1) 00433 return (PIX *)ERROR_PTR("pixs undefined or not 1 bpp", procName, NULL); 00434 if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK) 00435 return (PIX *)ERROR_PTR("invalid incolor", procName, NULL); 00436 00437 pixt1 = pixConvertTo8(pixs, 0); 00438 pixt2 = pixBlockconv(pixt1, 1, 1); /* smallest blur allowed */ 00439 pixt3 = pixRotateAM(pixt2, angle, incolor); 00440 pixt4 = pixUnsharpMasking(pixt3, 1, 1.0); /* sharpen a bit */ 00441 pixd = pixThresholdToBinary(pixt4, 128); 00442 pixDestroy(&pixt1); 00443 pixDestroy(&pixt2); 00444 pixDestroy(&pixt3); 00445 pixDestroy(&pixt4); 00446 return pixd; 00447 } 00448 00449 00450 /*------------------------------------------------------------------* 00451 * Rotation including alpha (blend) component * 00452 *------------------------------------------------------------------*/ 00453 /*! 00454 * pixRotateWithAlpha() 00455 * 00456 * Input: pixs (32 bpp rgb) 00457 * angle (radians; clockwise is positive) 00458 * pixg (<optional> 8 bpp, can be null) 00459 * fract (between 0.0 and 1.0, with 0.0 fully transparent 00460 * and 1.0 fully opaque) 00461 * Return: pixd, or null on error 00462 * 00463 * Notes: 00464 * (1) The alpha channel is transformed separately from pixs, 00465 * and aligns with it, being fully transparent outside the 00466 * boundary of the transformed pixs. For pixels that are fully 00467 * transparent, a blending function like pixBlendWithGrayMask() 00468 * will give zero weight to corresponding pixels in pixs. 00469 * (2) Rotation is about the center of the image; for very small 00470 * rotations, just return a clone. The dest is automatically 00471 * expanded so that no image pixels are lost. 00472 * (3) Rotation is by area mapping. It doesn't matter what 00473 * color is brought in because the alpha channel will 00474 * be transparent (black) there. 00475 * (4) If pixg is NULL, it is generated as an alpha layer that is 00476 * partially opaque, using @fract. Otherwise, it is cropped 00477 * to pixs if required and @fract is ignored. The alpha 00478 * channel in pixs is never used. 00479 * (4) Colormaps are removed. 00480 * (5) The default setting for the border values in the alpha channel 00481 * is 0 (transparent) for the outermost ring of pixels and 00482 * (0.5 * fract * 255) for the second ring. When blended over 00483 * a second image, this 00484 * (a) shrinks the visible image to make a clean overlap edge 00485 * with an image below, and 00486 * (b) softens the edges by weakening the aliasing there. 00487 * Use l_setAlphaMaskBorder() to change these values. 00488 * 00489 * *** Warning: implicit assumption about RGB component ordering *** 00490 */ 00491 PIX * 00492 pixRotateWithAlpha(PIX *pixs, 00493 l_float32 angle, 00494 PIX *pixg, 00495 l_float32 fract) 00496 { 00497 l_int32 ws, hs, d; 00498 PIX *pixd, *pixg2, *pixgr; 00499 00500 PROCNAME("pixRotateWithAlpha"); 00501 00502 if (!pixs) 00503 return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); 00504 pixGetDimensions(pixs, &ws, &hs, &d); 00505 if (d != 32 && pixGetColormap(pixs) == NULL) 00506 return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); 00507 if (pixg && pixGetDepth(pixg) != 8) { 00508 L_WARNING("pixg not 8 bpp; using @fract transparent alpha", procName); 00509 pixg = NULL; 00510 } 00511 if (!pixg && (fract < 0.0 || fract > 1.0)) { 00512 L_WARNING("invalid fract; using 1.0 (fully transparent)", procName); 00513 fract = 1.0; 00514 } 00515 if (!pixg && fract == 0.0) 00516 L_WARNING("fully opaque alpha; image cannot be blended", procName); 00517 00518 /* Do separate rotation of rgb channels of pixs and of pixg */ 00519 pixd = pixRotate(pixs, angle, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, ws, hs); 00520 if (!pixg) { 00521 pixg2 = pixCreate(ws, hs, 8); 00522 if (fract == 1.0) 00523 pixSetAll(pixg2); 00524 else 00525 pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); 00526 } 00527 else 00528 pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); 00529 if (ws > 10 && hs > 10) { /* see note 8 */ 00530 pixSetBorderRingVal(pixg2, 1, 00531 (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); 00532 pixSetBorderRingVal(pixg2, 2, 00533 (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); 00534 } 00535 pixgr = pixRotate(pixg2, angle, L_ROTATE_AREA_MAP, 00536 L_BRING_IN_BLACK, ws, hs); 00537 pixSetRGBComponent(pixd, pixgr, L_ALPHA_CHANNEL); 00538 00539 pixDestroy(&pixg2); 00540 pixDestroy(&pixgr); 00541 return pixd; 00542 } 00543 00544 00545 /*! 00546 * pixRotateGammaXform() 00547 * 00548 * Input: pixs (32 bpp rgb) 00549 * gamma (gamma correction; must be > 0.0) 00550 * angle (radians; clockwise is positive) 00551 * fract (between 0.0 and 1.0, with 1.0 fully transparent) 00552 * Return: pixd, or null on error 00553 * 00554 * Notes: 00555 * (1) This wraps a gamma/inverse-gamma photometric transform 00556 * around pixRotateWithAlpha(). 00557 * (2) For usage, see notes in pixRotateWithAlpha() and 00558 * pixGammaTRCWithAlpha(). 00559 * (3) The basic idea of a gamma/inverse-gamma transform is 00560 * to remove gamma correction before rotating and restore 00561 * it afterward. The effects can be subtle, but important for 00562 * some applications. For example, using gamma > 1.0 will 00563 * cause the dark areas to become somewhat lighter and slightly 00564 * reduce aliasing effects when blending using the alpha channel. 00565 */ 00566 PIX * 00567 pixRotateGammaXform(PIX *pixs, 00568 l_float32 gamma, 00569 l_float32 angle, 00570 l_float32 fract) 00571 { 00572 PIX *pixg, *pixd; 00573 00574 PROCNAME("pixRotateGammaXform"); 00575 00576 if (!pixs || (pixGetDepth(pixs) != 32)) 00577 return (PIX *)ERROR_PTR("pixs undefined or not 32 bpp", procName, NULL); 00578 if (fract == 0.0) 00579 L_WARNING("fully opaque alpha; image cannot be blended", procName); 00580 if (gamma <= 0.0) { 00581 L_WARNING("gamma must be > 0.0; setting to 1.0", procName); 00582 gamma = 1.0; 00583 } 00584 00585 pixg = pixGammaTRCWithAlpha(NULL, pixs, 1.0 / gamma, 0, 255); 00586 pixd = pixRotateWithAlpha(pixg, angle, NULL, fract); 00587 pixGammaTRCWithAlpha(pixd, pixd, gamma, 0, 255); 00588 pixDestroy(&pixg); 00589 return pixd; 00590 } 00591