package com.rec.photoeditor.graphics;
import java.util.LinkedList;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.util.Log;
import com.rec.photoeditor.graphics.commands.ImageProcessingCommand;
/**
* ImageProcessor class works as a thread pool with only one working thread.
*
*/
public class ImageProcessor {
// Singleton
private static ImageProcessor instance = null;
private boolean modified = false;
private ImageProcessorListener processListener;
private LinkedList<ImageProcessingCommand> queue;
private Bitmap savedBitmap;
private Bitmap lastResultBitmap;
private Context applicationContext;
private Handler uiThreadHandler;
public boolean isModified(){
return modified;
}
public void resetModificationFlag(){
modified = false;
}
public static ImageProcessor getInstance() {
if (instance == null) {
instance = new ImageProcessor();
}
return instance;
}
private ImageProcessor() {
queue = new LinkedList<ImageProcessingCommand>();
workingThread.start();
}
// ===========================
private Thread workingThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
ImageProcessingCommand cmd;
try {
// Queue object works as monitor
synchronized (queue) {
while (queue.isEmpty()) {
queue.wait();
}
cmd = queue.poll();
}
// Process command
onProcessStart();
lastResultBitmap = cmd.process(savedBitmap);
cmd = null;
onProcessEnd();
modified=true;
} catch (InterruptedException e) {
Log.e("Working Thread",
"InterruptedException handled:" + e.getMessage());
break; // Terminate
}
}
}
private void onProcessStart() {
if (uiThreadHandler!= null && processListener != null) {
uiThreadHandler.post(new Runnable() {
@Override
public void run() {
processListener.onProcessStart();
}
});
}
}
private void onProcessEnd() {
// do postprocessing HERE
// notify UI thread about new bitmap and save results
if (uiThreadHandler!= null && processListener != null) {
uiThreadHandler.post(new Runnable() {
@Override
public void run() {
Log.i("REC Photo Editor",
"Notify Listener: STOP");
processListener.onProcessEnd(lastResultBitmap);
}
});
}
}
});
public void setBitmap(Bitmap bitmap) {
this.savedBitmap = bitmap;
}
public Bitmap getBitmap() {
return savedBitmap;
}
/**
* Runs {@code ImageProcessingCommand} or add to queue if currently some
* command is running
*
* @param command
*/
public void runCommand(ImageProcessingCommand command) {
conditionallyAddToQueue(command);
}
/**
* Conditionally add new command to processing queue. If last element is the
* same type but with different argument as added one, removes last before
* adding. This method is helpful in slider handling, which send command in
* every value change.
*/
private void conditionallyAddToQueue(ImageProcessingCommand command) {
synchronized (queue) {
if (!queue.isEmpty()) {
ImageProcessingCommand c = queue.getLast();
Log.i("REC Photo Editor", "Command Added TO queue ID = " + c);
if (c.getId().equals(command.getId())) {
queue.removeLast();
Log.i("REC Photo Editor", "Queue Element Skipped");
}
}
queue.add(command);
queue.notify();
}
}
public void save() {
if (lastResultBitmap != null) {
if (savedBitmap != lastResultBitmap && savedBitmap != null) {
savedBitmap.recycle();
}
savedBitmap = lastResultBitmap;
lastResultBitmap = null;
}
}
public ImageProcessorListener getProcessListener() {
return processListener;
}
public void setProcessListener(ImageProcessorListener processListener) {
this.processListener = processListener;
}
public void clearProcessListener() {
this.processListener = null;
if (lasResultCanBeRecycled()) {
lastResultBitmap.recycle();
}
this.lastResultBitmap = null;
}
private boolean lasResultCanBeRecycled() {
return lastResultBitmap != savedBitmap && lastResultBitmap != null;
}
public Bitmap getLastResultBitmap() {
return lastResultBitmap;
}
public void setLastResultBitmap(Bitmap lastResultBitmap) {
this.lastResultBitmap = lastResultBitmap;
}
/**
* This method should be called in UI thread, or handler won't be able to receive messages.
*/
public void setApplicationCotnext(Context applicationContext){
if (this.applicationContext == null){
uiThreadHandler = new Handler();
}
this.applicationContext = applicationContext;
}
}