/*! * pixProjectivePtaWithAlpha() * * Input: pixs (32 bpp rgb) * ptad (4 pts of final coordinate space) * ptas (4 pts of initial coordinate space) * pixg (<optional> 8 bpp, for alpha channel, can be null) * fract (between 0.0 and 1.0, with 0.0 fully transparent * and 1.0 fully opaque) * border (of pixels added to capture transformed source pixels) * Return: pixd, or null on error * * Notes: * (1) The alpha channel is transformed separately from pixs, * and aligns with it, being fully transparent outside the * boundary of the transformed pixs. For pixels that are fully * transparent, a blending function like pixBlendWithGrayMask() * will give zero weight to corresponding pixels in pixs. * (2) If pixg is NULL, it is generated as an alpha layer that is * partially opaque, using @fract. Otherwise, it is cropped * to pixs if required and @fract is ignored. The alpha channel * in pixs is never used. * (3) Colormaps are removed. * (4) When pixs is transformed, it doesn't matter what color is brought * in because the alpha channel will be transparent (0) there. * (5) To avoid losing source pixels in the destination, it may be * necessary to add a border to the source pix before doing * the projective transformation. This can be any non-negative * number. * (6) The input @ptad and @ptas are in a coordinate space before * the border is added. Internally, we compensate for this * before doing the projective transform on the image after * the border is added. * (7) The default setting for the border values in the alpha channel * is 0 (transparent) for the outermost ring of pixels and * (0.5 * fract * 255) for the second ring. When blended over * a second image, this * (a) shrinks the visible image to make a clean overlap edge * with an image below, and * (b) softens the edges by weakening the aliasing there. * Use l_setAlphaMaskBorder() to change these values. */ PIX * pixProjectivePtaWithAlpha(PIX *pixs, PTA *ptad, PTA *ptas, PIX *pixg, l_float32 fract, l_int32 border) { l_int32 ws, hs, d; PIX *pixd, *pixb1, *pixb2, *pixg2, *pixga; PTA *ptad2, *ptas2; PROCNAME("pixProjectivePtaWithAlpha"); if (!pixs) return (PIX *)ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &ws, &hs, &d); if (d != 32 && pixGetColormap(pixs) == NULL) return (PIX *)ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); if (pixg && pixGetDepth(pixg) != 8) { L_WARNING("pixg not 8 bpp; using @fract transparent alpha", procName); pixg = NULL; } if (!pixg && (fract < 0.0 || fract > 1.0)) { L_WARNING("invalid fract; using 1.0 (fully transparent)", procName); fract = 1.0; } if (!pixg && fract == 0.0) L_WARNING("fully opaque alpha; image will not be blended", procName); if (!ptad) return (PIX *)ERROR_PTR("ptad not defined", procName, NULL); if (!ptas) return (PIX *)ERROR_PTR("ptas not defined", procName, NULL); /* Add border; the color doesn't matter */ pixb1 = pixAddBorder(pixs, border, 0); /* Transform the ptr arrays to work on the bordered image */ ptad2 = ptaTransform(ptad, border, border, 1.0, 1.0); ptas2 = ptaTransform(ptas, border, border, 1.0, 1.0); /* Do separate projective transform of rgb channels of pixs * and of pixg */ pixd = pixProjectivePtaColor(pixb1, ptad2, ptas2, 0); if (!pixg) { pixg2 = pixCreate(ws, hs, 8); if (fract == 1.0) pixSetAll(pixg2); else pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); } else pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); if (ws > 10 && hs > 10) { /* see note 7 */ pixSetBorderRingVal(pixg2, 1, (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); pixSetBorderRingVal(pixg2, 2, (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); } pixb2 = pixAddBorder(pixg2, border, 0); /* must be black border */ pixga = pixProjectivePtaGray(pixb2, ptad2, ptas2, 0); pixSetRGBComponent(pixd, pixga, L_ALPHA_CHANNEL); pixDestroy(&pixg2); pixDestroy(&pixb1); pixDestroy(&pixb2); pixDestroy(&pixga); ptaDestroy(&ptad2); ptaDestroy(&ptas2); return pixd; }
/*! * pixRotateWithAlpha() * * Input: pixs (32 bpp rgb or cmapped) * angle (radians; clockwise is positive) * pixg (<optional> 8 bpp, can be null) * fract (between 0.0 and 1.0, with 0.0 fully transparent * and 1.0 fully opaque) * Return: pixd (32 bpp rgba), or null on error * * Notes: * (1) The alpha channel is transformed separately from pixs, * and aligns with it, being fully transparent outside the * boundary of the transformed pixs. For pixels that are fully * transparent, a blending function like pixBlendWithGrayMask() * will give zero weight to corresponding pixels in pixs. * (2) Rotation is about the center of the image; for very small * rotations, just return a clone. The dest is automatically * expanded so that no image pixels are lost. * (3) Rotation is by area mapping. It doesn't matter what * color is brought in because the alpha channel will * be transparent (black) there. * (4) If pixg is NULL, it is generated as an alpha layer that is * partially opaque, using @fract. Otherwise, it is cropped * to pixs if required and @fract is ignored. The alpha * channel in pixs is never used. * (4) Colormaps are removed to 32 bpp. * (5) The default setting for the border values in the alpha channel * is 0 (transparent) for the outermost ring of pixels and * (0.5 * fract * 255) for the second ring. When blended over * a second image, this * (a) shrinks the visible image to make a clean overlap edge * with an image below, and * (b) softens the edges by weakening the aliasing there. * Use l_setAlphaMaskBorder() to change these values. * (6) A subtle use of gamma correction is to remove gamma correction * before rotation and restore it afterwards. This is done * by sandwiching this function between a gamma/inverse-gamma * photometric transform: * pixt = pixGammaTRCWithAlpha(NULL, pixs, 1.0 / gamma, 0, 255); * pixd = pixRotateWithAlpha(pixt, angle, NULL, fract); * pixGammaTRCWithAlpha(pixd, pixd, gamma, 0, 255); * pixDestroy(&pixt); * This has the side-effect of producing artifacts in the very * dark regions. * * *** Warning: implicit assumption about RGB component ordering *** */ PIX * pixRotateWithAlpha(PIX *pixs, l_float32 angle, PIX *pixg, l_float32 fract) { l_int32 ws, hs, d, spp; PIX *pixd, *pix32, *pixg2, *pixgr; PROCNAME("pixRotateWithAlpha"); if (!pixs) return (PIX *) ERROR_PTR("pixs not defined", procName, NULL); pixGetDimensions(pixs, &ws, &hs, &d); if (d != 32 && pixGetColormap(pixs) == NULL) return (PIX *) ERROR_PTR("pixs not cmapped or 32 bpp", procName, NULL); if (pixg && pixGetDepth(pixg) != 8) { L_WARNING("pixg not 8 bpp; using @fract transparent alpha\n", procName); pixg = NULL; } if (!pixg && (fract < 0.0 || fract > 1.0)) { L_WARNING("invalid fract; using fully opaque\n", procName); fract = 1.0; } if (!pixg && fract == 0.0) L_WARNING("transparent alpha; image will not be blended\n", procName); /* Make sure input to rotation is 32 bpp rgb, and rotate it */ if (d != 32) pix32 = pixConvertTo32(pixs); else pix32 = pixClone(pixs); spp = pixGetSpp(pix32); pixSetSpp(pix32, 3); /* ignore the alpha channel for the rotation */ pixd = pixRotate(pix32, angle, L_ROTATE_AREA_MAP, L_BRING_IN_WHITE, ws, hs); pixSetSpp(pix32, spp); /* restore initial value in case it's a clone */ pixDestroy(&pix32); /* Set up alpha layer with a fading border and rotate it */ if (!pixg) { pixg2 = pixCreate(ws, hs, 8); if (fract == 1.0) pixSetAll(pixg2); else if (fract > 0.0) pixSetAllArbitrary(pixg2, (l_int32)(255.0 * fract)); } else { pixg2 = pixResizeToMatch(pixg, NULL, ws, hs); } if (ws > 10 && hs > 10) { /* see note 8 */ pixSetBorderRingVal(pixg2, 1, (l_int32)(255.0 * fract * AlphaMaskBorderVals[0])); pixSetBorderRingVal(pixg2, 2, (l_int32)(255.0 * fract * AlphaMaskBorderVals[1])); } pixgr = pixRotate(pixg2, angle, L_ROTATE_AREA_MAP, L_BRING_IN_BLACK, ws, hs); /* Combine into a 4 spp result */ pixSetRGBComponent(pixd, pixgr, L_ALPHA_CHANNEL); pixDestroy(&pixg2); pixDestroy(&pixgr); return pixd; }