summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--html/en/history.html2
-rw-r--r--include/im_process_pon.h14
-rw-r--r--src/im_process.def1
-rw-r--r--src/lua5/im_process.lua1
-rw-r--r--src/lua5/imlua_process.c19
-rw-r--r--src/process/im_arithmetic_bin.cpp144
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;