diff options
-rw-r--r-- | html/en/history.html | 2 | ||||
-rw-r--r-- | include/im_process_pon.h | 14 | ||||
-rw-r--r-- | src/im_process.def | 1 | ||||
-rw-r--r-- | src/lua5/im_process.lua | 1 | ||||
-rw-r--r-- | src/lua5/imlua_process.c | 19 | ||||
-rw-r--r-- | src/process/im_arithmetic_bin.cpp | 144 |
6 files changed, 179 insertions, 2 deletions
diff --git a/html/en/history.html b/html/en/history.html index 0c6210a..78399e5 100644 --- a/html/en/history.html +++ b/html/en/history.html @@ -16,6 +16,8 @@ <ul dir="ltr"> <li><span style="color: #0000FF">New:</span> function <strong> imImageCopyPlane</strong>.</li> + <li><span style="color: #0000FF">New:</span> function <strong> + imProcessCompose</strong>.</li> <li dir="ltr"> <span style="color: #008000">Changed:</span> libTIFF updated to version 3.9.2.</li> diff --git a/include/im_process_pon.h b/include/im_process_pon.h index 2420360..635448a 100644 --- a/include/im_process_pon.h +++ b/include/im_process_pon.h @@ -126,6 +126,20 @@ void imProcessBlendConst(const imImage* src_image1, const imImage* src_image2, i * \ingroup arithm */ void imProcessBlend(const imImage* src_image1, const imImage* src_image2, const imImage* alpha_image, imImage* dst_image); +/** Compose two images that have an alpha channel using the OVER operator. \n + * Can be done in place, images must match size and type. \n + * Integer alpha values must be: +\verbatim +0 - 255 IM_BYTE +0 - 65535 IM_USHORT +0 - 2147483647 IM_INT +\endverbatim + * that will be normalized to 0 - 1. + * \verbatim im.ProcessCompose(src_image1: imImage, src_image2: imImage, dst_image: imImage) [in Lua 5] \endverbatim + * \verbatim im.ProcessComposeNew(image1: imImage, image2: imImage) -> new_image: imImage [in Lua 5] \endverbatim + * \ingroup arithm */ +void imProcessCompose(const imImage* src_image1, const imImage* src_image2, imImage* dst_image); + /** Split a complex image into two images with real and imaginary parts \n * or magnitude and phase parts (polar). \n * Source image must be IM_CFLOAT, destiny images must be IM_FLOAT. diff --git a/src/im_process.def b/src/im_process.def index 07bba47..7e637e9 100644 --- a/src/im_process.def +++ b/src/im_process.def @@ -73,6 +73,7 @@ EXPORTS imProcessBitwiseOp imProcessBlendConst imProcessBlend + imProcessCompose imProcessCalcRotateSize imProcessDifusionErrThreshold imProcessDirectConv diff --git a/src/lua5/im_process.lua b/src/lua5/im_process.lua index d74c6b1..8d866a4 100644 --- a/src/lua5/im_process.lua +++ b/src/lua5/im_process.lua @@ -255,6 +255,7 @@ end TwoSourcesOneDest("ProcessBlendConst") ThreeSourcesOneDest("ProcessBlend") +TwoSourcesOneDest("ProcessCompose") OneSourceTwoDests("ProcessSplitComplex") TwoSourcesOneDest("ProcessMergeComplex", nil, nil, nil, im.CFLOAT) diff --git a/src/lua5/imlua_process.c b/src/lua5/imlua_process.c index 198d0e5..299155a 100644 --- a/src/lua5/imlua_process.c +++ b/src/lua5/imlua_process.c @@ -2,7 +2,7 @@ * \brief IM Lua 5 Binding * * See Copyright Notice in im_lib.h - * $Id: imlua_process.c,v 1.9 2010/01/06 20:16:30 scuri Exp $ + * $Id: imlua_process.c,v 1.10 2010/01/08 03:49:05 scuri Exp $ */ #include <memory.h> @@ -1703,6 +1703,22 @@ static int imluaProcessBlend (lua_State *L) } /*****************************************************************************\ + im.ProcessCompose +\*****************************************************************************/ +static int imluaProcessCompose(lua_State *L) +{ + imImage *src_image1 = imlua_checkimage(L, 1); + imImage *src_image2 = imlua_checkimage(L, 2); + imImage *dst_image = imlua_checkimage(L, 4); + + imlua_match(L, src_image1, src_image2); + imlua_match(L, src_image1, dst_image); + + imProcessCompose(src_image1, src_image2, dst_image); + return 0; +} + +/*****************************************************************************\ im.ProcessSplitComplex \*****************************************************************************/ static int imluaProcessSplitComplex (lua_State *L) @@ -2990,6 +3006,7 @@ static const luaL_reg improcess_lib[] = { {"ProcessArithmeticConstOp", imluaProcessArithmeticConstOp}, {"ProcessBlendConst", imluaProcessBlendConst}, {"ProcessBlend", imluaProcessBlend}, + {"ProcessCompose", imluaProcessCompose}, {"ProcessSplitComplex", imluaProcessSplitComplex}, {"ProcessMergeComplex", imluaProcessMergeComplex}, {"ProcessMultipleMean", imluaProcessMultipleMean}, diff --git a/src/process/im_arithmetic_bin.cpp b/src/process/im_arithmetic_bin.cpp index 494b6c0..575e650 100644 --- a/src/process/im_arithmetic_bin.cpp +++ b/src/process/im_arithmetic_bin.cpp @@ -2,7 +2,7 @@ * \brief Binary Arithmetic Operations * * See Copyright Notice in im_lib.h - * $Id: im_arithmetic_bin.cpp,v 1.2 2009/10/01 02:56:58 scuri Exp $ + * $Id: im_arithmetic_bin.cpp,v 1.3 2010/01/08 03:49:05 scuri Exp $ */ @@ -262,6 +262,148 @@ void imProcessBlend(const imImage* src_image1, const imImage* src_image2, const } } +#define COMPOSE_OVER(_SRC, _SRC_ALPHA, _DST, _TMP_MULTI, _TMP_ALPHA) (T)(((_SRC_ALPHA)*(_SRC) + (_TMP_MULTI)*(_DST)) / (_TMP_ALPHA)) +#define ALPHA_BLEND(_src,_dst,_alpha) (T)(((_src) * (_alpha) + (_dst) * (max - (_alpha))) / max) + +template <class T, class TA> +static inline T compose_op(const T& v1, const T& v2, const T& alpha1, const T& alpha2, const TA& max) +{ + if (alpha1 != max) /* some transparency */ + { + if (alpha1 != 0) /* source not full transparent */ + { + if (alpha2 == 0) /* destiny full transparent */ + { + return v1; + } + else if (alpha2 == max) /* destiny opaque */ + { + return ALPHA_BLEND(v1, v2, alpha1); + } + else /* (0<alpha2<max && 0<alpha1<max) destiny and source are semi-transparent */ + { + /* Closed Compositing SRC over DST (see smith95a.pdf) */ + /* Colors NOT Premultiplied by Alpha */ + /* DST = SRC * SRC_ALPHA + DST * DST_ALPHA * (1 - SRC_ALPHA) */ + /* DST_ALPHA = SRC_ALPHA + DST_ALPHA * (1 - SRC_ALPHA) */ + /* DST /= DST_ALPHA */ + TA _tmp_multi = alpha2 * (max - alpha1); + TA _tmp_src_alpha = alpha1*max; + TA _tmp_alpha = _tmp_src_alpha + _tmp_multi; + return COMPOSE_OVER(v1, _tmp_src_alpha, v2, _tmp_multi, _tmp_alpha); + } + } + else /* (alpha1 == 0) source full transparent */ + { + return v2; + } + } + else /* (alpha1 == max) source has no alpha = opaque */ + { + return v1; + } +} + +template <class T, class TA> +static inline T compose_alpha_op(const T& alpha1, const T& alpha2, const TA& max) +{ + if (alpha1 != max) /* some transparency */ + { + if (alpha1 != 0) /* source not full transparent */ + { + if (alpha2 == 0) /* destiny full transparent */ + { + return alpha1; + } + else if (alpha2 == max) /* destiny opaque */ + { + /* alpha2 is not changed */ + return alpha2; + } + else /* (0<alpha2<max && 0<alpha1<max) destiny and source are semi-transparent */ + { + /* Closed Compositing SRC over DST (see smith95a.pdf) */ + /* Colors NOT Premultiplied by Alpha */ + /* DST = SRC * SRC_ALPHA + DST * DST_ALPHA * (1 - SRC_ALPHA) */ + /* DST_ALPHA = SRC_ALPHA + DST_ALPHA * (1 - SRC_ALPHA) */ + /* DST /= DST_ALPHA */ + TA _tmp_multi = alpha2 * (max - alpha1); + TA _tmp_src_alpha = alpha1*max; + TA _tmp_alpha = _tmp_src_alpha + _tmp_multi; + return (T)(_tmp_alpha / max); + } + } + else /* (alpha1 == 0) source full transparent */ + { + /* alpha2 is not changed */ + return alpha2; + } + } + else /* (alpha1 == max) source has no alpha = opaque */ + { + return (unsigned char)max; /* set destiny as opaque */ + } +} + +template <class T, class TA> +static void DoCompose(T *map1, T *map2, T *alpha1, T *alpha2, T *map, int count, TA max) +{ + for (int i = 0; i < count; i++) + map[i] = compose_op(map1[i], map2[i], alpha1[i], alpha2[i], max); +} + +template <class T, class TA> +static void DoComposeAlpha(T *alpha1, T *alpha2, T *dst_alpha, int count, TA max) +{ + for (int i = 0; i < count; i++) + dst_alpha[i] = compose_alpha_op(alpha1[i], alpha2[i], max); +} + +void imProcessCompose(const imImage* src_image1, const imImage* src_image2, imImage* dst_image) +{ + int count = src_image1->count, + src_alpha = src_image1->depth; + + if (!src_image1->has_alpha || !src_image2->has_alpha || !dst_image->has_alpha) + return; + + for (int i = 0; i < src_image1->depth; i++) + { + switch(src_image1->data_type) + { + case IM_BYTE: + DoCompose((imbyte*)src_image1->data[i], (imbyte*)src_image2->data[i], (imbyte*)src_image1->data[src_alpha], (imbyte*)src_image2->data[src_alpha], (imbyte*)dst_image->data[i], count, (int)255); + break; + case IM_USHORT: + DoCompose((imushort*)src_image1->data[i], (imushort*)src_image2->data[i], (imushort*)src_image1->data[src_alpha], (imushort*)src_image2->data[src_alpha], (imushort*)dst_image->data[i], count, (int)65535); + break; + case IM_INT: + DoCompose((int*)src_image1->data[i], (int*)src_image2->data[i], (int*)src_image1->data[src_alpha], (int*)src_image2->data[src_alpha], (int*)dst_image->data[i], count, (int)2147483647); + break; + case IM_FLOAT: + DoCompose((float*)src_image1->data[i], (float*)src_image2->data[i], (float*)src_image1->data[src_alpha], (float*)src_image2->data[src_alpha], (float*)dst_image->data[i], count, 1.0f); + break; + } + } + + /* one more for the alpha channel */ + switch(src_image1->data_type) + { + case IM_BYTE: + DoComposeAlpha((imbyte*)src_image1->data[src_alpha], (imbyte*)src_image2->data[src_alpha], (imbyte*)dst_image->data[src_alpha], count, (int)255); + break; + case IM_USHORT: + DoComposeAlpha((imushort*)src_image1->data[src_alpha], (imushort*)src_image2->data[src_alpha], (imushort*)dst_image->data[src_alpha], count, (int)65535); + break; + case IM_INT: + DoComposeAlpha((int*)src_image1->data[src_alpha], (int*)src_image2->data[src_alpha], (int*)dst_image->data[src_alpha], count, (int)2147483647); + break; + case IM_FLOAT: + DoComposeAlpha((float*)src_image1->data[src_alpha], (float*)src_image2->data[src_alpha], (float*)dst_image->data[src_alpha], count, 1.0f); + break; + } +} + static void DoBinaryConstOpCpxReal(imcfloat *map1, float value, imcfloat *map, int count, int op) { int i; |