1 /* 2 * Copyright (C) 2012 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.presets; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Rect; 21 import android.support.v8.renderscript.Allocation; 22 import android.util.Log; 23 24 import com.android.gallery3d.filtershow.cache.CachingPipeline; 25 import com.android.gallery3d.filtershow.cache.ImageLoader; 26 import com.android.gallery3d.filtershow.filters.BaseFiltersManager; 27 import com.android.gallery3d.filtershow.filters.FilterRepresentation; 28 import com.android.gallery3d.filtershow.filters.ImageFilter; 29 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; 30 import com.android.gallery3d.filtershow.imageshow.MasterImage; 31 import com.android.gallery3d.filtershow.state.State; 32 import com.android.gallery3d.filtershow.state.StateAdapter; 33 import com.android.gallery3d.util.UsageStatistics; 34 35 import java.util.Vector; 36 37 public class ImagePreset { 38 39 private static final String LOGTAG = "ImagePreset"; 40 41 private FilterRepresentation mBorder = null; 42 public static final int QUALITY_ICON = 0; 43 public static final int QUALITY_PREVIEW = 1; 44 public static final int QUALITY_FINAL = 2; 45 public static final int STYLE_ICON = 3; 46 47 private ImageLoader mImageLoader = null; 48 49 private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>(); 50 51 protected String mName = "Original"; 52 private String mHistoryName = "Original"; 53 protected boolean mIsFxPreset = false; 54 55 private boolean mDoApplyGeometry = true; 56 private boolean mDoApplyFilters = true; 57 58 public final GeometryMetadata mGeoData = new GeometryMetadata(); 59 private boolean mPartialRendering = false; 60 private Rect mPartialRenderingBounds; 61 62 private Bitmap mPreviewImage; 63 64 public ImagePreset() { 65 setup(); 66 } 67 68 public ImagePreset(String historyName) { 69 setHistoryName(historyName); 70 setup(); 71 } 72 73 public ImagePreset(ImagePreset source, String historyName) { 74 this(source); 75 if (historyName != null) { 76 setHistoryName(historyName); 77 } 78 } 79 80 public ImagePreset(ImagePreset source) { 81 try { 82 if (source.mBorder != null) { 83 mBorder = source.mBorder.clone(); 84 } 85 for (int i = 0; i < source.mFilters.size(); i++) { 86 FilterRepresentation representation = source.mFilters.elementAt(i).clone(); 87 addFilter(representation); 88 } 89 } catch (java.lang.CloneNotSupportedException e) { 90 Log.v(LOGTAG, "Exception trying to clone: " + e); 91 } 92 mName = source.name(); 93 mHistoryName = source.name(); 94 mIsFxPreset = source.isFx(); 95 mImageLoader = source.getImageLoader(); 96 mPreviewImage = source.getPreviewImage(); 97 98 mGeoData.set(source.mGeoData); 99 } 100 101 public FilterRepresentation getFilterRepresentation(int position) { 102 FilterRepresentation representation = null; 103 try { 104 representation = mFilters.elementAt(position).clone(); 105 } catch (CloneNotSupportedException e) { 106 e.printStackTrace(); 107 } 108 return representation; 109 } 110 111 public int getPositionForRepresentation(FilterRepresentation representation) { 112 for (int i = 0; i < mFilters.size(); i++) { 113 if (mFilters.elementAt(i).getFilterClass() == representation.getFilterClass()) { 114 return i; 115 } 116 } 117 return -1; 118 } 119 120 public FilterRepresentation getFilterRepresentationCopyFrom(FilterRepresentation filterRepresentation) { 121 // TODO: add concept of position in the filters (to allow multiple instances) 122 if (filterRepresentation == null) { 123 return null; 124 } 125 FilterRepresentation representation = null; 126 if ((mBorder != null) 127 && (mBorder.getFilterClass() == filterRepresentation.getFilterClass())) { 128 // TODO: instead of special casing for border, we should correctly implements "filters priority set" 129 representation = mBorder; 130 } else { 131 int position = getPositionForRepresentation(filterRepresentation); 132 if (position == -1) { 133 return null; 134 } 135 representation = mFilters.elementAt(position); 136 } 137 if (representation != null) { 138 try { 139 representation = representation.clone(); 140 } catch (CloneNotSupportedException e) { 141 e.printStackTrace(); 142 } 143 } 144 return representation; 145 } 146 147 public void updateFilterRepresentation(FilterRepresentation representation) { 148 if (representation == null) { 149 return; 150 } 151 synchronized (mFilters) { 152 if (representation instanceof GeometryMetadata) { 153 setGeometry((GeometryMetadata) representation); 154 } else { 155 if ((mBorder != null) 156 && (mBorder.getFilterClass() == representation.getFilterClass())) { 157 mBorder.updateTempParametersFrom(representation); 158 } else { 159 int position = getPositionForRepresentation(representation); 160 if (position == -1) { 161 return; 162 } 163 FilterRepresentation old = mFilters.elementAt(position); 164 old.updateTempParametersFrom(representation); 165 } 166 } 167 } 168 MasterImage.getImage().invalidatePreview(); 169 fillImageStateAdapter(MasterImage.getImage().getState()); 170 } 171 172 public void setDoApplyGeometry(boolean value) { 173 mDoApplyGeometry = value; 174 } 175 176 public void setDoApplyFilters(boolean value) { 177 mDoApplyFilters = value; 178 } 179 180 public boolean getDoApplyFilters() { 181 return mDoApplyFilters; 182 } 183 184 public synchronized GeometryMetadata getGeometry() { 185 return mGeoData; 186 } 187 188 public boolean hasModifications() { 189 if (mBorder != null && !mBorder.isNil()) { 190 return true; 191 } 192 if (mGeoData.hasModifications()) { 193 return true; 194 } 195 for (int i = 0; i < mFilters.size(); i++) { 196 FilterRepresentation filter = mFilters.elementAt(i); 197 if (!filter.isNil() && !filter.getName().equalsIgnoreCase("none")) { 198 return true; 199 } 200 } 201 return false; 202 } 203 204 public boolean isPanoramaSafe() { 205 if (mBorder != null && !mBorder.isNil()) { 206 return false; 207 } 208 if (mGeoData.hasModifications()) { 209 return false; 210 } 211 for (FilterRepresentation representation : mFilters) { 212 if (representation.getPriority() == FilterRepresentation.TYPE_VIGNETTE 213 && !representation.isNil()) { 214 return false; 215 } 216 if (representation.getPriority() == FilterRepresentation.TYPE_TINYPLANET 217 && !representation.isNil()) { 218 return false; 219 } 220 } 221 return true; 222 } 223 224 public synchronized void setGeometry(GeometryMetadata m) { 225 mGeoData.set(m); 226 MasterImage.getImage().notifyGeometryChange(); 227 } 228 229 private void setBorder(FilterRepresentation filter) { 230 mBorder = filter; 231 } 232 233 public void resetBorder() { 234 mBorder = null; 235 } 236 237 public boolean isFx() { 238 return mIsFxPreset; 239 } 240 241 public void setIsFx(boolean value) { 242 mIsFxPreset = value; 243 } 244 245 public void setName(String name) { 246 mName = name; 247 mHistoryName = name; 248 } 249 250 public void setHistoryName(String name) { 251 mHistoryName = name; 252 } 253 254 public ImageLoader getImageLoader() { 255 return mImageLoader; 256 } 257 258 public void setImageLoader(ImageLoader mImageLoader) { 259 this.mImageLoader = mImageLoader; 260 } 261 262 public boolean equals(ImagePreset preset) { 263 if (!same(preset)) { 264 return false; 265 } 266 if (mDoApplyFilters && preset.mDoApplyFilters) { 267 for (int i = 0; i < preset.mFilters.size(); i++) { 268 FilterRepresentation a = preset.mFilters.elementAt(i); 269 FilterRepresentation b = mFilters.elementAt(i); 270 if (!a.equals(b)) { 271 return false; 272 } 273 } 274 } 275 return true; 276 } 277 278 public boolean same(ImagePreset preset) { 279 if (preset == null) { 280 return false; 281 } 282 283 if (preset.mFilters.size() != mFilters.size()) { 284 return false; 285 } 286 287 if (!mName.equalsIgnoreCase(preset.name())) { 288 return false; 289 } 290 291 if (mDoApplyGeometry != preset.mDoApplyGeometry) { 292 return false; 293 } 294 295 if (mDoApplyGeometry && !mGeoData.equals(preset.mGeoData)) { 296 return false; 297 } 298 299 if (mDoApplyGeometry && mBorder != preset.mBorder) { 300 return false; 301 } 302 303 if (mBorder != null && !mBorder.equals(preset.mBorder)) { 304 return false; 305 } 306 307 if (mDoApplyFilters != preset.mDoApplyFilters) { 308 if (mFilters.size() > 0 || preset.mFilters.size() > 0) { 309 return false; 310 } 311 } 312 313 if (mDoApplyFilters && preset.mDoApplyFilters) { 314 for (int i = 0; i < preset.mFilters.size(); i++) { 315 FilterRepresentation a = preset.mFilters.elementAt(i); 316 FilterRepresentation b = mFilters.elementAt(i); 317 if (!a.same(b)) { 318 return false; 319 } 320 } 321 } 322 323 return true; 324 } 325 326 public int similarUpTo(ImagePreset preset) { 327 if (!mGeoData.equals(preset.mGeoData)) { 328 return -1; 329 } 330 331 for (int i = 0; i < preset.mFilters.size(); i++) { 332 FilterRepresentation a = preset.mFilters.elementAt(i); 333 if (i < mFilters.size()) { 334 FilterRepresentation b = mFilters.elementAt(i); 335 if (!a.same(b)) { 336 return i; 337 } 338 if (!a.equals(b)) { 339 return i; 340 } 341 } else { 342 return i; 343 } 344 } 345 return preset.mFilters.size(); 346 } 347 348 public String name() { 349 return mName; 350 } 351 352 public String historyName() { 353 return mHistoryName; 354 } 355 356 public void showFilters() { 357 Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters"); 358 int n = 0; 359 for (FilterRepresentation representation : mFilters) { 360 Log.v(LOGTAG, " filter " + n + " : " + representation.toString()); 361 n++; 362 } 363 Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters"); 364 } 365 366 public FilterRepresentation getLastRepresentation() { 367 if (mFilters.size() > 0) { 368 return mFilters.lastElement(); 369 } 370 return null; 371 } 372 373 public void removeFilter(FilterRepresentation filterRepresentation) { 374 if (filterRepresentation.getPriority() == FilterRepresentation.TYPE_BORDER) { 375 setBorder(null); 376 setHistoryName("Remove"); 377 return; 378 } 379 for (int i = 0; i < mFilters.size(); i++) { 380 if (mFilters.elementAt(i).getFilterClass() == filterRepresentation.getFilterClass()) { 381 mFilters.remove(i); 382 setHistoryName("Remove"); 383 return; 384 } 385 } 386 } 387 388 public void addFilter(FilterRepresentation representation) { 389 if (representation instanceof GeometryMetadata) { 390 setGeometry((GeometryMetadata) representation); 391 return; 392 } 393 if (representation.getPriority() == FilterRepresentation.TYPE_BORDER) { 394 setHistoryName(representation.getName()); 395 setBorder(representation); 396 } else if (representation.getPriority() == FilterRepresentation.TYPE_FX) { 397 boolean found = false; 398 for (int i = 0; i < mFilters.size(); i++) { 399 int type = mFilters.elementAt(i).getPriority(); 400 if (found) { 401 if (type != FilterRepresentation.TYPE_VIGNETTE) { 402 mFilters.remove(i); 403 continue; 404 } 405 } 406 if (type == FilterRepresentation.TYPE_FX) { 407 mFilters.remove(i); 408 mFilters.add(i, representation); 409 setHistoryName(representation.getName()); 410 found = true; 411 } 412 } 413 if (!found) { 414 mFilters.add(representation); 415 setHistoryName(representation.getName()); 416 } 417 } else { 418 mFilters.add(representation); 419 setHistoryName(representation.getName()); 420 } 421 } 422 423 public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) { 424 if (filterRepresentation instanceof GeometryMetadata) { 425 return mGeoData; 426 } 427 for (int i = 0; i < mFilters.size(); i++) { 428 FilterRepresentation representation = mFilters.elementAt(i); 429 if (representation.getFilterClass() == filterRepresentation.getFilterClass()) { 430 return representation; 431 } 432 } 433 if (mBorder != null && mBorder.getFilterClass() == filterRepresentation.getFilterClass()) { 434 return mBorder; 435 } 436 return null; 437 } 438 439 public void setup() { 440 // do nothing here 441 } 442 443 public Bitmap apply(Bitmap original, FilterEnvironment environment) { 444 Bitmap bitmap = original; 445 bitmap = applyFilters(bitmap, -1, -1, environment); 446 return applyBorder(bitmap, environment); 447 } 448 449 public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) { 450 // Apply any transform -- 90 rotate, flip, straighten, crop 451 // Returns a new bitmap. 452 if (mDoApplyGeometry) { 453 mGeoData.synchronizeRepresentation(); 454 bitmap = environment.applyRepresentation(mGeoData, bitmap); 455 } 456 return bitmap; 457 } 458 459 public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) { 460 if (mBorder != null && mDoApplyGeometry) { 461 mBorder.synchronizeRepresentation(); 462 bitmap = environment.applyRepresentation(mBorder, bitmap); 463 if (environment.getQuality() == QUALITY_FINAL) { 464 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR, 465 "SaveBorder", mBorder.getName(), 1); 466 } 467 } 468 return bitmap; 469 } 470 471 public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) { 472 if (mDoApplyFilters) { 473 if (from < 0) { 474 from = 0; 475 } 476 if (to == -1) { 477 to = mFilters.size(); 478 } 479 for (int i = from; i < to; i++) { 480 FilterRepresentation representation = null; 481 synchronized (mFilters) { 482 representation = mFilters.elementAt(i); 483 representation.synchronizeRepresentation(); 484 } 485 bitmap = environment.applyRepresentation(representation, bitmap); 486 if (environment.getQuality() == QUALITY_FINAL) { 487 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR, 488 "SaveFilter", representation.getName(), 1); 489 } 490 if (environment.needsStop()) { 491 return bitmap; 492 } 493 } 494 } 495 496 return bitmap; 497 } 498 499 public void applyBorder(Allocation in, Allocation out, FilterEnvironment environment) { 500 if (mBorder != null && mDoApplyGeometry) { 501 mBorder.synchronizeRepresentation(); 502 // TODO: should keep the bitmap around 503 Allocation bitmapIn = Allocation.createTyped(CachingPipeline.getRenderScriptContext(), in.getType()); 504 bitmapIn.copyFrom(out); 505 environment.applyRepresentation(mBorder, bitmapIn, out); 506 } 507 } 508 509 public void applyFilters(int from, int to, Allocation in, Allocation out, FilterEnvironment environment) { 510 if (mDoApplyFilters) { 511 if (from < 0) { 512 from = 0; 513 } 514 if (to == -1) { 515 to = mFilters.size(); 516 } 517 for (int i = from; i < to; i++) { 518 FilterRepresentation representation = null; 519 synchronized (mFilters) { 520 representation = mFilters.elementAt(i); 521 representation.synchronizeRepresentation(); 522 } 523 if (i > from) { 524 in.copyFrom(out); 525 } 526 environment.applyRepresentation(representation, in, out); 527 } 528 } 529 } 530 531 public boolean canDoPartialRendering() { 532 if (mGeoData.hasModifications()) { 533 return false; 534 } 535 if (mBorder != null && !mBorder.supportsPartialRendering()) { 536 return false; 537 } 538 if (ImageLoader.getZoomOrientation() != ImageLoader.ORI_NORMAL) { 539 return false; 540 } 541 for (int i = 0; i < mFilters.size(); i++) { 542 FilterRepresentation representation = null; 543 synchronized (mFilters) { 544 representation = mFilters.elementAt(i); 545 } 546 if (!representation.supportsPartialRendering()) { 547 return false; 548 } 549 } 550 return true; 551 } 552 553 public void fillImageStateAdapter(StateAdapter imageStateAdapter) { 554 if (imageStateAdapter == null) { 555 return; 556 } 557 Vector<State> states = new Vector<State>(); 558 // TODO: supports Geometry representations in the state panel. 559 if (false && mGeoData != null && mGeoData.hasModifications()) { 560 State geo = new State("Geometry"); 561 geo.setFilterRepresentation(mGeoData); 562 states.add(geo); 563 } 564 for (FilterRepresentation filter : mFilters) { 565 State state = new State(filter.getName()); 566 state.setFilterRepresentation(filter); 567 states.add(state); 568 } 569 if (mBorder != null) { 570 State border = new State(mBorder.getName()); 571 border.setFilterRepresentation(mBorder); 572 states.add(border); 573 } 574 imageStateAdapter.fill(states); 575 } 576 577 public void setPartialRendering(boolean partialRendering, Rect bounds) { 578 mPartialRendering = partialRendering; 579 mPartialRenderingBounds = bounds; 580 } 581 582 public boolean isPartialRendering() { 583 return mPartialRendering; 584 } 585 586 public Rect getPartialRenderingBounds() { 587 return mPartialRenderingBounds; 588 } 589 590 public Bitmap getPreviewImage() { 591 return mPreviewImage; 592 } 593 594 public void setPreviewImage(Bitmap previewImage) { 595 mPreviewImage = previewImage; 596 } 597 598 public Vector<ImageFilter> getUsedFilters(BaseFiltersManager filtersManager) { 599 Vector<ImageFilter> usedFilters = new Vector<ImageFilter>(); 600 for (int i = 0; i < mFilters.size(); i++) { 601 FilterRepresentation representation = mFilters.elementAt(i); 602 ImageFilter filter = filtersManager.getFilterForRepresentation(representation); 603 usedFilters.add(filter); 604 } 605 return usedFilters; 606 } 607 608 } 609