src/rotate.c
PIX *
pixEmbedForRotation(PIX       *pixs,
                    l_float32  angle,
                    l_int32    incolor,
                    l_int32    width,
                    l_int32    height)
{
l_int32    w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
l_float64  sina, cosa, fw, fh;
PIX       *pixd;

    PROCNAME("pixEmbedForRotation");

    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
        return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
    if (L_ABS(angle) < VERY_SMALL_ANGLE)
        return pixClone(pixs);

        /* Test if big enough to hold any rotation of the original image */
    pixGetDimensions(pixs, &w, &h, &d);
    maxside = (l_int32)(sqrt((l_float64)(width * width) +
                             (l_float64)(height * height)) + 0.5);
    if (w >= maxside && h >= maxside)  /* big enough */
        return pixClone(pixs);
    
        /* Find the new sizes required to hold the image after rotation */
    cosa = cos(angle);
    sina = sin(angle);
    fw = (l_float64)w;
    fh = (l_float64)h;
    w1 = (l_int32)L_ABS(fw * cosa - fh * sina);
    w2 = (l_int32)L_ABS(-fw * cosa - fh * sina);
    h1 = (l_int32)L_ABS(fw * sina + fh * cosa);
    h2 = (l_int32)L_ABS(-fw * sina + fh * cosa);
    wnew = L_MAX(w1, w2);
    hnew = L_MAX(h1, h2);

    if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixCopyColormap(pixd, pixs);
    pixCopyText(pixd, pixs);
    xoff = (wnew - w) / 2;
    yoff = (hnew - h) / 2;

        /* Set background to color to be rotated in */
    setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE;
    pixSetBlackOrWhite(pixd, setcolor);

    pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
    return pixd;
}
/*!
 *  pixEmbedForRotation()
 *
 *      Input:  pixs (1, 2, 4, 8, 32 bpp rgb)
 *              angle (radians; clockwise is positive)
 *              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
 *              width (original width; use 0 to avoid embedding)
 *              height (original height; use 0 to avoid embedding)
 *      Return: pixd, or null on error
 *
 *  Notes:
 *      (1) For very small rotations, just return a clone.
 *      (2) Generate larger image to embed pixs if necessary, and
 *          place in the center.
 *      (3) Rotation brings either white or black pixels in
 *          from outside the image.  For colormapped images where
 *          there is no white or black, a new color is added if
 *          possible for these pixels; otherwise, either the
 *          lightest or darkest color is used.  In most cases,
 *          the colormap will be removed prior to rotation.
 *      (4) The dest is to be expanded so that no image pixels
 *          are lost after rotation.  Input of the original width
 *          and height allows the expansion to stop at the maximum
 *          required size, which is a square with side equal to
 *          sqrt(w*w + h*h).
 *      (5) For an arbitrary angle, the expansion can be found by
 *          considering the UL and UR corners.  As the image is
 *          rotated, these move in an arc centered at the center of
 *          the image.  Normalize to a unit circle by dividing by half
 *          the image diagonal.  After a rotation of T radians, the UL
 *          and UR corners are at points T radians along the unit
 *          circle.  Compute the x and y coordinates of both these
 *          points and take the max of absolute values; these represent
 *          the half width and half height of the containing rectangle.
 *          The arithmetic is done using formulas for sin(a+b) and cos(a+b),
 *          where b = T.  For the UR corner, sin(a) = h/d and cos(a) = w/d.
 *          For the UL corner, replace a by (pi - a), and you have
 *          sin(pi - a) = h/d, cos(pi - a) = -w/d.  The equations
 *          given below follow directly.
 */
pixEmbedForRotation()
Input:  pixs (1, 2, 4, 8, 32 bpp rgb)
angle (radians; clockwise is positive)
incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
width (original width; use 0 to avoid embedding)
height (original height; use 0 to avoid embedding)
Return: pixd, or null on error

(1) For very small rotations, just return a clone.
(2) Generate larger image to embed pixs if necessary, and
place in the center.
(3) Rotation brings either white or black pixels in
from outside the image.  For colormapped images where
there is no white or black, a new color is added if
possible for these pixels; otherwise, either the
lightest or darkest color is used.  In most cases,
the colormap will be removed prior to rotation.
(4) The dest is to be expanded so that no image pixels
are lost after rotation.  Input of the original width
and height allows the expansion to stop at the maximum
required size, which is a square with side equal to
sqrt(w*w + h*h).
(5) For an arbitrary angle, the expansion can be found by
considering the UL and UR corners.  As the image is
rotated, these move in an arc centered at the center of
the image.  Normalize to a unit circle by dividing by half
the image diagonal.  After a rotation of T radians, the UL
and UR corners are at points T radians along the unit
circle.  Compute the x and y coordinates of both these
points and take the max of absolute values; these represent
the half width and half height of the containing rectangle.
The arithmetic is done using formulas for sin(a+b) and cos(a+b),
where b = T.  For the UR corner, sin(a) = h/d and cos(a) = w/d.
For the UL corner, replace a by (pi - a), and you have
sin(pi - a) = h/d, cos(pi - a) = -w/d.  The equations
given below follow directly.
pixEmbedForRotation () Input : pixs ( 1 2 4 8 32 bpp rgb ) angle ( radians ; clockwise is positive ) incolor ( L_BRING_IN_WHITE L_BRING_IN_BLACK ) width ( original width ; use 0 to avoid embedding ) height ( original height ; use 0 to avoid embedding ) Return : pixd or null on error ( 1 ) For very small rotations just return a clone . ( 2 ) Generate larger image to embed pixs if necessary and place in the center . ( 3 ) Rotation brings either white or black pixels in from outside the image . For colormapped images where there is no white or black a new color is added if possible for these pixels ; otherwise either the lightest or darkest color is used . In most cases the colormap will be removed prior to rotation . ( 4 ) The dest is to be expanded so that no image pixels are lost after rotation . Input of the original width and height allows the expansion to stop at the maximum required size which is a square with side equal to sqrt ( w * w + h * h ) . ( 5 ) For an arbitrary angle the expansion can be found by considering the UL and UR corners . As the image is rotated these move in an arc centered at the center of the image . Normalize to a unit circle by dividing by half the image diagonal . After a rotation of T radians the UL and UR corners are at points T radians along the unit circle . Compute the x and y coordinates of both these points and take the max of absolute values ; these represent the half width and half height of the containing rectangle . The arithmetic is done using formulas for sin ( a + b ) and cos ( a + b ) where b = T . For the UR corner sin ( a ) = h / d and cos ( a ) = w / d . For the UL corner replace a by ( pi - a ) and you have sin ( pi - a ) = h / d cos ( pi - a ) = - w / d . The equations given below follow directly .
PIX *
pixEmbedForRotation(PIX       *pixs,
                    l_float32  angle,
                    l_int32    incolor,
                    l_int32    width,
                    l_int32    height)
{
l_int32    w, h, d, w1, h1, w2, h2, maxside, wnew, hnew, xoff, yoff, setcolor;
l_float64  sina, cosa, fw, fh;
PIX       *pixd;
    PROCNAME("pixEmbedForRotation");
    if (!pixs)
        return (PIX *)ERROR_PTR("pixs not defined", procName, NULL);
    if (incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK)
        return (PIX *)ERROR_PTR("invalid incolor", procName, NULL);
    if (L_ABS(angle) < VERY_SMALL_ANGLE)
        return pixClone(pixs);
    pixGetDimensions(pixs, &w, &h, &d);
    maxside = (l_int32)(sqrt((l_float64)(width * width) +
                             (l_float64)(height * height)) + 0.5);
    if (w >= maxside && h >= maxside)  
        return pixClone(pixs);
    cosa = cos(angle);
    sina = sin(angle);
    fw = (l_float64)w;
    fh = (l_float64)h;
    w1 = (l_int32)L_ABS(fw * cosa - fh * sina);
    w2 = (l_int32)L_ABS(-fw * cosa - fh * sina);
    h1 = (l_int32)L_ABS(fw * sina + fh * cosa);
    h2 = (l_int32)L_ABS(-fw * sina + fh * cosa);
    wnew = L_MAX(w1, w2);
    hnew = L_MAX(h1, h2);
    if ((pixd = pixCreate(wnew, hnew, d)) == NULL)
        return (PIX *)ERROR_PTR("pixd not made", procName, NULL);
    pixCopyResolution(pixd, pixs);
    pixCopyColormap(pixd, pixs);
    pixCopyText(pixd, pixs);
    xoff = (wnew - w) / 2;
    yoff = (hnew - h) / 2;
    setcolor = (incolor == L_BRING_IN_BLACK) ? L_SET_BLACK : L_SET_WHITE;
    pixSetBlackOrWhite(pixd, setcolor);
    pixRasterop(pixd, xoff, yoff, w, h, PIX_SRC, pixs, 0, 0);
    return pixd;
}
PIX * pixEmbedForRotation ( PIX * pixs , l_float32 angle , l_int32 incolor , l_int32 width , l_int32 height ) { l_int32 w , h , d , w1 , h1 , w2 , h2 , maxside , wnew , hnew , xoff , yoff , setcolor ; l_float64 sina , cosa , fw , fh ; PIX * pixd ; PROCNAME ( " " ) ; if ( ! pixs ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( incolor != L_BRING_IN_WHITE && incolor != L_BRING_IN_BLACK ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; if ( L_ABS ( angle ) < VERY_SMALL_ANGLE ) return pixClone ( pixs ) ; pixGetDimensions ( pixs , & w , & h , & d ) ; maxside = ( l_int32 ) ( sqrt ( ( l_float64 ) ( width * width ) + ( l_float64 ) ( height * height ) ) + 0.5 ) ; if ( w >= maxside && h >= maxside ) return pixClone ( pixs ) ; cosa = cos ( angle ) ; sina = sin ( angle ) ; fw = ( l_float64 ) w ; fh = ( l_float64 ) h ; w1 = ( l_int32 ) L_ABS ( fw * cosa - fh * sina ) ; w2 = ( l_int32 ) L_ABS ( - fw * cosa - fh * sina ) ; h1 = ( l_int32 ) L_ABS ( fw * sina + fh * cosa ) ; h2 = ( l_int32 ) L_ABS ( - fw * sina + fh * cosa ) ; wnew = L_MAX ( w1 , w2 ) ; hnew = L_MAX ( h1 , h2 ) ; if ( ( pixd = pixCreate ( wnew , hnew , d ) ) == NULL ) return ( PIX * ) ERROR_PTR ( " " , procName , NULL ) ; pixCopyResolution ( pixd , pixs ) ; pixCopyColormap ( pixd , pixs ) ; pixCopyText ( pixd , pixs ) ; xoff = ( wnew - w ) / 2 ; yoff = ( hnew - h ) / 2 ; setcolor = ( incolor == L_BRING_IN_BLACK ) ? L_SET_BLACK : L_SET_WHITE ; pixSetBlackOrWhite ( pixd , setcolor ) ; pixRasterop ( pixd , xoff , yoff , w , h , PIX_SRC , pixs , 0 , 0 ) ; return pixd ; }
pixEmbedForRotation()
      Input:  pixs (1, 2, 4, 8, 32 bpp rgb)
              angle (radians; clockwise is positive)
              incolor (L_BRING_IN_WHITE, L_BRING_IN_BLACK)
              width (original width; use 0 to avoid embedding)
              height (original height; use 0 to avoid embedding)
      Return: pixd, or null on error
pixEmbedForRotation () Input : pixs ( 1 2 4 8 32 bpp rgb ) angle ( radians ; clockwise is positive ) incolor ( L_BRING_IN_WHITE L_BRING_IN_BLACK ) width ( original width ; use 0 to avoid embedding ) height ( original height ; use 0 to avoid embedding ) Return : pixd or null on error
/* Test if big enough to hold any rotation of the original image */ /* big enough */ /* Find the new sizes required to hold the image after rotation */ /* Set background to color to be rotated in */