1 /*
   2  * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 
  27 /*
  28  * FUNCTION
  29  *      mlib_ImageConvMxN - image convolution with edge condition
  30  *
  31  * SYNOPSIS
  32  *      mlib_status mlib_ImageConvMxN(mlib_image       *dst,
  33  *                                    const mlib_image *src,
  34  *                                    const mlib_s32   *kernel,
  35  *                                    mlib_s32         m,
  36  *                                    mlib_s32         n,
  37  *                                    mlib_s32         dm,
  38  *                                    mlib_s32         dn,
  39  *                                    mlib_s32         scale,
  40  *                                    mlib_s32         cmask,
  41  *                                    mlib_edge        edge)
  42  *
  43  * ARGUMENTS
  44  *      dst       Pointer to destination image.
  45  *      src       Pointer to source image.
  46  *      m         Kernel width (m must be not less than 1).
  47  *      n         Kernel height (n must be not less than 1).
  48  *      dm, dn    Position of key element in convolution kernel.
  49  *      kernel    Pointer to convolution kernel.
  50  *      scale     The scaling factor to convert the input integer
  51  *                coefficients into floating-point coefficients:
  52  *                floating-point coefficient = integer coefficient * 2^(-scale)
  53  *      cmask     Channel mask to indicate the channels to be convolved.
  54  *                Each bit of which represents a channel in the image. The
  55  *                channels corresponded to 1 bits are those to be processed.
  56  *      edge      Type of edge condition.
  57  *
  58  * DESCRIPTION
  59  *      2-D convolution, MxN kernel.
  60  *
  61  *      The center of the source image is mapped to the center of the
  62  *      destination image.
  63  *      The unselected channels are not overwritten. If both src and dst have
  64  *      just one channel, cmask is ignored.
  65  *
  66  *      The edge condition can be one of the following:
  67  *              MLIB_EDGE_DST_NO_WRITE  (default)
  68  *              MLIB_EDGE_DST_FILL_ZERO
  69  *              MLIB_EDGE_DST_COPY_SRC
  70  *              MLIB_EDGE_SRC_EXTEND
  71  *
  72  * RESTRICTION
  73  *      The src and the dst must be the same type and have same number
  74  *      of channels (1, 2, 3, or 4).
  75  *      m >= 1, n >= 1,
  76  *      0 <= dm < m, 0 <= dn < n.
  77  *      For data type MLIB_BYTE:   16 <= scale <= 31 (to be compatible with VIS version)
  78  *      For data type MLIB_USHORT: 17 <= scale <= 32 (to be compatible with VIS version)
  79  *      For data type MLIB_SHORT:  17 <= scale <= 32 (to be compatible with VIS version)
  80  *      For data type MLIB_INT:    scale >= 0
  81  */
  82 
  83 #include "mlib_image.h"
  84 #include "mlib_ImageConv.h"
  85 
  86 /***************************************************************/
  87 static void mlib_ImageConvMxNMulAdd_S32(mlib_d64       *dst,
  88                                         const mlib_s32 *src,
  89                                         const mlib_d64 *dkernel,
  90                                         mlib_s32       n,
  91                                         mlib_s32       m,
  92                                         mlib_s32       nch);
  93 
  94 static void mlib_ImageConvMxNMedian_S32(mlib_s32 *dst,
  95                                         mlib_d64 *src,
  96                                         mlib_s32 n,
  97                                         mlib_s32 nch);
  98 
  99 static void mlib_ImageConvMxNS322S32_ext(mlib_s32       *dst,
 100                                          const mlib_s32 *src,
 101                                          mlib_s32       n,
 102                                          mlib_s32       nch,
 103                                          mlib_s32       dx_l,
 104                                          mlib_s32       dx_r);
 105 
 106 /***************************************************************/
 107 #ifdef MLIB_USE_FTOI_CLAMPING
 108 
 109 #define CLAMP_S32(dst, src)                                     \
 110   dst = (mlib_s32)(src)
 111 
 112 #else
 113 
 114 #define CLAMP_S32(dst, src) {                                   \
 115   mlib_d64 s0 = (mlib_d64)(src);                                \
 116   if (s0 > (mlib_d64)MLIB_S32_MAX) s0 = (mlib_d64)MLIB_S32_MAX; \
 117   if (s0 < (mlib_d64)MLIB_S32_MIN) s0 = (mlib_d64)MLIB_S32_MIN; \
 118   dst = (mlib_s32)s0;                                           \
 119 }
 120 
 121 #endif /* MLIB_USE_FTOI_CLAMPING */
 122 
 123 /***************************************************************/
 124 void mlib_ImageConvMxNMulAdd_S32(mlib_d64       *dst,
 125                                  const mlib_s32 *src,
 126                                  const mlib_d64 *dkernel,
 127                                  mlib_s32       n,
 128                                  mlib_s32       m,
 129                                  mlib_s32       nch)
 130 {
 131   mlib_d64 *dst1 = dst + 1;
 132   mlib_s32 i, j;
 133 
 134   for (j = 0; j < m; j += 3, src += 3 * nch, dkernel += 3) {
 135     const mlib_s32 *src2 = src + 2 * nch;
 136     mlib_d64 hval0 = dkernel[0];
 137     mlib_d64 hval1 = dkernel[1];
 138     mlib_d64 hval2 = dkernel[2];
 139     mlib_d64 val0 = src[0];
 140     mlib_d64 val1 = src[nch];
 141     mlib_d64 dval = dst[0];
 142 
 143     if (j == m - 2) {
 144       hval2 = 0.f;
 145     }
 146     else if (j == m - 1) {
 147       hval1 = 0.f;
 148       hval2 = 0.f;
 149     }
 150 
 151     for (i = 0; i < n; i++) {
 152       mlib_d64 dval0 = val0 * hval0 + dval;
 153       mlib_d64 val2 = src2[i * nch];
 154 
 155       dval = dst1[i];
 156       dval0 += val1 * hval1;
 157       dval0 += val2 * hval2;
 158       val0 = val1;
 159       val1 = val2;
 160 
 161       dst[i] = dval0;
 162     }
 163   }
 164 }
 165 
 166 /***************************************************************/
 167 void mlib_ImageConvMxNMedian_S32(mlib_s32 *dst,
 168                                  mlib_d64 *src,
 169                                  mlib_s32 n,
 170                                  mlib_s32 nch)
 171 {
 172   mlib_s32 i;
 173 
 174   for (i = 0; i < n; i++) {
 175     mlib_s32 res;
 176 
 177     CLAMP_S32(res, src[i]);
 178     src[i] = 0.5;
 179     dst[i * nch] = res;
 180   }
 181 }
 182 
 183 /***************************************************************/
 184 void mlib_ImageConvMxNS322S32_ext(mlib_s32       *dst,
 185                                   const mlib_s32 *src,
 186                                   mlib_s32       n,
 187                                   mlib_s32       nch,
 188                                   mlib_s32       dx_l,
 189                                   mlib_s32       dx_r)
 190 {
 191   mlib_s32 i;
 192   mlib_d64 val = src[0];
 193 
 194   for (i = 0; i < dx_l; i++)
 195     dst[i] = (mlib_s32) val;
 196   for (; i < n - dx_r; i++)
 197     dst[i] = src[nch * (i - dx_l)];
 198   val = dst[n - dx_r - 1];
 199   for (; i < n; i++)
 200     dst[i] = (mlib_s32) val;
 201 }
 202 
 203 /***************************************************************/
 204 mlib_status mlib_convMxNext_s32(mlib_image       *dst,
 205                                 const mlib_image *src,
 206                                 const mlib_s32   *kernel,
 207                                 mlib_s32         m,
 208                                 mlib_s32         n,
 209                                 mlib_s32         dx_l,
 210                                 mlib_s32         dx_r,
 211                                 mlib_s32         dy_t,
 212                                 mlib_s32         dy_b,
 213                                 mlib_s32         scale,
 214                                 mlib_s32         cmask)
 215 {
 216   mlib_d64 dspace[1024], *dsa = dspace;
 217   mlib_d64 akernel[256], *dkernel = akernel, fscale = 1.0;
 218   mlib_s32 wid_e = mlib_ImageGetWidth(src);
 219   mlib_d64 *dsh, *dsv;
 220   mlib_s32 *isa;
 221   mlib_s32 *da = mlib_ImageGetData(dst);
 222   mlib_s32 *sa = mlib_ImageGetData(src);
 223   mlib_s32 dlb = mlib_ImageGetStride(dst) >> 2;
 224   mlib_s32 slb = mlib_ImageGetStride(src) >> 2;
 225   mlib_s32 dw = mlib_ImageGetWidth(dst);
 226   mlib_s32 dh = mlib_ImageGetHeight(dst);
 227   mlib_s32 nch = mlib_ImageGetChannels(dst);
 228   mlib_s32 i, j, j1, k, mn;
 229 
 230   /* internal buffer */
 231 
 232   if (3 * wid_e + m > 1024) {
 233     dsa = mlib_malloc((3 * wid_e + m) * sizeof(mlib_d64));
 234 
 235     if (dsa == NULL)
 236       return MLIB_FAILURE;
 237   }
 238 
 239   isa = (mlib_s32 *) dsa;
 240 
 241   /* load kernel */
 242   mn = m * n;
 243 
 244   if (mn > 256) {
 245     dkernel = mlib_malloc(mn * sizeof(mlib_d64));
 246 
 247     if (dkernel == NULL) {
 248       if (dsa != dspace) mlib_free(dsa);
 249       return MLIB_FAILURE;
 250     }
 251   }
 252 
 253   while (scale > 30) {
 254     fscale /= (1 << 30);
 255     scale -= 30;
 256   }
 257 
 258   fscale /= (1 << scale);
 259 
 260   for (i = 0; i < mn; i++) {
 261     dkernel[i] = ((mlib_s32 *) kernel)[i] * fscale;
 262   }
 263 
 264   dsh = dsa + dw + m;
 265   dsv = dsh + dw;
 266 
 267   for (i = 0; i < dw; i++) {
 268     dsh[i] = 0.5;
 269     dsv[i] = 0.5;
 270   }
 271 
 272   for (j = 0; j < dh; j++, da += dlb) {
 273     for (k = 0; k < nch; k++)
 274       if (cmask & (1 << (nch - 1 - k))) {
 275         mlib_s32 *sa1 = sa + k;
 276         mlib_d64 *dkernel1 = dkernel;
 277 
 278         for (j1 = 0; j1 < n; j1++, dkernel1 += m) {
 279           mlib_ImageConvMxNS322S32_ext(isa, sa1, dw + m - 1, nch, dx_l, dx_r);
 280           mlib_ImageConvMxNMulAdd_S32(dsh, isa, dkernel1, dw, m, 1);
 281 
 282           if ((j + j1 >= dy_t) && (j + j1 < dh + n - dy_b - 2))
 283             sa1 += slb;
 284         }
 285 
 286         mlib_ImageConvMxNMedian_S32(da + k, dsh, dw, nch);
 287       }
 288 
 289     if ((j >= dy_t) && (j < dh + n - dy_b - 2))
 290       sa += slb;
 291   }
 292 
 293   if (dkernel != akernel)
 294     mlib_free(dkernel);
 295   if (dsa != dspace)
 296     mlib_free(dsa);
 297   return MLIB_SUCCESS;
 298 }
 299 
 300 /***************************************************************/