MasterImage.java revision d2e9ea0ce1949e192e80ce806e702606694440ac
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17package com.android.gallery3d.filtershow.imageshow; 18 19import android.graphics.Bitmap; 20import android.graphics.Matrix; 21import android.graphics.Point; 22import android.graphics.Rect; 23import android.graphics.RectF; 24import android.os.Handler; 25import android.os.Message; 26 27import com.android.gallery3d.filtershow.FilterShowActivity; 28import com.android.gallery3d.filtershow.history.HistoryAdapter; 29import com.android.gallery3d.filtershow.history.HistoryItem; 30import com.android.gallery3d.filtershow.cache.FilteringPipeline; 31import com.android.gallery3d.filtershow.cache.ImageLoader; 32import com.android.gallery3d.filtershow.cache.RenderingRequest; 33import com.android.gallery3d.filtershow.cache.RenderingRequestCaller; 34import com.android.gallery3d.filtershow.cache.TripleBufferBitmap; 35import com.android.gallery3d.filtershow.filters.FilterRepresentation; 36import com.android.gallery3d.filtershow.filters.ImageFilter; 37import com.android.gallery3d.filtershow.presets.ImagePreset; 38import com.android.gallery3d.filtershow.state.StateAdapter; 39 40import java.util.Vector; 41 42public class MasterImage implements RenderingRequestCaller { 43 44 private static final String LOGTAG = "MasterImage"; 45 private boolean DEBUG = false; 46 private static final boolean DISABLEZOOM = false; 47 private static MasterImage sMasterImage = null; 48 private static int sIconSeedSize = 128; 49 private static float sHistoryPreviewSize = 128.0f; 50 51 private boolean mSupportsHighRes = false; 52 53 private ImageFilter mCurrentFilter = null; 54 private ImagePreset mPreset = null; 55 private ImagePreset mLoadedPreset = null; 56 private ImagePreset mGeometryOnlyPreset = null; 57 private ImagePreset mFiltersOnlyPreset = null; 58 59 private TripleBufferBitmap mFilteredPreview = new TripleBufferBitmap(); 60 61 private Bitmap mGeometryOnlyBitmap = null; 62 private Bitmap mFiltersOnlyBitmap = null; 63 private Bitmap mPartialBitmap = null; 64 private Bitmap mHighresBitmap = null; 65 66 private ImageLoader mLoader = null; 67 private HistoryAdapter mHistory = null; 68 private StateAdapter mState = null; 69 70 private FilterShowActivity mActivity = null; 71 72 private Vector<ImageShow> mObservers = new Vector<ImageShow>(); 73 private FilterRepresentation mCurrentFilterRepresentation; 74 private Vector<GeometryListener> mGeometryListeners = new Vector<GeometryListener>(); 75 76 private GeometryMetadata mPreviousGeometry = null; 77 78 private float mScaleFactor = 1.0f; 79 private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image 80 private Point mTranslation = new Point(); 81 private Point mOriginalTranslation = new Point(); 82 83 private Point mImageShowSize = new Point(); 84 85 private boolean mShowsOriginal; 86 87 final private static int NEW_GEOMETRY = 1; 88 89 private final Handler mHandler = new Handler() { 90 @Override 91 public void handleMessage(Message msg) { 92 switch (msg.what) { 93 case NEW_GEOMETRY: { 94 hasNewGeometry(); 95 break; 96 } 97 } 98 } 99 }; 100 101 private MasterImage() { 102 } 103 104 // TODO: remove singleton 105 public static void setMaster(MasterImage master) { 106 sMasterImage = master; 107 } 108 109 public static MasterImage getImage() { 110 if (sMasterImage == null) { 111 sMasterImage = new MasterImage(); 112 } 113 return sMasterImage; 114 } 115 116 public void setSupportsHighRes(boolean value) { 117 mSupportsHighRes = value; 118 } 119 120 public static void setIconSeedSize(int iconSeedSize) { 121 sIconSeedSize = iconSeedSize; 122 } 123 124 public void addObserver(ImageShow observer) { 125 if (mObservers.contains(observer)) { 126 return; 127 } 128 mObservers.add(observer); 129 } 130 131 public void setActivity(FilterShowActivity activity) { 132 mActivity = activity; 133 } 134 135 public ImageLoader getLoader() { 136 return mLoader; 137 } 138 139 public synchronized ImagePreset getPreset() { 140 return mPreset; 141 } 142 143 public synchronized ImagePreset getGeometryPreset() { 144 return mGeometryOnlyPreset; 145 } 146 147 public synchronized ImagePreset getFiltersOnlyPreset() { 148 return mFiltersOnlyPreset; 149 } 150 151 public synchronized void setPreset(ImagePreset preset, 152 FilterRepresentation change, 153 boolean addToHistory) { 154 if (DEBUG) { 155 preset.showFilters(); 156 } 157 mPreset = preset; 158 mPreset.setImageLoader(mLoader); 159 setGeometry(); 160 mPreset.fillImageStateAdapter(mState); 161 if (addToHistory) { 162 HistoryItem historyItem = new HistoryItem(mPreset, change); 163 mHistory.addHistoryItem(historyItem); 164 } 165 updatePresets(true); 166 GeometryMetadata geo = mPreset.getGeometry(); 167 if (!geo.equals(mPreviousGeometry)) { 168 notifyGeometryChange(); 169 } 170 mPreviousGeometry = new GeometryMetadata(geo); 171 mActivity.updateCategories(); 172 } 173 174 private void setGeometry() { 175 Bitmap image = mLoader.getOriginalBitmapLarge(); 176 if (image == null) { 177 return; 178 } 179 float w = image.getWidth(); 180 float h = image.getHeight(); 181 GeometryMetadata geo = mPreset.getGeometry(); 182 RectF pb = geo.getPhotoBounds(); 183 if (w == pb.width() && h == pb.height()) { 184 return; 185 } 186 RectF r = new RectF(0, 0, w, h); 187 geo.setPhotoBounds(r); 188 geo.setCropBounds(r); 189 mPreset.setGeometry(geo); 190 } 191 192 public void onHistoryItemClick(int position) { 193 HistoryItem historyItem = mHistory.getItem(position); 194 // We need a copy from the history 195 ImagePreset newPreset = new ImagePreset(historyItem.getImagePreset()); 196 // don't need to add it to the history 197 setPreset(newPreset, historyItem.getFilterRepresentation(), false); 198 mHistory.setCurrentPreset(position); 199 } 200 201 public HistoryAdapter getHistory() { 202 return mHistory; 203 } 204 205 public StateAdapter getState() { 206 return mState; 207 } 208 209 public void setHistoryAdapter(HistoryAdapter adapter) { 210 mHistory = adapter; 211 } 212 213 public void setStateAdapter(StateAdapter adapter) { 214 mState = adapter; 215 } 216 217 public void setImageLoader(ImageLoader loader) { 218 mLoader = loader; 219 } 220 221 public ImageLoader getImageLoader() { 222 return mLoader; 223 } 224 225 public void setCurrentFilter(ImageFilter filter) { 226 mCurrentFilter = filter; 227 } 228 229 public ImageFilter getCurrentFilter() { 230 return mCurrentFilter; 231 } 232 233 public synchronized boolean hasModifications() { 234 if (mPreset == null) { 235 return getLoadedPreset() != null; 236 } else { 237 // TODO: same() is quite strict check here. We should be only 238 // checking for functionality parity. 239 return !mPreset.same(getLoadedPreset()); 240 } 241 } 242 243 public TripleBufferBitmap getDoubleBuffer() { 244 return mFilteredPreview; 245 } 246 247 public void setOriginalGeometry(Bitmap originalBitmapLarge) { 248 GeometryMetadata geo = getPreset().getGeometry(); 249 float w = originalBitmapLarge.getWidth(); 250 float h = originalBitmapLarge.getHeight(); 251 RectF r = new RectF(0, 0, w, h); 252 geo.setPhotoBounds(r); 253 geo.setCropBounds(r); 254 getPreset().setGeometry(geo); 255 } 256 257 public Bitmap getFilteredImage() { 258 return mFilteredPreview.getConsumer(); 259 } 260 261 public Bitmap getFiltersOnlyImage() { 262 return mFiltersOnlyBitmap; 263 } 264 265 public Bitmap getGeometryOnlyImage() { 266 return mGeometryOnlyBitmap; 267 } 268 269 public Bitmap getPartialImage() { 270 return mPartialBitmap; 271 } 272 273 public Bitmap getHighresImage() { 274 return mHighresBitmap; 275 } 276 277 public void notifyObservers() { 278 for (ImageShow observer : mObservers) { 279 observer.invalidate(); 280 } 281 } 282 283 public void updatePresets(boolean force) { 284 if (force || mGeometryOnlyPreset == null) { 285 ImagePreset newPreset = new ImagePreset(mPreset); 286 newPreset.setDoApplyFilters(false); 287 newPreset.setDoApplyGeometry(true); 288 if (force || mGeometryOnlyPreset == null 289 || !newPreset.same(mGeometryOnlyPreset)) { 290 mGeometryOnlyPreset = newPreset; 291 RenderingRequest.post(mLoader.getOriginalBitmapLarge(), 292 mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this); 293 } 294 } 295 if (force || mFiltersOnlyPreset == null) { 296 ImagePreset newPreset = new ImagePreset(mPreset); 297 newPreset.setDoApplyFilters(true); 298 newPreset.setDoApplyGeometry(false); 299 if (force || mFiltersOnlyPreset == null 300 || !newPreset.same(mFiltersOnlyPreset)) { 301 mFiltersOnlyPreset = newPreset; 302 RenderingRequest.post(mLoader.getOriginalBitmapLarge(), 303 mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this); 304 } 305 } 306 invalidatePreview(); 307 mActivity.enableSave(hasModifications()); 308 } 309 310 public FilterRepresentation getCurrentFilterRepresentation() { 311 return mCurrentFilterRepresentation; 312 } 313 314 public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) { 315 mCurrentFilterRepresentation = currentFilterRepresentation; 316 } 317 318 public void invalidateFiltersOnly() { 319 mFiltersOnlyPreset = null; 320 updatePresets(false); 321 } 322 323 public void invalidatePartialPreview() { 324 if (mPartialBitmap != null) { 325 mPartialBitmap = null; 326 notifyObservers(); 327 } 328 } 329 330 public void invalidateHighresPreview() { 331 if (mHighresBitmap != null) { 332 mHighresBitmap = null; 333 notifyObservers(); 334 } 335 } 336 337 public void invalidatePreview() { 338 mFilteredPreview.invalidate(); 339 invalidatePartialPreview(); 340 invalidateHighresPreview(); 341 needsUpdatePartialPreview(); 342 needsUpdateHighResPreview(); 343 FilteringPipeline.getPipeline().updatePreviewBuffer(); 344 } 345 346 public void setImageShowSize(int w, int h) { 347 if (mImageShowSize.x != w || mImageShowSize.y != h) { 348 mImageShowSize.set(w, h); 349 needsUpdatePartialPreview(); 350 needsUpdateHighResPreview(); 351 } 352 } 353 354 private Matrix getImageToScreenMatrix(boolean reflectRotation) { 355 GeometryMetadata geo = mPreset.getGeometry(); 356 if (geo == null || mLoader == null 357 || mLoader.getOriginalBounds() == null 358 || mImageShowSize.x == 0) { 359 return new Matrix(); 360 } 361 Matrix m = geo.getOriginalToScreen(reflectRotation, 362 mLoader.getOriginalBounds().width(), 363 mLoader.getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y); 364 Point translate = getTranslation(); 365 float scaleFactor = getScaleFactor(); 366 m.postTranslate(translate.x, translate.y); 367 m.postScale(scaleFactor, scaleFactor, mImageShowSize.x/2.0f, mImageShowSize.y/2.0f); 368 return m; 369 } 370 371 private Matrix getScreenToImageMatrix(boolean reflectRotation) { 372 Matrix m = getImageToScreenMatrix(reflectRotation); 373 Matrix invert = new Matrix(); 374 m.invert(invert); 375 return invert; 376 } 377 378 public void needsUpdateHighResPreview() { 379 if (!mSupportsHighRes) { 380 return; 381 } 382 RenderingRequest.post(null, mPreset, RenderingRequest.HIGHRES_RENDERING, this); 383 invalidateHighresPreview(); 384 } 385 386 public void needsUpdatePartialPreview() { 387 if (!mPreset.canDoPartialRendering()) { 388 invalidatePartialPreview(); 389 return; 390 } 391 Matrix m = getScreenToImageMatrix(true); 392 RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y); 393 RectF dest = new RectF(); 394 m.mapRect(dest, r); 395 Rect bounds = new Rect(); 396 dest.roundOut(bounds); 397 RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING, 398 this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y)); 399 invalidatePartialPreview(); 400 } 401 402 @Override 403 public void available(RenderingRequest request) { 404 if (request.getBitmap() == null) { 405 return; 406 } 407 if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { 408 mGeometryOnlyBitmap = request.getBitmap(); 409 } 410 if (request.getType() == RenderingRequest.FILTERS_RENDERING) { 411 mFiltersOnlyBitmap = request.getBitmap(); 412 } 413 if (request.getType() == RenderingRequest.PARTIAL_RENDERING 414 && request.getScaleFactor() == getScaleFactor()) { 415 mPartialBitmap = request.getBitmap(); 416 notifyObservers(); 417 } 418 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) { 419 mHighresBitmap = request.getBitmap(); 420 notifyObservers(); 421 } 422 } 423 424 public static void reset() { 425 sMasterImage = null; 426 } 427 428 public void addGeometryListener(GeometryListener listener) { 429 mGeometryListeners.add(listener); 430 } 431 432 public void notifyGeometryChange() { 433 if (mHandler.hasMessages(NEW_GEOMETRY)) { 434 return; 435 } 436 mHandler.sendEmptyMessage(NEW_GEOMETRY); 437 } 438 439 public void hasNewGeometry() { 440 updatePresets(true); 441 for (GeometryListener listener : mGeometryListeners) { 442 listener.geometryChanged(); 443 } 444 } 445 446 447 public float getScaleFactor() { 448 return mScaleFactor; 449 } 450 451 public void setScaleFactor(float scaleFactor) { 452 if (DISABLEZOOM) { 453 return; 454 } 455 if (scaleFactor == mScaleFactor) { 456 return; 457 } 458 mScaleFactor = scaleFactor; 459 invalidatePartialPreview(); 460 } 461 462 public Point getTranslation() { 463 return mTranslation; 464 } 465 466 public void setTranslation(Point translation) { 467 if (DISABLEZOOM) { 468 mTranslation.x = 0; 469 mTranslation.y = 0; 470 return; 471 } 472 mTranslation.x = translation.x; 473 mTranslation.y = translation.y; 474 needsUpdatePartialPreview(); 475 } 476 477 public Point getOriginalTranslation() { 478 return mOriginalTranslation; 479 } 480 481 public void setOriginalTranslation(Point originalTranslation) { 482 if (DISABLEZOOM) { 483 return; 484 } 485 mOriginalTranslation.x = originalTranslation.x; 486 mOriginalTranslation.y = originalTranslation.y; 487 } 488 489 public void resetTranslation() { 490 mTranslation.x = 0; 491 mTranslation.y = 0; 492 needsUpdatePartialPreview(); 493 } 494 495 public Bitmap getThumbnailBitmap() { 496 return mLoader.getOriginalBitmapSmall(); 497 } 498 499 public Bitmap getLargeThumbnailBitmap() { 500 return mLoader.getOriginalBitmapLarge(); 501 } 502 503 public float getMaxScaleFactor() { 504 if (DISABLEZOOM) { 505 return 1; 506 } 507 return mMaxScaleFactor; 508 } 509 510 public void setMaxScaleFactor(float maxScaleFactor) { 511 mMaxScaleFactor = maxScaleFactor; 512 } 513 514 public boolean supportsHighRes() { 515 return mSupportsHighRes; 516 } 517 518 public void setShowsOriginal(boolean value) { 519 mShowsOriginal = value; 520 notifyObservers(); 521 } 522 523 public boolean showsOriginal() { 524 return mShowsOriginal; 525 } 526 527 public void setLoadedPreset(ImagePreset preset) { 528 mLoadedPreset = preset; 529 } 530 531 public ImagePreset getLoadedPreset() { 532 return mLoadedPreset; 533 } 534} 535