package at.favre.lib.dali.builder.live;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.view.ViewCompat;
import android.util.Log;
import android.view.View;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import at.favre.lib.dali.Dali;
import at.favre.lib.dali.builder.exception.LiveBlurWorkerException;
import at.favre.lib.dali.util.BuilderUtil;
/**
* This is contains the business logic for the live blur feature.
* It is optimized to reuse resources for fast and continuous re-blurring.
*/
public class LiveBlurWorker {
private final static String TAG = LiveBlurWorker.class.getSimpleName();
private static final int BLUR_ROUNDS_PER_UPDATE = 75;
private AtomicBoolean isWorking = new AtomicBoolean(false);
private AtomicInteger blursLeft = new AtomicInteger(0);
private Bitmap dest;
private Handler handler = new Handler(Looper.getMainLooper());
private Runnable blurRunnable = new BlurRunnable();
private LiveBlurBuilder.LiveBlurData data;
public LiveBlurWorker(LiveBlurBuilder.LiveBlurData data) {
this.data = data;
}
public boolean updateBlurView() {
try {
Dali.logD(TAG, "update");
if (!BuilderUtil.isOnUiThread()) {
Dali.logD(TAG, "Not on ui thread");
return false;
}
if (data.rootView == null || data.viewsToBlurOnto.isEmpty()) {
Dali.logD(TAG, "Views not set");
return false;
}
if (data.viewsToBlurOnto.get(0).getWidth() == 0 || data.viewsToBlurOnto.get(0).getHeight() == 0) {
Dali.logD(TAG, "Views not ready to be blurred");
return false;
}
if (blursLeft.get() < BLUR_ROUNDS_PER_UPDATE) {
blursLeft.addAndGet(BLUR_ROUNDS_PER_UPDATE);
}
if (!isWorking.get()) {
handler.post(blurRunnable);
return true;
} else {
Dali.logD(TAG, "Skip blur frame, already in blur");
}
} catch (Throwable t) {
isWorking.set(false);
if (data.silentFail) {
Log.e(TAG, "Could not create blur view", t);
} else {
throw new LiveBlurWorkerException("Error while updating the live blur", t);
}
}
return false;
}
private class BlurRunnable implements Runnable {
@Override
public void run() {
isWorking.compareAndSet(false, true);
dest = BuilderUtil.drawViewToBitmap(dest, data.rootView, data.downSampleSize, data.config);
for (View view : data.viewsToBlurOnto) {
Drawable d = new BitmapDrawable(data.contextWrapper.getResources(), data.blurAlgorithm.blur(data.blurRadius, crop(dest.copy(dest.getConfig(), true), view, data.downSampleSize)));
ViewCompat.setBackground(view, d);
}
int left = blursLeft.decrementAndGet();
if (left > 0) {
handler.post(this);
} else {
isWorking.compareAndSet(true, false);
}
}
}
/**
* crops the srcBmp with the canvasView bounds and returns the cropped bitmap
*/
private static Bitmap crop(Bitmap srcBmp, View canvasView, int downsampling) {
float scale = 1f / downsampling;
return Bitmap.createBitmap(
srcBmp,
(int) Math.floor((ViewCompat.getX(canvasView)) * scale),
(int) Math.floor((ViewCompat.getY(canvasView)) * scale),
(int) Math.floor((canvasView.getWidth()) * scale),
(int) Math.floor((canvasView.getHeight()) * scale)
);
}
}