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 17 package com.android.gallery3d.filtershow.imageshow; 18 19 import android.graphics.*; 20 import android.os.Handler; 21 import android.os.Message; 22 23 import com.android.gallery3d.filtershow.FilterShowActivity; 24 import com.android.gallery3d.filtershow.HistoryAdapter; 25 import com.android.gallery3d.filtershow.cache.*; 26 import com.android.gallery3d.filtershow.filters.FilterRepresentation; 27 import com.android.gallery3d.filtershow.filters.ImageFilter; 28 import com.android.gallery3d.filtershow.presets.ImagePreset; 29 import com.android.gallery3d.filtershow.state.StateAdapter; 30 31 import java.util.Vector; 32 33 public class MasterImage implements RenderingRequestCaller { 34 35 private static final String LOGTAG = "MasterImage"; 36 private boolean DEBUG = false; 37 private static final boolean DISABLEZOOM = true; 38 private static MasterImage sMasterImage = null; 39 private static int sIconSeedSize = 128; 40 private static float sHistoryPreviewSize = 128.0f; 41 42 private boolean mSupportsHighRes = false; 43 44 private ImageFilter mCurrentFilter = null; 45 private ImagePreset mPreset = null; 46 private ImagePreset mGeometryOnlyPreset = null; 47 private ImagePreset mFiltersOnlyPreset = null; 48 49 private TripleBufferBitmap mFilteredPreview = new TripleBufferBitmap(); 50 51 private Bitmap mGeometryOnlyBitmap = null; 52 private Bitmap mFiltersOnlyBitmap = null; 53 private Bitmap mPartialBitmap = null; 54 private Bitmap mHighresBitmap = null; 55 56 private ImageLoader mLoader = null; 57 private HistoryAdapter mHistory = null; 58 private StateAdapter mState = null; 59 60 private FilterShowActivity mActivity = null; 61 62 private Vector<ImageShow> mObservers = new Vector<ImageShow>(); 63 private FilterRepresentation mCurrentFilterRepresentation; 64 private Vector<GeometryListener> mGeometryListeners = new Vector<GeometryListener>(); 65 66 private GeometryMetadata mPreviousGeometry = null; 67 68 private float mScaleFactor = 1.0f; 69 private float mMaxScaleFactor = 3.0f; // TODO: base this on the current view / image 70 private Point mTranslation = new Point(); 71 private Point mOriginalTranslation = new Point(); 72 73 private Point mImageShowSize = new Point(); 74 75 private boolean mShowsOriginal; 76 77 final private static int NEW_GEOMETRY = 1; 78 79 private final Handler mHandler = new Handler() { 80 @Override 81 public void handleMessage(Message msg) { 82 switch (msg.what) { 83 case NEW_GEOMETRY: { 84 hasNewGeometry(); 85 break; 86 } 87 } 88 } 89 }; 90 91 private MasterImage() { 92 } 93 94 // TODO: remove singleton 95 public static void setMaster(MasterImage master) { 96 sMasterImage = master; 97 } 98 99 public static MasterImage getImage() { 100 if (sMasterImage == null) { 101 sMasterImage = new MasterImage(); 102 } 103 return sMasterImage; 104 } 105 106 public void setSupportsHighRes(boolean value) { 107 mSupportsHighRes = value; 108 } 109 110 public static void setIconSeedSize(int iconSeedSize) { 111 sIconSeedSize = iconSeedSize; 112 } 113 114 public void addObserver(ImageShow observer) { 115 if (mObservers.contains(observer)) { 116 return; 117 } 118 mObservers.add(observer); 119 } 120 121 public void setActivity(FilterShowActivity activity) { 122 mActivity = activity; 123 } 124 125 public ImageLoader getLoader() { 126 return mLoader; 127 } 128 129 public synchronized ImagePreset getPreset() { 130 return mPreset; 131 } 132 133 public synchronized ImagePreset getGeometryPreset() { 134 return mGeometryOnlyPreset; 135 } 136 137 public synchronized ImagePreset getFiltersOnlyPreset() { 138 return mFiltersOnlyPreset; 139 } 140 141 public synchronized void setPreset(ImagePreset preset, boolean addToHistory) { 142 mPreset = preset; 143 mPreset.setImageLoader(mLoader); 144 setGeometry(); 145 mPreset.fillImageStateAdapter(mState); 146 if (addToHistory) { 147 mHistory.addHistoryItem(mPreset); 148 } 149 updatePresets(true); 150 GeometryMetadata geo = mPreset.mGeoData; 151 if (!geo.equals(mPreviousGeometry)) { 152 notifyGeometryChange(); 153 } 154 mPreviousGeometry = new GeometryMetadata(geo); 155 } 156 157 private void renderHistoryPreview() { 158 ImagePreset historyPreset = mPreset; 159 if (historyPreset != null) { 160 Bitmap preview = mLoader.getOriginalBitmapSmall(); 161 if (preview != null) { 162 float s = Math.min(preview.getWidth(), preview.getHeight()); 163 float f = sHistoryPreviewSize / s; 164 int w = (int) (preview.getWidth() * f); 165 int h = (int) (preview.getHeight() * f); 166 Bitmap historyPreview = Bitmap.createScaledBitmap(preview, w, h, true); 167 historyPreset.setPreviewImage(historyPreview); 168 RenderingRequest.post(historyPreview, 169 historyPreset, RenderingRequest.ICON_RENDERING, this); 170 } 171 } 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.mGeoData; 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 } 190 191 public void onHistoryItemClick(int position) { 192 setPreset(new ImagePreset(mHistory.getItem(position)), false); 193 // We need a copy from the history 194 mHistory.setCurrentPreset(position); 195 } 196 197 public HistoryAdapter getHistory() { 198 return mHistory; 199 } 200 201 public StateAdapter getState() { 202 return mState; 203 } 204 205 public void setHistoryAdapter(HistoryAdapter adapter) { 206 mHistory = adapter; 207 } 208 209 public void setStateAdapter(StateAdapter adapter) { 210 mState = adapter; 211 } 212 213 public void setImageLoader(ImageLoader loader) { 214 mLoader = loader; 215 } 216 217 public ImageLoader getImageLoader() { 218 return mLoader; 219 } 220 221 public void setCurrentFilter(ImageFilter filter) { 222 mCurrentFilter = filter; 223 } 224 225 public ImageFilter getCurrentFilter() { 226 return mCurrentFilter; 227 } 228 229 public synchronized boolean hasModifications() { 230 if (mPreset == null) { 231 return false; 232 } 233 return mPreset.hasModifications(); 234 } 235 236 public TripleBufferBitmap getDoubleBuffer() { 237 return mFilteredPreview; 238 } 239 240 public void setOriginalGeometry(Bitmap originalBitmapLarge) { 241 GeometryMetadata geo = getPreset().mGeoData; 242 float w = originalBitmapLarge.getWidth(); 243 float h = originalBitmapLarge.getHeight(); 244 RectF r = new RectF(0, 0, w, h); 245 geo.setPhotoBounds(r); 246 geo.setCropBounds(r); 247 getPreset().setGeometry(geo); 248 } 249 250 public Bitmap getFilteredImage() { 251 return mFilteredPreview.getConsumer(); 252 } 253 254 public Bitmap getFiltersOnlyImage() { 255 return mFiltersOnlyBitmap; 256 } 257 258 public Bitmap getGeometryOnlyImage() { 259 return mGeometryOnlyBitmap; 260 } 261 262 public Bitmap getPartialImage() { 263 return mPartialBitmap; 264 } 265 266 public Bitmap getHighresImage() { 267 return mHighresBitmap; 268 } 269 270 public void notifyObservers() { 271 for (ImageShow observer : mObservers) { 272 observer.invalidate(); 273 } 274 } 275 276 public void updatePresets(boolean force) { 277 if (force || mGeometryOnlyPreset == null) { 278 ImagePreset newPreset = new ImagePreset(mPreset); 279 newPreset.setDoApplyFilters(false); 280 newPreset.setDoApplyGeometry(true); 281 if (force || mGeometryOnlyPreset == null 282 || !newPreset.same(mGeometryOnlyPreset)) { 283 mGeometryOnlyPreset = newPreset; 284 RenderingRequest.post(mLoader.getOriginalBitmapLarge(), 285 mGeometryOnlyPreset, RenderingRequest.GEOMETRY_RENDERING, this); 286 } 287 } 288 if (force || mFiltersOnlyPreset == null) { 289 ImagePreset newPreset = new ImagePreset(mPreset); 290 newPreset.setDoApplyFilters(true); 291 newPreset.setDoApplyGeometry(false); 292 if (force || mFiltersOnlyPreset == null 293 || !newPreset.same(mFiltersOnlyPreset)) { 294 mFiltersOnlyPreset = newPreset; 295 RenderingRequest.post(mLoader.getOriginalBitmapLarge(), 296 mFiltersOnlyPreset, RenderingRequest.FILTERS_RENDERING, this); 297 } 298 } 299 invalidatePreview(); 300 mActivity.enableSave(hasModifications()); 301 } 302 303 public FilterRepresentation getCurrentFilterRepresentation() { 304 return mCurrentFilterRepresentation; 305 } 306 307 public void setCurrentFilterRepresentation(FilterRepresentation currentFilterRepresentation) { 308 mCurrentFilterRepresentation = currentFilterRepresentation; 309 } 310 311 public void invalidateFiltersOnly() { 312 mFiltersOnlyPreset = null; 313 updatePresets(false); 314 } 315 316 public void invalidatePartialPreview() { 317 if (mPartialBitmap != null) { 318 mPartialBitmap = null; 319 notifyObservers(); 320 } 321 } 322 323 public void invalidateHighresPreview() { 324 if (mHighresBitmap != null) { 325 mHighresBitmap = null; 326 notifyObservers(); 327 } 328 } 329 330 public void invalidatePreview() { 331 mFilteredPreview.invalidate(); 332 invalidatePartialPreview(); 333 invalidateHighresPreview(); 334 needsUpdatePartialPreview(); 335 needsUpdateHighResPreview(); 336 FilteringPipeline.getPipeline().updatePreviewBuffer(); 337 } 338 339 public void setImageShowSize(int w, int h) { 340 if (mImageShowSize.x != w || mImageShowSize.y != h) { 341 mImageShowSize.set(w, h); 342 needsUpdatePartialPreview(); 343 needsUpdateHighResPreview(); 344 } 345 } 346 347 private Matrix getImageToScreenMatrix(boolean reflectRotation) { 348 GeometryMetadata geo = mPreset.mGeoData; 349 if (geo == null || mLoader == null 350 || mLoader.getOriginalBounds() == null 351 || mImageShowSize.x == 0) { 352 return new Matrix(); 353 } 354 Matrix m = geo.getOriginalToScreen(reflectRotation, 355 mLoader.getOriginalBounds().width(), 356 mLoader.getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y); 357 Point translate = getTranslation(); 358 float scaleFactor = getScaleFactor(); 359 m.postTranslate(translate.x, translate.y); 360 m.postScale(scaleFactor, scaleFactor, mImageShowSize.x/2.0f, mImageShowSize.y/2.0f); 361 return m; 362 } 363 364 private Matrix getScreenToImageMatrix(boolean reflectRotation) { 365 Matrix m = getImageToScreenMatrix(reflectRotation); 366 Matrix invert = new Matrix(); 367 m.invert(invert); 368 return invert; 369 } 370 371 public void needsUpdateHighResPreview() { 372 if (!mSupportsHighRes) { 373 return; 374 } 375 RenderingRequest.post(null, mPreset, RenderingRequest.HIGHRES_RENDERING, this); 376 invalidateHighresPreview(); 377 } 378 379 public void needsUpdatePartialPreview() { 380 if (!mPreset.canDoPartialRendering()) { 381 invalidatePartialPreview(); 382 return; 383 } 384 Matrix m = getScreenToImageMatrix(true); 385 RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y); 386 RectF dest = new RectF(); 387 m.mapRect(dest, r); 388 Rect bounds = new Rect(); 389 dest.roundOut(bounds); 390 RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING, 391 this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y)); 392 invalidatePartialPreview(); 393 } 394 395 @Override 396 public void available(RenderingRequest request) { 397 if (request.getBitmap() == null) { 398 return; 399 } 400 if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) { 401 mGeometryOnlyBitmap = request.getBitmap(); 402 } 403 if (request.getType() == RenderingRequest.FILTERS_RENDERING) { 404 mFiltersOnlyBitmap = request.getBitmap(); 405 } 406 if (request.getType() == RenderingRequest.PARTIAL_RENDERING 407 && request.getScaleFactor() == getScaleFactor()) { 408 mPartialBitmap = request.getBitmap(); 409 notifyObservers(); 410 } 411 if (request.getType() == RenderingRequest.HIGHRES_RENDERING) { 412 mHighresBitmap = request.getBitmap(); 413 notifyObservers(); 414 } 415 416 if (request.getType() == RenderingRequest.ICON_RENDERING) { 417 // History preview images 418 ImagePreset preset = request.getOriginalImagePreset(); 419 preset.setPreviewImage(request.getBitmap()); 420 mHistory.notifyDataSetChanged(); 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