Leptonica 1.68
C Image Processing Library

rotate.c

Go to the documentation of this file.
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 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines