content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   * 
   *   http://www.apache.org/licenses/LICENSE-2.0
   * 
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  package org.apache.weex.commons.adapter;
  
  import android.graphics.Bitmap;
  import android.support.annotation.NonNull;
  import android.support.annotation.Nullable;
  
  import com.taobao.weex.utils.WXLogUtils;
  
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  import java.util.concurrent.ThreadFactory;
  
  /**
   * Created by rowandjj(chuyi)<br/>
   */
  
  public class BlurTool {
  
      public interface OnBlurCompleteListener {
          /**
           * blur complete event.(Notice:in sub thread)
           *
           * @param bitmap the blurred bitmap
           * */
          void onBlurComplete(@NonNull Bitmap bitmap);
      }
  
      private static ExecutorService sExecutorService = Executors.newCachedThreadPool(new ThreadFactory() {
          @Override
          public Thread newThread(Runnable r) {
              return new Thread(r,"wx_blur_thread");
          }
      });
  
      private static final String TAG = "BlurTool";
  
      /**
       * radius in [0,10]
       * */
      @NonNull
      @SuppressWarnings("unused")
      public static Bitmap blur(@NonNull Bitmap originalImage, int radius) {
          long start = System.currentTimeMillis();
          radius = Math.min(10, Math.max(0,radius));//[0,10]
          if(radius == 0) {
              return originalImage;
          }
          int width = originalImage.getWidth();
          int height = originalImage.getHeight();
  
          if(width <= 0 || height <= 0) {
              return originalImage;
          }
  
          double sampling = calculateSampling(radius);
          int retryTimes = 3;
          Bitmap sampledImage = Bitmap.createScaledBitmap(originalImage,(int)(sampling*width),(int)(sampling*height),true);
          for(int i = 0; i < retryTimes; i++) {
              try {
                  if(radius == 0) {
                      return originalImage;
                  }
                  double s = calculateSampling(radius);
                  if(s != sampling) {
                      sampling = s;
                      sampledImage = Bitmap.createScaledBitmap(originalImage,(int)(sampling*width),(int)(sampling*height),true);
                  }
  
                  Bitmap result = stackBlur(sampledImage,radius);
                  return result;
              }catch (Exception e) {
                  WXLogUtils.e(TAG, "thrown exception when blurred image(times = " + i + "),"+ e.getMessage());
                  radius -= 1;
                  radius = Math.max(0,radius);
              }
          }
          WXLogUtils.d(TAG, "elapsed time on blurring image(radius:"+ radius + ",sampling: " + sampling + "): " + (System.currentTimeMillis() - start) + "ms");
          return originalImage;
      }
  
      private static double calculateSampling(int radius){
          double sampling;
          if(radius <= 3) {
              sampling = 1/(double)2;
          }else if(radius <= 8) {
              sampling = 1/(double)4;
          }else {
              sampling = 1/(double)8;
          }
  
          return sampling;
      }
  
      @SuppressWarnings("unused")
      public static void asyncBlur(@NonNull final Bitmap originalImage, final int radius, @Nullable final OnBlurCompleteListener listener) {
          sExecutorService.execute(new Runnable() {
              @Override
              public void run() {
                  if(listener != null) {
                      listener.onBlurComplete(blur(originalImage,radius));
                  }
              }
          });
      }
  
      private static Bitmap stackBlur(Bitmap sentBitmap, int radius) {
          // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
          Bitmap bitmap = sentBitmap.copy(sentBitmap.getConfig(), true);
  
          if (radius < 1) {
              return (null);
          }
  
          int w = bitmap.getWidth();
          int h = bitmap.getHeight();
  
          int[] pix = new int[w * h];
          bitmap.getPixels(pix, 0, w, 0, 0, w, h);
  
          int wm = w - 1;
          int hm = h - 1;
          int wh = w * h;
          int div = radius + radius + 1;
  
          int r[] = new int[wh];
          int g[] = new int[wh];
          int b[] = new int[wh];
          int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
          int vmin[] = new int[Math.max(w, h)];
  
          int divsum = (div + 1) >> 1;
          divsum *= divsum;
          int dv[] = new int[256 * divsum];
          for (i = 0; i < 256 * divsum; i++) {
              dv[i] = (i / divsum);
          }
  
          yw = yi = 0;
  
          int[][] stack = new int[div][3];
          int stackpointer;
          int stackstart;
          int[] sir;
          int rbs;
          int r1 = radius + 1;
          int routsum, goutsum, boutsum;
          int rinsum, ginsum, binsum;
  
          for (y = 0; y < h; y++) {
              rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
              for (i = -radius; i <= radius; i++) {
                  p = pix[yi + Math.min(wm, Math.max(i, 0))];
                  sir = stack[i + radius];
                  sir[0] = (p & 0xff0000) >> 16;
                  sir[1] = (p & 0x00ff00) >> 8;
                  sir[2] = (p & 0x0000ff);
                  rbs = r1 - Math.abs(i);
                  rsum += sir[0] * rbs;
                  gsum += sir[1] * rbs;
                  bsum += sir[2] * rbs;
                  if (i > 0) {
                      rinsum += sir[0];
                      ginsum += sir[1];
                      binsum += sir[2];
                  } else {
                      routsum += sir[0];
                      goutsum += sir[1];
                      boutsum += sir[2];
                  }
              }
              stackpointer = radius;
  
              for (x = 0; x < w; x++) {
  
                  r[yi] = dv[rsum];
                  g[yi] = dv[gsum];
                  b[yi] = dv[bsum];
  
                  rsum -= routsum;
                  gsum -= goutsum;
                  bsum -= boutsum;
  
                  stackstart = stackpointer - radius + div;
                  sir = stack[stackstart % div];
  
                  routsum -= sir[0];
                  goutsum -= sir[1];
                  boutsum -= sir[2];
  
                  if (y == 0) {
                      vmin[x] = Math.min(x + radius + 1, wm);
                  }
                  p = pix[yw + vmin[x]];
  
                  sir[0] = (p & 0xff0000) >> 16;
                  sir[1] = (p & 0x00ff00) >> 8;
                  sir[2] = (p & 0x0000ff);
  
                  rinsum += sir[0];
                  ginsum += sir[1];
                  binsum += sir[2];
  
                  rsum += rinsum;
                  gsum += ginsum;
                  bsum += binsum;
  
                  stackpointer = (stackpointer + 1) % div;
                  sir = stack[(stackpointer) % div];
  
                  routsum += sir[0];
                  goutsum += sir[1];
                  boutsum += sir[2];
  
                  rinsum -= sir[0];
                  ginsum -= sir[1];
                  binsum -= sir[2];
  
                  yi++;
              }
              yw += w;
          }
          for (x = 0; x < w; x++) {
              rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
              yp = -radius * w;
              for (i = -radius; i <= radius; i++) {
                  yi = Math.max(0, yp) + x;
  
                  sir = stack[i + radius];
  
                  sir[0] = r[yi];
                  sir[1] = g[yi];
                  sir[2] = b[yi];
  
                  rbs = r1 - Math.abs(i);
  
                  rsum += r[yi] * rbs;
                  gsum += g[yi] * rbs;
                  bsum += b[yi] * rbs;
  
                  if (i > 0) {
                      rinsum += sir[0];
                      ginsum += sir[1];
                      binsum += sir[2];
                  } else {
                      routsum += sir[0];
                      goutsum += sir[1];
                      boutsum += sir[2];
                  }
  
                  if (i < hm) {
                      yp += w;
                  }
              }
              yi = x;
              stackpointer = radius;
              for (y = 0; y < h; y++) {
                  // Preserve alpha channel: ( 0xff000000 & pix[yi] )
                  pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
  
                  rsum -= routsum;
                  gsum -= goutsum;
                  bsum -= boutsum;
  
                  stackstart = stackpointer - radius + div;
                  sir = stack[stackstart % div];
  
                  routsum -= sir[0];
                  goutsum -= sir[1];
                  boutsum -= sir[2];
  
                  if (x == 0) {
                      vmin[y] = Math.min(y + r1, hm) * w;
                  }
                  p = x + vmin[y];
  
                  sir[0] = r[p];
                  sir[1] = g[p];
                  sir[2] = b[p];
  
                  rinsum += sir[0];
                  ginsum += sir[1];
                  binsum += sir[2];
  
                  rsum += rinsum;
                  gsum += ginsum;
                  bsum += binsum;
  
                  stackpointer = (stackpointer + 1) % div;
                  sir = stack[stackpointer];
  
                  routsum += sir[0];
                  goutsum += sir[1];
                  boutsum += sir[2];
  
                  rinsum -= sir[0];
                  ginsum -= sir[1];
                  binsum -= sir[2];
  
                  yi += w;
              }
          }
  
          bitmap.setPixels(pix, 0, w, 0, 0, w, h);
  
          return (bitmap);
      }
  }
  