/* * Copyright (C) 2013 The Android Open Source Project * * Licensed 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 com.android.gallery3d.filtershow.imageshow; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.os.Handler; import android.os.Message; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.history.HistoryManager; import com.android.gallery3d.filtershow.history.HistoryItem; import com.android.gallery3d.filtershow.pipeline.FilteringPipeline; import com.android.gallery3d.filtershow.cache.ImageLoader; import com.android.gallery3d.filtershow.pipeline.RenderingRequest; import com.android.gallery3d.filtershow.pipeline.RenderingRequestCaller; import com.android.gallery3d.filtershow.filters.FilterRepresentation; import com.android.gallery3d.filtershow.filters.ImageFilter; import com.android.gallery3d.filtershow.pipeline.Buffer; import com.android.gallery3d.filtershow.pipeline.SharedBuffer; import com.android.gallery3d.filtershow.pipeline.SharedPreset; import com.android.gallery3d.filtershow.pipeline.ImagePreset; import com.android.gallery3d.filtershow.state.StateAdapter; import java.util.Vector; public class MasterImage implements RenderingRequestCaller { private static final String LOGTAG = "MasterImage"; private boolean DEBUG = false; private static final boolean DISABLEZOOM = false; private static MasterImage sMasterImage = null; private boolean mSupportsHighRes = false; private ImageFilter mCurrentFilter = null; private ImagePreset mPreset = null; private ImagePreset mLoadedPreset = null; private ImagePreset mGeometryOnlyPreset = null; private ImagePreset mFiltersOnlyPreset = null; private SharedBuffer mPreviewBuffer = new SharedBuffer(); private SharedPreset mPreviewPreset = new SharedPreset(); private Bitmap mGeometryOnlyBitmap = null; private Bitmap mFiltersOnlyBitmap = null; private Bitmap mPartialBitmap = null; private Bitmap mHighresBitmap = null; private ImageLoader mLoader = null; private HistoryManager mHistory = null; private StateAdapter mState = null; private FilterShowActivity mActivity = null; private Vector mObservers = new Vector(); private FilterRepresentation mCurrentFilterRepresentation; private Vector mGeometryListeners = new Vector(); private GeometryMetadata mPreviousGeometry = null; private float mScaleFactor = 1.0f; private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image private Point mTranslation = new Point(); private Point mOriginalTranslation = new Point(); private Point mImageShowSize = new Point(); private boolean mShowsOriginal; final private static int NEW_GEOMETRY = 1; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case NEW_GEOMETRY: { hasNewGeometry(); break; } } } }; private MasterImage() { } // TODO: remove singleton public static void setMaster(MasterImage master) { sMasterImage = master; } public static MasterImage getImage() { if (sMasterImage == null) { sMasterImage = new MasterImage(); } return sMasterImage; } public void setSupportsHighRes(boolean value) { mSupportsHighRes = value; } public void addObserver(ImageShow observer) { if (mObservers.contains(observer)) { return; } mObservers.add(observer); } public void setActivity(FilterShowActivity activity) { mActivity = activity; } public ImageLoader getLoader() { return mLoader; } public synchronized ImagePreset getPreset() { return mPreset; } public synchronized ImagePreset getGeometryPreset() { return mGeometryOnlyPreset; } public synchronized ImagePreset getFiltersOnlyPreset() { return mFiltersOnlyPreset; } public synchronized void setPreset(ImagePreset preset, FilterRepresentation change, boolean addToHistory) { if (DEBUG) { preset.showFilters(); } mPreset = preset; mPreset.setImageLoader(mLoader); setGeometry(); mPreset.fillImageStateAdapter(mState); if (addToHistory) { HistoryItem historyItem = new HistoryItem(mPreset, change); mHistory.addHistoryItem(historyItem); } updatePresets(true); GeometryMetadata geo = mPreset.getGeometry(); if (!geo.equals(mPreviousGeometry)) { notifyGeometryChange(); } mPreviousGeometry = new GeometryMetadata(geo); mActivity.updateCategories(); } private void setGeometry() { Bitmap image = mLoader.getOriginalBitmapLarge(); if (image == null) { return; } float w = image.getWidth(); float h = image.getHeight(); GeometryMetadata geo = mPreset.getGeometry(); RectF pb = geo.getPhotoBounds(); if (w == pb.width() && h == pb.height()) { return; } RectF r = new RectF(0, 0, w, h); geo.setPhotoBounds(r); geo.setCropBounds(r); mPreset.setGeometry(geo); } public void onHistoryItemClick(int position) { HistoryItem historyItem = mHistory.getItem(position); // We need a copy from the history ImagePreset newPreset = new ImagePreset(historyItem.getImagePreset()); // don't need to add it to the history setPreset(newPreset, historyItem.getFilterRepresentation(), false); mHistory.setCurrentPreset(position); } public HistoryManager getHistory() { return mHistory; } public StateAdapter getState() { return mState; } public void setHistoryManager(HistoryManager adapter) { mHistory = adapter; } public void setStateAdapter(StateAdapter adapter) { mState = adapter; } public void setImageLoader(ImageLoader loader) { mLoader = loader; } public ImageLoader getImageLoader() { return mLoader; } public void setCurrentFilter(ImageFilter filter) { mCurrentFilter = filter; } public ImageFilter getCurrentFilter() { return mCurrentFilter; } public synchronized boolean hasModifications() { // TODO: We need to have a better same effects check to see if two // presets are functionally the same. Right now, we are relying on a // stricter check as equals(). ImagePreset loadedPreset = getLoadedPreset(); if (mPreset == null) { if (loadedPreset == null) { return false; } else { return loadedPreset.hasModifications(); } } else { if (loadedPreset == null) { return mPreset.hasModifications(); } else { return !mPreset.equals(getLoadedPreset()); } } } public SharedBuffer getPreviewBuffer() { return mPreviewBuffer; } public SharedPreset getPreviewPreset() { return mPreviewPreset; } public void setOriginalGeometry(Bitmap originalBitmapLarge) { GeometryMetadata geo = getPreset().getGeometry(); float w = originalBitmapLarge.getWidth(); float h = originalBitmapLarge.getHeight(); RectF r = new RectF(0, 0, w, h); geo.setPhotoBounds(r); geo.setCropBounds(r); getPreset().setGeometry(geo); } public Bitmap getFilteredImage() { mPreviewBuffer.swapConsumerIfNeeded(); // get latest bitmap Buffer consumer = mPreviewBuffer.getConsumer(); if (consumer != null) { return consumer.getBitmap(); } return null; } public Bitmap getFiltersOnlyImage() { return mFiltersOnlyBitmap; } public Bitmap getGeometryOnlyImage() { return mGeometryOnlyBitmap; } public Bitmap getPartialImage() { return mPartialBitmap; } public Bitmap getHighresImage() { return mHighresBitmap; } public void notifyObservers() { for (ImageShow observer : mObservers) { observer.invalidate(); } } public void updatePresets(boolean force) { if (force || mGeometryOnlyPreset == null) { ImagePreset newPreset = new ImagePreset(mPreset); newPreset.setDoApplyFilters(false); newPreset.setDoApplyGeometry(true); if (force || mGeometryOnlyPreset == null || !newPreset.same(mGeometryOnlyPreset)) { mGeometryOnlyPreset = newPreset; RenderingRequest.post(mLoader.getOriginalBitmapLarge(), mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this); } } if (force || mFiltersOnlyPreset == null) { ImagePreset newPreset = new ImagePreset(mPreset); newPreset.setDoApplyFilters(true); newPreset.setDoApplyGeometry(false); if (force || mFiltersOnlyPreset == null || !newPreset.same(mFiltersOnlyPreset)) { mFiltersOnlyPreset = newPreset; RenderingRequest.post(mLoader.getOriginalBitmapLarge(), mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this); } } invalidatePreview(); } public FilterRepresentation getCurrentFilterRepresentation() { return mCurrentFilterRepresentation; } public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) { mCurrentFilterRepresentation = currentFilterRepresentation; } public void invalidateFiltersOnly() { mFiltersOnlyPreset = null; updatePresets(false); } public void invalidatePartialPreview() { if (mPartialBitmap != null) { mPartialBitmap = null; notifyObservers(); } } public void invalidateHighresPreview() { if (mHighresBitmap != null) { mHighresBitmap = null; notifyObservers(); } } public void invalidatePreview() { mPreviewPreset.enqueuePreset(mPreset); mPreviewBuffer.invalidate(); invalidatePartialPreview(); invalidateHighresPreview(); needsUpdatePartialPreview(); needsUpdateHighResPreview(); FilteringPipeline.getPipeline().updatePreviewBuffer(); } public void setImageShowSize(int w, int h) { if (mImageShowSize.x != w || mImageShowSize.y != h) { mImageShowSize.set(w, h); needsUpdatePartialPreview(); needsUpdateHighResPreview(); } } private Matrix getImageToScreenMatrix(boolean reflectRotation) { GeometryMetadata geo = mPreset.getGeometry(); if (geo == null || mLoader == null || mLoader.getOriginalBounds() == null || mImageShowSize.x == 0) { return new Matrix(); } Matrix m = geo.getOriginalToScreen(reflectRotation, mLoader.getOriginalBounds().width(), mLoader.getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y); Point translate = getTranslation(); float scaleFactor = getScaleFactor(); m.postTranslate(translate.x, translate.y); m.postScale(scaleFactor, scaleFactor, mImageShowSize.x / 2.0f, mImageShowSize.y / 2.0f); return m; } private Matrix getScreenToImageMatrix(boolean reflectRotation) { Matrix m = getImageToScreenMatrix(reflectRotation); Matrix invert = new Matrix(); m.invert(invert); return invert; } public void needsUpdateHighResPreview() { if (!mSupportsHighRes) { return; } RenderingRequest.post(null, mPreset, RenderingRequest.HIGHRES_RENDERING, this); invalidateHighresPreview(); } public void needsUpdatePartialPreview() { if (!mPreset.canDoPartialRendering()) { invalidatePartialPreview(); return; } Matrix m = getScreenToImageMatrix(true); RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y); RectF dest = new RectF(); m.mapRect(dest, r); Rect bounds = new Rect(); dest.roundOut(bounds); RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING, this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y)); invalidatePartialPreview(); } @Override public void available(RenderingRequest request) { if (request.getBitmap() == null) { return; } boolean needsCheckModification = false; if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { mGeometryOnlyBitmap = request.getBitmap(); needsCheckModification = true; } if (request.getType() == RenderingRequest.FILTERS_RENDERING) { mFiltersOnlyBitmap = request.getBitmap(); needsCheckModification = true; } if (request.getType() == RenderingRequest.PARTIAL_RENDERING && request.getScaleFactor() == getScaleFactor()) { mPartialBitmap = request.getBitmap(); notifyObservers(); needsCheckModification = true; } if (request.getType() == RenderingRequest.HIGHRES_RENDERING) { mHighresBitmap = request.getBitmap(); notifyObservers(); needsCheckModification = true; } if (needsCheckModification) { mActivity.enableSave(hasModifications()); } } public static void reset() { sMasterImage = null; } public void addGeometryListener(GeometryListener listener) { mGeometryListeners.add(listener); } public void notifyGeometryChange() { if (mHandler.hasMessages(NEW_GEOMETRY)) { return; } mHandler.sendEmptyMessage(NEW_GEOMETRY); } public void hasNewGeometry() { updatePresets(true); for (GeometryListener listener : mGeometryListeners) { listener.geometryChanged(); } } public float getScaleFactor() { return mScaleFactor; } public void setScaleFactor(float scaleFactor) { if (DISABLEZOOM) { return; } if (scaleFactor == mScaleFactor) { return; } mScaleFactor = scaleFactor; invalidatePartialPreview(); } public Point getTranslation() { return mTranslation; } public void setTranslation(Point translation) { if (DISABLEZOOM) { mTranslation.x = 0; mTranslation.y = 0; return; } mTranslation.x = translation.x; mTranslation.y = translation.y; needsUpdatePartialPreview(); } public Point getOriginalTranslation() { return mOriginalTranslation; } public void setOriginalTranslation(Point originalTranslation) { if (DISABLEZOOM) { return; } mOriginalTranslation.x = originalTranslation.x; mOriginalTranslation.y = originalTranslation.y; } public void resetTranslation() { mTranslation.x = 0; mTranslation.y = 0; needsUpdatePartialPreview(); } public Bitmap getThumbnailBitmap() { return mLoader.getOriginalBitmapSmall(); } public Bitmap getLargeThumbnailBitmap() { return mLoader.getOriginalBitmapLarge(); } public float getMaxScaleFactor() { if (DISABLEZOOM) { return 1; } return mMaxScaleFactor; } public void setMaxScaleFactor(float maxScaleFactor) { mMaxScaleFactor = maxScaleFactor; } public boolean supportsHighRes() { return mSupportsHighRes; } public void setShowsOriginal(boolean value) { mShowsOriginal = value; notifyObservers(); } public boolean showsOriginal() { return mShowsOriginal; } public void setLoadedPreset(ImagePreset preset) { mLoadedPreset = preset; } public ImagePreset getLoadedPreset() { return mLoadedPreset; } }