2 * Copyright (C) 2013 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.gallery3d.filtershow.imageshow;
19 import android.graphics.Bitmap;
20 import android.graphics.Matrix;
21 import android.graphics.Point;
22 import android.graphics.Rect;
23 import android.graphics.RectF;
24 import android.os.Handler;
25 import android.os.Message;
27 import com.android.gallery3d.filtershow.FilterShowActivity;
28 import com.android.gallery3d.filtershow.history.HistoryManager;
29 import com.android.gallery3d.filtershow.history.HistoryItem;
30 import com.android.gallery3d.filtershow.pipeline.FilteringPipeline;
31 import com.android.gallery3d.filtershow.cache.ImageLoader;
32 import com.android.gallery3d.filtershow.pipeline.RenderingRequest;
33 import com.android.gallery3d.filtershow.pipeline.RenderingRequestCaller;
34 import com.android.gallery3d.filtershow.filters.FilterRepresentation;
35 import com.android.gallery3d.filtershow.filters.ImageFilter;
36 import com.android.gallery3d.filtershow.pipeline.Buffer;
37 import com.android.gallery3d.filtershow.pipeline.SharedBuffer;
38 import com.android.gallery3d.filtershow.pipeline.SharedPreset;
39 import com.android.gallery3d.filtershow.pipeline.ImagePreset;
40 import com.android.gallery3d.filtershow.state.StateAdapter;
42 import java.util.Vector;
44 public class MasterImage implements RenderingRequestCaller {
46 private static final String LOGTAG = "MasterImage";
47 private boolean DEBUG = false;
48 private static final boolean DISABLEZOOM = false;
49 private static MasterImage sMasterImage = null;
50 private static int sIconSeedSize = 128;
51 private static float sHistoryPreviewSize = 128.0f;
53 private boolean mSupportsHighRes = false;
55 private ImageFilter mCurrentFilter = null;
56 private ImagePreset mPreset = null;
57 private ImagePreset mLoadedPreset = null;
58 private ImagePreset mGeometryOnlyPreset = null;
59 private ImagePreset mFiltersOnlyPreset = null;
61 private SharedBuffer mPreviewBuffer = new SharedBuffer();
62 private SharedPreset mPreviewPreset = new SharedPreset();
64 private Bitmap mGeometryOnlyBitmap = null;
65 private Bitmap mFiltersOnlyBitmap = null;
66 private Bitmap mPartialBitmap = null;
67 private Bitmap mHighresBitmap = null;
69 private ImageLoader mLoader = null;
70 private HistoryManager mHistory = null;
71 private StateAdapter mState = null;
73 private FilterShowActivity mActivity = null;
75 private Vector<ImageShow> mObservers = new Vector<ImageShow>();
76 private FilterRepresentation mCurrentFilterRepresentation;
77 private Vector<GeometryListener> mGeometryListeners = new Vector<GeometryListener>();
79 private GeometryMetadata mPreviousGeometry = null;
81 private float mScaleFactor = 1.0f;
82 private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image
83 private Point mTranslation = new Point();
84 private Point mOriginalTranslation = new Point();
86 private Point mImageShowSize = new Point();
88 private boolean mShowsOriginal;
90 final private static int NEW_GEOMETRY = 1;
92 private final Handler mHandler = new Handler() {
94 public void handleMessage(Message msg) {
104 private MasterImage() {
107 // TODO: remove singleton
108 public static void setMaster(MasterImage master) {
109 sMasterImage = master;
112 public static MasterImage getImage() {
113 if (sMasterImage == null) {
114 sMasterImage = new MasterImage();
119 public void setSupportsHighRes(boolean value) {
120 mSupportsHighRes = value;
123 public static void setIconSeedSize(int iconSeedSize) {
124 sIconSeedSize = iconSeedSize;
127 public void addObserver(ImageShow observer) {
128 if (mObservers.contains(observer)) {
131 mObservers.add(observer);
134 public void setActivity(FilterShowActivity activity) {
135 mActivity = activity;
138 public ImageLoader getLoader() {
142 public synchronized ImagePreset getPreset() {
146 public synchronized ImagePreset getGeometryPreset() {
147 return mGeometryOnlyPreset;
150 public synchronized ImagePreset getFiltersOnlyPreset() {
151 return mFiltersOnlyPreset;
154 public synchronized void setPreset(ImagePreset preset,
155 FilterRepresentation change,
156 boolean addToHistory) {
158 preset.showFilters();
161 mPreset.setImageLoader(mLoader);
163 mPreset.fillImageStateAdapter(mState);
165 HistoryItem historyItem = new HistoryItem(mPreset, change);
166 mHistory.addHistoryItem(historyItem);
169 GeometryMetadata geo = mPreset.getGeometry();
170 if (!geo.equals(mPreviousGeometry)) {
171 notifyGeometryChange();
173 mPreviousGeometry = new GeometryMetadata(geo);
174 mActivity.updateCategories();
177 private void setGeometry() {
178 Bitmap image = mLoader.getOriginalBitmapLarge();
182 float w = image.getWidth();
183 float h = image.getHeight();
184 GeometryMetadata geo = mPreset.getGeometry();
185 RectF pb = geo.getPhotoBounds();
186 if (w == pb.width() && h == pb.height()) {
189 RectF r = new RectF(0, 0, w, h);
190 geo.setPhotoBounds(r);
191 geo.setCropBounds(r);
192 mPreset.setGeometry(geo);
195 public void onHistoryItemClick(int position) {
196 HistoryItem historyItem = mHistory.getItem(position);
197 // We need a copy from the history
198 ImagePreset newPreset = new ImagePreset(historyItem.getImagePreset());
199 // don't need to add it to the history
200 setPreset(newPreset, historyItem.getFilterRepresentation(), false);
201 mHistory.setCurrentPreset(position);
204 public HistoryManager getHistory() {
208 public StateAdapter getState() {
212 public void setHistoryManager(HistoryManager adapter) {
216 public void setStateAdapter(StateAdapter adapter) {
220 public void setImageLoader(ImageLoader loader) {
224 public ImageLoader getImageLoader() {
228 public void setCurrentFilter(ImageFilter filter) {
229 mCurrentFilter = filter;
232 public ImageFilter getCurrentFilter() {
233 return mCurrentFilter;
236 public synchronized boolean hasModifications() {
237 // TODO: We need to have a better same effects check to see if two
238 // presets are functionally the same. Right now, we are relying on a
239 // stricter check as equals().
240 ImagePreset loadedPreset = getLoadedPreset();
241 if (mPreset == null) {
242 if (loadedPreset == null) {
245 return loadedPreset.hasModifications();
248 if (loadedPreset == null) {
249 return mPreset.hasModifications();
251 return !mPreset.equals(getLoadedPreset());
256 public SharedBuffer getPreviewBuffer() {
257 return mPreviewBuffer;
260 public SharedPreset getPreviewPreset() {
261 return mPreviewPreset;
264 public void setOriginalGeometry(Bitmap originalBitmapLarge) {
265 GeometryMetadata geo = getPreset().getGeometry();
266 float w = originalBitmapLarge.getWidth();
267 float h = originalBitmapLarge.getHeight();
268 RectF r = new RectF(0, 0, w, h);
269 geo.setPhotoBounds(r);
270 geo.setCropBounds(r);
271 getPreset().setGeometry(geo);
274 public Bitmap getFilteredImage() {
275 mPreviewBuffer.swapConsumerIfNeeded(); // get latest bitmap
276 Buffer consumer = mPreviewBuffer.getConsumer();
277 if (consumer != null) {
278 return consumer.getBitmap();
283 public Bitmap getFiltersOnlyImage() {
284 return mFiltersOnlyBitmap;
287 public Bitmap getGeometryOnlyImage() {
288 return mGeometryOnlyBitmap;
291 public Bitmap getPartialImage() {
292 return mPartialBitmap;
295 public Bitmap getHighresImage() {
296 return mHighresBitmap;
299 public void notifyObservers() {
300 for (ImageShow observer : mObservers) {
301 observer.invalidate();
305 public void updatePresets(boolean force) {
306 if (force || mGeometryOnlyPreset == null) {
307 ImagePreset newPreset = new ImagePreset(mPreset);
308 newPreset.setDoApplyFilters(false);
309 newPreset.setDoApplyGeometry(true);
310 if (force || mGeometryOnlyPreset == null
311 || !newPreset.same(mGeometryOnlyPreset)) {
312 mGeometryOnlyPreset = newPreset;
313 RenderingRequest.post(mLoader.getOriginalBitmapLarge(),
314 mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this);
317 if (force || mFiltersOnlyPreset == null) {
318 ImagePreset newPreset = new ImagePreset(mPreset);
319 newPreset.setDoApplyFilters(true);
320 newPreset.setDoApplyGeometry(false);
321 if (force || mFiltersOnlyPreset == null
322 || !newPreset.same(mFiltersOnlyPreset)) {
323 mFiltersOnlyPreset = newPreset;
324 RenderingRequest.post(mLoader.getOriginalBitmapLarge(),
325 mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this);
331 public FilterRepresentation getCurrentFilterRepresentation() {
332 return mCurrentFilterRepresentation;
335 public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) {
336 mCurrentFilterRepresentation = currentFilterRepresentation;
339 public void invalidateFiltersOnly() {
340 mFiltersOnlyPreset = null;
341 updatePresets(false);
344 public void invalidatePartialPreview() {
345 if (mPartialBitmap != null) {
346 mPartialBitmap = null;
351 public void invalidateHighresPreview() {
352 if (mHighresBitmap != null) {
353 mHighresBitmap = null;
358 public void invalidatePreview() {
359 mPreviewPreset.enqueuePreset(mPreset);
360 mPreviewBuffer.invalidate();
361 invalidatePartialPreview();
362 invalidateHighresPreview();
363 needsUpdatePartialPreview();
364 needsUpdateHighResPreview();
365 FilteringPipeline.getPipeline().updatePreviewBuffer();
368 public void setImageShowSize(int w, int h) {
369 if (mImageShowSize.x != w || mImageShowSize.y != h) {
370 mImageShowSize.set(w, h);
371 needsUpdatePartialPreview();
372 needsUpdateHighResPreview();
376 private Matrix getImageToScreenMatrix(boolean reflectRotation) {
377 GeometryMetadata geo = mPreset.getGeometry();
378 if (geo == null || mLoader == null
379 || mLoader.getOriginalBounds() == null
380 || mImageShowSize.x == 0) {
383 Matrix m = geo.getOriginalToScreen(reflectRotation,
384 mLoader.getOriginalBounds().width(),
385 mLoader.getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y);
386 Point translate = getTranslation();
387 float scaleFactor = getScaleFactor();
388 m.postTranslate(translate.x, translate.y);
389 m.postScale(scaleFactor, scaleFactor, mImageShowSize.x / 2.0f, mImageShowSize.y / 2.0f);
393 private Matrix getScreenToImageMatrix(boolean reflectRotation) {
394 Matrix m = getImageToScreenMatrix(reflectRotation);
395 Matrix invert = new Matrix();
400 public void needsUpdateHighResPreview() {
401 if (!mSupportsHighRes) {
404 RenderingRequest.post(null, mPreset, RenderingRequest.HIGHRES_RENDERING, this);
405 invalidateHighresPreview();
408 public void needsUpdatePartialPreview() {
409 if (!mPreset.canDoPartialRendering()) {
410 invalidatePartialPreview();
413 Matrix m = getScreenToImageMatrix(true);
414 RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y);
415 RectF dest = new RectF();
417 Rect bounds = new Rect();
418 dest.roundOut(bounds);
419 RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING,
420 this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y));
421 invalidatePartialPreview();
425 public void available(RenderingRequest request) {
426 if (request.getBitmap() == null) {
430 boolean needsCheckModification = false;
431 if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
432 mGeometryOnlyBitmap = request.getBitmap();
433 needsCheckModification = true;
435 if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
436 mFiltersOnlyBitmap = request.getBitmap();
437 needsCheckModification = true;
439 if (request.getType() == RenderingRequest.PARTIAL_RENDERING
440 && request.getScaleFactor() == getScaleFactor()) {
441 mPartialBitmap = request.getBitmap();
443 needsCheckModification = true;
445 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) {
446 mHighresBitmap = request.getBitmap();
448 needsCheckModification = true;
450 if (needsCheckModification) {
451 mActivity.enableSave(hasModifications());
455 public static void reset() {
459 public void addGeometryListener(GeometryListener listener) {
460 mGeometryListeners.add(listener);
463 public void notifyGeometryChange() {
464 if (mHandler.hasMessages(NEW_GEOMETRY)) {
467 mHandler.sendEmptyMessage(NEW_GEOMETRY);
470 public void hasNewGeometry() {
472 for (GeometryListener listener : mGeometryListeners) {
473 listener.geometryChanged();
478 public float getScaleFactor() {
482 public void setScaleFactor(float scaleFactor) {
486 if (scaleFactor == mScaleFactor) {
489 mScaleFactor = scaleFactor;
490 invalidatePartialPreview();
493 public Point getTranslation() {
497 public void setTranslation(Point translation) {
503 mTranslation.x = translation.x;
504 mTranslation.y = translation.y;
505 needsUpdatePartialPreview();
508 public Point getOriginalTranslation() {
509 return mOriginalTranslation;
512 public void setOriginalTranslation(Point originalTranslation) {
516 mOriginalTranslation.x = originalTranslation.x;
517 mOriginalTranslation.y = originalTranslation.y;
520 public void resetTranslation() {
523 needsUpdatePartialPreview();
526 public Bitmap getThumbnailBitmap() {
527 return mLoader.getOriginalBitmapSmall();
530 public Bitmap getLargeThumbnailBitmap() {
531 return mLoader.getOriginalBitmapLarge();
534 public float getMaxScaleFactor() {
538 return mMaxScaleFactor;
541 public void setMaxScaleFactor(float maxScaleFactor) {
542 mMaxScaleFactor = maxScaleFactor;
545 public boolean supportsHighRes() {
546 return mSupportsHighRes;
549 public void setShowsOriginal(boolean value) {
550 mShowsOriginal = value;
554 public boolean showsOriginal() {
555 return mShowsOriginal;
558 public void setLoadedPreset(ImagePreset preset) {
559 mLoadedPreset = preset;
562 public ImagePreset getLoadedPreset() {
563 return mLoadedPreset;