| /* |
| * 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.*; |
| import android.os.Handler; |
| import android.os.Message; |
| |
| import com.android.gallery3d.filtershow.FilterShowActivity; |
| import com.android.gallery3d.filtershow.HistoryAdapter; |
| import com.android.gallery3d.filtershow.cache.*; |
| import com.android.gallery3d.filtershow.filters.FilterRepresentation; |
| import com.android.gallery3d.filtershow.filters.ImageFilter; |
| import com.android.gallery3d.filtershow.presets.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 = true; |
| private static MasterImage sMasterImage = null; |
| private static int sIconSeedSize = 128; |
| private static float sHistoryPreviewSize = 128.0f; |
| |
| private boolean mSupportsHighRes = false; |
| |
| private ImageFilter mCurrentFilter = null; |
| private ImagePreset mPreset = null; |
| private ImagePreset mGeometryOnlyPreset = null; |
| private ImagePreset mFiltersOnlyPreset = null; |
| |
| private TripleBufferBitmap mFilteredPreview = new TripleBufferBitmap(); |
| |
| private Bitmap mGeometryOnlyBitmap = null; |
| private Bitmap mFiltersOnlyBitmap = null; |
| private Bitmap mPartialBitmap = null; |
| private Bitmap mHighresBitmap = null; |
| |
| private ImageLoader mLoader = null; |
| private HistoryAdapter mHistory = null; |
| private StateAdapter mState = null; |
| |
| private FilterShowActivity mActivity = null; |
| |
| private Vector<ImageShow> mObservers = new Vector<ImageShow>(); |
| private FilterRepresentation mCurrentFilterRepresentation; |
| private Vector<GeometryListener> mGeometryListeners = new Vector<GeometryListener>(); |
| |
| 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 static void setIconSeedSize(int iconSeedSize) { |
| sIconSeedSize = iconSeedSize; |
| } |
| |
| 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, boolean addToHistory) { |
| mPreset = preset; |
| mPreset.setImageLoader(mLoader); |
| setGeometry(); |
| mPreset.fillImageStateAdapter(mState); |
| if (addToHistory) { |
| mHistory.addHistoryItem(mPreset); |
| } |
| updatePresets(true); |
| GeometryMetadata geo = mPreset.mGeoData; |
| if (!geo.equals(mPreviousGeometry)) { |
| notifyGeometryChange(); |
| } |
| mPreviousGeometry = new GeometryMetadata(geo); |
| } |
| |
| private void renderHistoryPreview() { |
| ImagePreset historyPreset = mPreset; |
| if (historyPreset != null) { |
| Bitmap preview = mLoader.getOriginalBitmapSmall(); |
| if (preview != null) { |
| float s = Math.min(preview.getWidth(), preview.getHeight()); |
| float f = sHistoryPreviewSize / s; |
| int w = (int) (preview.getWidth() * f); |
| int h = (int) (preview.getHeight() * f); |
| Bitmap historyPreview = Bitmap.createScaledBitmap(preview, w, h, true); |
| historyPreset.setPreviewImage(historyPreview); |
| RenderingRequest.post(historyPreview, |
| historyPreset, RenderingRequest.ICON_RENDERING, this); |
| } |
| } |
| } |
| |
| private void setGeometry() { |
| Bitmap image = mLoader.getOriginalBitmapLarge(); |
| if (image == null) { |
| return; |
| } |
| float w = image.getWidth(); |
| float h = image.getHeight(); |
| GeometryMetadata geo = mPreset.mGeoData; |
| 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); |
| } |
| |
| public void onHistoryItemClick(int position) { |
| setPreset(new ImagePreset(mHistory.getItem(position)), false); |
| // We need a copy from the history |
| mHistory.setCurrentPreset(position); |
| } |
| |
| public HistoryAdapter getHistory() { |
| return mHistory; |
| } |
| |
| public StateAdapter getState() { |
| return mState; |
| } |
| |
| public void setHistoryAdapter(HistoryAdapter 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() { |
| if (mPreset == null) { |
| return false; |
| } |
| return mPreset.hasModifications(); |
| } |
| |
| public TripleBufferBitmap getDoubleBuffer() { |
| return mFilteredPreview; |
| } |
| |
| public void setOriginalGeometry(Bitmap originalBitmapLarge) { |
| GeometryMetadata geo = getPreset().mGeoData; |
| 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() { |
| return mFilteredPreview.getConsumer(); |
| } |
| |
| 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(); |
| mActivity.enableSave(hasModifications()); |
| } |
| |
| 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() { |
| mFilteredPreview.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.mGeoData; |
| 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; |
| } |
| if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { |
| mGeometryOnlyBitmap = request.getBitmap(); |
| } |
| if (request.getType() == RenderingRequest.FILTERS_RENDERING) { |
| mFiltersOnlyBitmap = request.getBitmap(); |
| } |
| if (request.getType() == RenderingRequest.PARTIAL_RENDERING |
| && request.getScaleFactor() == getScaleFactor()) { |
| mPartialBitmap = request.getBitmap(); |
| notifyObservers(); |
| } |
| if (request.getType() == RenderingRequest.HIGHRES_RENDERING) { |
| mHighresBitmap = request.getBitmap(); |
| notifyObservers(); |
| } |
| |
| if (request.getType() == RenderingRequest.ICON_RENDERING) { |
| // History preview images |
| ImagePreset preset = request.getOriginalImagePreset(); |
| preset.setPreviewImage(request.getBitmap()); |
| mHistory.notifyDataSetChanged(); |
| } |
| } |
| |
| 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; |
| } |
| } |