10f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh/*
20f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * Copyright (C) 2010 The Android Open Source Project
30f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh *
40f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * Licensed under the Apache License, Version 2.0 (the "License");
50f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * you may not use this file except in compliance with the License.
60f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * You may obtain a copy of the License at
70f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh *
80f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh *      http://www.apache.org/licenses/LICENSE-2.0
90f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh *
100f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * Unless required by applicable law or agreed to in writing, software
110f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * distributed under the License is distributed on an "AS IS" BASIS,
120f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * See the License for the specific language governing permissions and
140f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * limitations under the License.
150f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh */
160f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
170f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehpackage com.android.photoeditor;
180f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
190f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehimport android.graphics.Bitmap;
200f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehimport android.os.Handler;
210f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehimport android.os.HandlerThread;
220f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehimport android.os.Message;
230f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
240f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehimport com.android.photoeditor.filters.Filter;
250f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
260f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehimport java.util.Stack;
270f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehimport java.util.Vector;
280f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
290f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh/**
300f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh * A stack of filters to be applied onto a photo.
310f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh */
320f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsiehpublic class FilterStack {
330f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
340f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    /**
350f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh     * Listener of stack changes.
360f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh     */
370f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public interface StackListener {
380f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
390f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        void onStackChanged(boolean canUndo, boolean canRedo);
400f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
410f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
420f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static class OutputMessageObj {
430f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        PhotoOutputCallback callback;
440f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        Photo result;
450f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
460f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
470f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int COPY_SOURCE = 1;
480f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int COPY_RESULT = 2;
490f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int SET_SOURCE = 3;
500f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int CLEAR_SOURCE = 4;
510f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int CLEAR_STACKS = 5;
520f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int PUSH_FILTER = 6;
530f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int UNDO = 7;
540f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int REDO = 8;
550f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int TOP_FILTER_CHANGE = 9;
560f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private static final int OUTPUT = 10;
570f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
580f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private final Stack<Filter> appliedStack = new Stack<Filter>();
590f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private final Stack<Filter> redoStack = new Stack<Filter>();
600f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private final Vector<Message> pendingMessages = new Vector<Message>();
610f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private final Handler mainHandler;
620f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private final Handler workerHandler;
630f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
640f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    // Use two photo buffers as in and out in turns to apply filters in the stack.
650f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private final Photo[] buffers = new Photo[2];
660f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
670f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private Photo source;
680f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private StackListener stackListener;
690f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
700f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public FilterStack() {
710f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        HandlerThread workerThread = new HandlerThread("FilterStackWorker");
720f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerThread.start();
730f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
740f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        mainHandler = new Handler() {
750f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
760f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            @Override
770f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            public void handleMessage(Message msg) {
780f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                switch (msg.what) {
790f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case OUTPUT:
800f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        OutputMessageObj obj = (OutputMessageObj) msg.obj;
810f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        obj.callback.onReady(obj.result);
820f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
830f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                }
840f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
850f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        };
860f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler = new Handler(workerThread.getLooper()) {
870f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
880f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            private void output(PhotoOutputCallback callback, Photo target) {
890f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                // Copy target photo in rgb-565 format to update photo-view or save.
900f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                OutputMessageObj obj = new OutputMessageObj();
910f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                obj.callback = callback;
920f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                obj.result = (target != null) ? target.copy(Bitmap.Config.RGB_565) : null;
930f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                mainHandler.sendMessage(mainHandler.obtainMessage(OUTPUT, obj));
940f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
950f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
960f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            private void clearBuffers() {
970f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                pendingMessages.clear();
980f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                workerHandler.removeMessages(TOP_FILTER_CHANGE);
990f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                mainHandler.removeMessages(OUTPUT);
1000f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                for (int i = 0; i < buffers.length; i++) {
1010f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    if (buffers[i] != null) {
1020f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        buffers[i].clear();
1030f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        buffers[i] = null;
1040f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    }
1050f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                }
1060f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
1070f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1080f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            private void reallocateBuffer(int target) {
1090f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                int other = target ^ 1;
1100f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                buffers[target] = Photo.create(Bitmap.createBitmap(
1110f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        buffers[other].width(), buffers[other].height(), Bitmap.Config.ARGB_8888));
1120f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
1130f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1140f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            private void invalidate() {
1150f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                // In/out buffers need redrawn by reloading source photo and re-applying filters.
1160f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                clearBuffers();
1170f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                buffers[0] = (source != null) ? source.copy(Bitmap.Config.ARGB_8888) : null;
1180f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                if (buffers[0] != null) {
1190f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    reallocateBuffer(1);
1200f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1210f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    int out = 1;
1220f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    for (Filter filter : appliedStack) {
1230f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        runFilter(filter, out);
1240f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        out = out ^ 1;
1250f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    }
1260f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                }
1270f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
1280f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1290f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            private void runFilter(Filter filter, int out) {
1300f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                if ((buffers[0] != null) && (buffers[1] != null)) {
1310f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    int in = out ^ 1;
1320f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    filter.process(buffers[in], buffers[out]);
1330f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    if (!buffers[in].matchDimension(buffers[out])) {
1340f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        buffers[in].clear();
1350f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        reallocateBuffer(in);
1360f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    }
1370f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                }
1380f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
1390f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1400f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            private int getOutBufferIndex() {
1410f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                // buffers[0] and buffers[1] are swapped in turns as the in/out buffers for
1420f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                // processing stacked filters. For example, the first filter reads buffer[0] and
1430f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                // writes buffer[1]; the second filter then reads buffer[1] and writes buffer[0].
1440f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                return appliedStack.size() % 2;
1450f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
1460f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1470f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            @Override
1480f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            public void handleMessage(Message msg) {
1490f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                switch (msg.what) {
1500f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case COPY_SOURCE:
1510f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        output((PhotoOutputCallback) msg.obj, source);
1520f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
1530f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1540f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case COPY_RESULT:
1550f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        output((PhotoOutputCallback) msg.obj, buffers[getOutBufferIndex()]);
1560f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
1570f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1580f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case SET_SOURCE:
1590f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        source = (Photo) msg.obj;
1600f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        invalidate();
1610f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
1620f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1630f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case CLEAR_SOURCE:
1640f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        if (source != null) {
1650f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            source.clear();
1660f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            source = null;
1670f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        }
1680f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        clearBuffers();
1690f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
1700f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1710f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case CLEAR_STACKS:
1720f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        redoStack.clear();
1730f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        appliedStack.clear();
1740f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
1750f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1760f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case PUSH_FILTER:
1770f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        redoStack.clear();
1780f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        appliedStack.push((Filter) msg.obj);
1790f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        stackChanged();
1800f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
1810f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1820f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case UNDO:
1830f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        if (!appliedStack.empty()) {
1840f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            redoStack.push(appliedStack.pop());
1850f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            stackChanged();
1860f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            invalidate();
1870f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        }
1880f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        output((PhotoOutputCallback) msg.obj, buffers[getOutBufferIndex()]);
1890f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
1900f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
1910f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case REDO:
1920f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        if (!redoStack.empty()) {
1930f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            Filter filter = redoStack.pop();
1940f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            appliedStack.push(filter);
1950f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            stackChanged();
1960f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            runFilter(filter, getOutBufferIndex());
1970f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        }
1980f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        output((PhotoOutputCallback) msg.obj, buffers[getOutBufferIndex()]);
1990f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
2000f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2010f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                    case TOP_FILTER_CHANGE:
2020f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        if (pendingMessages.remove(msg) && !appliedStack.empty()) {
2030f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            int out = getOutBufferIndex();
2040f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            runFilter(appliedStack.peek(), out);
2050f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                            output((PhotoOutputCallback) msg.obj, buffers[out]);
2060f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        }
2070f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                        break;
2080f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh                }
2090f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            }
2100f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        };
2110f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2120f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2130f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void getSourceCopy(PhotoOutputCallback callback) {
2140f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(COPY_SOURCE, callback));
2150f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2160f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2170f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void getResultCopy(PhotoOutputCallback callback) {
2180f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(COPY_RESULT, callback));
2190f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2200f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2210f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void setPhotoSource(Photo source) {
2220f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(SET_SOURCE, source));
2230f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2240f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2250f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void clearPhotoSource() {
2260f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(CLEAR_SOURCE));
2270f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2280f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2290f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void clearStacks() {
2300f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(CLEAR_STACKS));
2310f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2320f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2330f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void pushFilter(Filter filter) {
2340f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(PUSH_FILTER, filter));
2350f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2360f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2370f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void undo(PhotoOutputCallback callback) {
2380f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(UNDO, callback));
2390f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2400f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2410f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void redo(PhotoOutputCallback callback) {
2420f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(workerHandler.obtainMessage(REDO, callback));
2430f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2440f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2450f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public void topFilterChanged(PhotoOutputCallback callback) {
2460f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        // Flush outdated top-filter messages before sending new ones.
2470f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        Message msg = workerHandler.obtainMessage(TOP_FILTER_CHANGE, callback);
2480f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        pendingMessages.clear();
2490f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        pendingMessages.add(msg);
2500f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.removeMessages(TOP_FILTER_CHANGE);
2510f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        workerHandler.sendMessage(msg);
2520f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2530f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2540f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    public synchronized void setStackListener(StackListener listener) {
2550f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        stackListener = listener;
2560f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2570f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh
2580f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    private synchronized void stackChanged() {
2590f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        if (stackListener != null) {
2600f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh            stackListener.onStackChanged(!appliedStack.empty(), !redoStack.empty());
2610f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh        }
2620f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh    }
2630f8a40e4cfdc5f6cd47c22e81f69ed0446067c54Andrew Hsieh}
264