ImagePreset.java revision fa474a198019851ecc3824a1dfbac94cd1928efc
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 17package com.android.gallery3d.filtershow.pipeline; 18 19import android.graphics.Bitmap; 20import android.graphics.Rect; 21import android.support.v8.renderscript.Allocation; 22import android.util.JsonReader; 23import android.util.JsonWriter; 24import android.util.Log; 25 26import com.android.gallery3d.R; 27import com.android.gallery3d.filtershow.cache.ImageLoader; 28import com.android.gallery3d.filtershow.filters.BaseFiltersManager; 29import com.android.gallery3d.filtershow.filters.FilterCropRepresentation; 30import com.android.gallery3d.filtershow.filters.FilterFxRepresentation; 31import com.android.gallery3d.filtershow.filters.FilterImageBorderRepresentation; 32import com.android.gallery3d.filtershow.filters.FilterMirrorRepresentation; 33import com.android.gallery3d.filtershow.filters.FilterRepresentation; 34import com.android.gallery3d.filtershow.filters.FilterRotateRepresentation; 35import com.android.gallery3d.filtershow.filters.FilterStraightenRepresentation; 36import com.android.gallery3d.filtershow.filters.FilterUserPresetRepresentation; 37import com.android.gallery3d.filtershow.filters.FiltersManager; 38import com.android.gallery3d.filtershow.filters.ImageFilter; 39import com.android.gallery3d.filtershow.imageshow.GeometryMetadata; 40import com.android.gallery3d.filtershow.imageshow.MasterImage; 41import com.android.gallery3d.filtershow.state.State; 42import com.android.gallery3d.filtershow.state.StateAdapter; 43import com.android.gallery3d.util.UsageStatistics; 44 45import java.io.IOException; 46import java.io.StringReader; 47import java.io.StringWriter; 48import java.util.Vector; 49 50public class ImagePreset { 51 52 private static final String LOGTAG = "ImagePreset"; 53 54 private Vector<FilterRepresentation> mFilters = new Vector<FilterRepresentation>(); 55 56 private boolean mDoApplyGeometry = true; 57 private boolean mDoApplyFilters = true; 58 59 private boolean mPartialRendering = false; 60 private Rect mPartialRenderingBounds; 61 private static final boolean DEBUG = false; 62 63 public ImagePreset() { 64 } 65 66 public ImagePreset(ImagePreset source) { 67 for (int i = 0; i < source.mFilters.size(); i++) { 68 FilterRepresentation representation = null; 69 FilterRepresentation sourceRepresentation = source.mFilters.elementAt(i); 70 if (sourceRepresentation instanceof GeometryMetadata) { 71 GeometryMetadata geoData = new GeometryMetadata(); 72 GeometryMetadata srcGeo = (GeometryMetadata) sourceRepresentation; 73 geoData.set(srcGeo); 74 representation = geoData; 75 } else { 76 representation = sourceRepresentation.copy(); 77 } 78 mFilters.add(representation); 79 } 80 } 81 82 public FilterRepresentation getFilterRepresentation(int position) { 83 FilterRepresentation representation = null; 84 85 representation = mFilters.elementAt(position).copy(); 86 87 return representation; 88 } 89 90 public int getPositionForRepresentation(FilterRepresentation representation) { 91 for (int i = 0; i < mFilters.size(); i++) { 92 if (mFilters.elementAt(i).getFilterClass() == representation.getFilterClass()) { 93 return i; 94 } 95 } 96 return -1; 97 } 98 99 private FilterRepresentation getFilterRepresentationForType(int type) { 100 for (int i = 0; i < mFilters.size(); i++) { 101 if (mFilters.elementAt(i).getFilterType() == type) { 102 return mFilters.elementAt(i); 103 } 104 } 105 return null; 106 } 107 108 public int getPositionForType(int type) { 109 for (int i = 0; i < mFilters.size(); i++) { 110 if (mFilters.elementAt(i).getFilterType() == type) { 111 return i; 112 } 113 } 114 return -1; 115 } 116 117 public FilterRepresentation getFilterRepresentationCopyFrom(FilterRepresentation filterRepresentation) { 118 // TODO: add concept of position in the filters (to allow multiple instances) 119 if (filterRepresentation == null) { 120 return null; 121 } 122 int position = getPositionForRepresentation(filterRepresentation); 123 if (position == -1) { 124 return null; 125 } 126 FilterRepresentation representation = mFilters.elementAt(position); 127 if (representation != null) { 128 representation = representation.copy(); 129 } 130 return representation; 131 } 132 133 public void updateFilterRepresentation(FilterRepresentation representation) { 134 if (representation == null) { 135 return; 136 } 137 if (representation instanceof GeometryMetadata) { 138 setGeometry((GeometryMetadata) representation); 139 } else { 140 int position = getPositionForRepresentation(representation); 141 if (position == -1) { 142 return; 143 } 144 FilterRepresentation old = mFilters.elementAt(position); 145 old.useParametersFrom(representation); 146 } 147 MasterImage.getImage().invalidatePreview(); 148 fillImageStateAdapter(MasterImage.getImage().getState()); 149 } 150 151 public void setDoApplyGeometry(boolean value) { 152 mDoApplyGeometry = value; 153 } 154 155 public void setDoApplyFilters(boolean value) { 156 mDoApplyFilters = value; 157 } 158 159 public boolean getDoApplyFilters() { 160 return mDoApplyFilters; 161 } 162 163 public GeometryMetadata getGeometry() { 164 for (FilterRepresentation representation : mFilters) { 165 if (representation instanceof GeometryMetadata) { 166 return (GeometryMetadata) representation; 167 } 168 } 169 GeometryMetadata geo = new GeometryMetadata(); 170 mFilters.add(0, geo); // Hard Requirement for now -- Geometry ought to be first. 171 return geo; 172 } 173 174 public boolean hasModifications() { 175 for (int i = 0; i < mFilters.size(); i++) { 176 FilterRepresentation filter = mFilters.elementAt(i); 177 if (filter instanceof GeometryMetadata) { 178 if (((GeometryMetadata) filter).hasModifications()) { 179 return true; 180 } 181 } else if (!filter.isNil()) { 182 return true; 183 } 184 } 185 return false; 186 } 187 188 public boolean isPanoramaSafe() { 189 for (FilterRepresentation representation : mFilters) { 190 if (representation instanceof GeometryMetadata) { 191 if (((GeometryMetadata) representation).hasModifications()) { 192 return false; 193 } 194 } 195 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER 196 && !representation.isNil()) { 197 return false; 198 } 199 if (representation.getFilterType() == FilterRepresentation.TYPE_VIGNETTE 200 && !representation.isNil()) { 201 return false; 202 } 203 if (representation.getFilterType() == FilterRepresentation.TYPE_TINYPLANET 204 && !representation.isNil()) { 205 return false; 206 } 207 } 208 return true; 209 } 210 211 public void setGeometry(GeometryMetadata representation) { 212 GeometryMetadata geoData = getGeometry(); 213 if (geoData != representation) { 214 geoData.set(representation); 215 } 216 } 217 218 public boolean equals(ImagePreset preset) { 219 if (!same(preset)) { 220 return false; 221 } 222 if (mDoApplyFilters && preset.mDoApplyFilters) { 223 for (int i = 0; i < preset.mFilters.size(); i++) { 224 FilterRepresentation a = preset.mFilters.elementAt(i); 225 FilterRepresentation b = mFilters.elementAt(i); 226 if (!a.equals(b)) { 227 return false; 228 } 229 } 230 } 231 return true; 232 } 233 234 public boolean same(ImagePreset preset) { 235 if (preset == null) { 236 return false; 237 } 238 239 if (preset.mFilters.size() != mFilters.size()) { 240 return false; 241 } 242 243 if (mDoApplyGeometry != preset.mDoApplyGeometry) { 244 return false; 245 } 246 247 if (mDoApplyGeometry && !getGeometry().equals(preset.getGeometry())) { 248 return false; 249 } 250 251 if (mDoApplyFilters != preset.mDoApplyFilters) { 252 if (mFilters.size() > 0 || preset.mFilters.size() > 0) { 253 return false; 254 } 255 } 256 257 if (mDoApplyFilters && preset.mDoApplyFilters) { 258 for (int i = 0; i < preset.mFilters.size(); i++) { 259 FilterRepresentation a = preset.mFilters.elementAt(i); 260 FilterRepresentation b = mFilters.elementAt(i); 261 if (a instanceof GeometryMetadata) { 262 // Note: Geometry will always be at the same place 263 continue; 264 } 265 if (!a.same(b)) { 266 return false; 267 } 268 } 269 } 270 271 return true; 272 } 273 274 public int similarUpTo(ImagePreset preset) { 275 if (!getGeometry().equals(preset.getGeometry())) { 276 return -1; 277 } 278 279 for (int i = 0; i < preset.mFilters.size(); i++) { 280 FilterRepresentation a = preset.mFilters.elementAt(i); 281 if (i < mFilters.size()) { 282 FilterRepresentation b = mFilters.elementAt(i); 283 if (!a.same(b)) { 284 return i; 285 } 286 if (!a.equals(b)) { 287 return i; 288 } 289 } else { 290 return i; 291 } 292 } 293 return preset.mFilters.size(); 294 } 295 296 public void showFilters() { 297 Log.v(LOGTAG, "\\\\\\ showFilters -- " + mFilters.size() + " filters"); 298 int n = 0; 299 for (FilterRepresentation representation : mFilters) { 300 Log.v(LOGTAG, " filter " + n + " : " + representation.toString()); 301 n++; 302 } 303 Log.v(LOGTAG, "/// showFilters -- " + mFilters.size() + " filters"); 304 } 305 306 public FilterRepresentation getLastRepresentation() { 307 if (mFilters.size() > 0) { 308 return mFilters.lastElement(); 309 } 310 return null; 311 } 312 313 public void removeFilter(FilterRepresentation filterRepresentation) { 314 if (filterRepresentation.getFilterType() == FilterRepresentation.TYPE_BORDER) { 315 for (int i = 0; i < mFilters.size();i++) { 316 if (mFilters.elementAt(i).getFilterType() 317 == filterRepresentation.getFilterType()) { 318 mFilters.remove(i); 319 break; 320 } 321 } 322 } else { 323 for (int i = 0; i < mFilters.size(); i++) { 324 if (mFilters.elementAt(i).getFilterClass() 325 == filterRepresentation.getFilterClass()) { 326 mFilters.remove(i); 327 break; 328 } 329 } 330 } 331 } 332 333 // If the filter is an "None" effect or border, then just don't add this 334 // filter. 335 public void addFilter(FilterRepresentation representation) { 336 if (representation instanceof GeometryMetadata) { 337 setGeometry((GeometryMetadata) representation); 338 return; 339 } 340 if (representation instanceof FilterUserPresetRepresentation) { 341 ImagePreset preset = ((FilterUserPresetRepresentation) representation).getImagePreset(); 342 // user preset replace everything but geometry 343 GeometryMetadata geometry = getGeometry(); 344 mFilters.clear(); 345 mFilters.add(geometry); 346 for (int i = 0; i < preset.nbFilters(); i++) { 347 FilterRepresentation rep = preset.getFilterRepresentation(i); 348 if (!(representation instanceof GeometryMetadata)) { 349 addFilter(rep); 350 } 351 } 352 mFilters.add(representation); 353 return; 354 } 355 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { 356 removeFilter(representation); 357 if (!isNoneBorderFilter(representation)) { 358 mFilters.add(representation); 359 } 360 } else if (representation.getFilterType() == FilterRepresentation.TYPE_FX) { 361 boolean found = false; 362 for (int i = 0; i < mFilters.size(); i++) { 363 FilterRepresentation current = mFilters.elementAt(i); 364 int type = current.getFilterType(); 365 if (found) { 366 if (type != FilterRepresentation.TYPE_VIGNETTE) { 367 mFilters.remove(i); 368 continue; 369 } 370 } 371 if (type == FilterRepresentation.TYPE_FX) { 372 if (current instanceof FilterUserPresetRepresentation) { 373 ImagePreset preset = ((FilterUserPresetRepresentation) current) 374 .getImagePreset(); 375 // If we had an existing user preset, let's remove all the presets that 376 // were added by it 377 for (int j = 0; j < preset.nbFilters(); j++) { 378 FilterRepresentation rep = preset.getFilterRepresentation(j); 379 int pos = getPositionForRepresentation(rep); 380 if (pos != -1) { 381 mFilters.remove(pos); 382 } 383 } 384 int pos = getPositionForRepresentation(current); 385 if (pos != -1) { 386 mFilters.remove(pos); 387 } else { 388 pos = 0; 389 } 390 if (!isNoneFxFilter(representation)) { 391 mFilters.add(pos, representation); 392 } 393 394 } else { 395 mFilters.remove(i); 396 if (!isNoneFxFilter(representation)) { 397 mFilters.add(i, representation); 398 } 399 } 400 found = true; 401 } 402 } 403 if (!found) { 404 if (!isNoneFxFilter(representation)) { 405 mFilters.add(representation); 406 } 407 } 408 } else { 409 mFilters.add(representation); 410 } 411 } 412 413 private boolean isNoneBorderFilter(FilterRepresentation representation) { 414 return representation instanceof FilterImageBorderRepresentation && 415 ((FilterImageBorderRepresentation) representation).getDrawableResource() == 0; 416 } 417 418 private boolean isNoneFxFilter(FilterRepresentation representation) { 419 return representation instanceof FilterFxRepresentation && 420 ((FilterFxRepresentation)representation).getNameResource() == R.string.none; 421 } 422 423 public FilterRepresentation getRepresentation(FilterRepresentation filterRepresentation) { 424 for (int i = 0; i < mFilters.size(); i++) { 425 FilterRepresentation representation = mFilters.elementAt(i); 426 if (representation.getFilterClass() == filterRepresentation.getFilterClass()) { 427 return representation; 428 } 429 } 430 return null; 431 } 432 433 public Bitmap apply(Bitmap original, FilterEnvironment environment) { 434 Bitmap bitmap = original; 435 bitmap = applyFilters(bitmap, -1, -1, environment); 436 return applyBorder(bitmap, environment); 437 } 438 439 public Bitmap applyGeometry(Bitmap bitmap, FilterEnvironment environment) { 440 // Apply any transform -- 90 rotate, flip, straighten, crop 441 // Returns a new bitmap. 442 if (mDoApplyGeometry) { 443 GeometryMetadata geoData = getGeometry(); 444 bitmap = environment.applyRepresentation(geoData, bitmap); 445 } 446 return bitmap; 447 } 448 449 public Bitmap applyBorder(Bitmap bitmap, FilterEnvironment environment) { 450 // get the border from the list of filters. 451 FilterRepresentation border = getFilterRepresentationForType( 452 FilterRepresentation.TYPE_BORDER); 453 if (border != null && mDoApplyGeometry) { 454 bitmap = environment.applyRepresentation(border, bitmap); 455 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) { 456 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR, 457 "SaveBorder", border.getSerializationName(), 1); 458 } 459 } 460 return bitmap; 461 } 462 463 public int nbFilters() { 464 return mFilters.size(); 465 } 466 467 public Bitmap applyFilters(Bitmap bitmap, int from, int to, FilterEnvironment environment) { 468 if (mDoApplyFilters) { 469 if (from < 0) { 470 from = 0; 471 } 472 if (to == -1) { 473 to = mFilters.size(); 474 } 475 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) { 476 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR, 477 "SaveFilters", "Total", to - from + 1); 478 } 479 for (int i = from; i < to; i++) { 480 FilterRepresentation representation = mFilters.elementAt(i); 481 if (representation instanceof GeometryMetadata) { 482 // skip the geometry as it's already applied. 483 continue; 484 } 485 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { 486 // for now, let's skip the border as it will be applied in applyBorder() 487 // TODO: might be worth getting rid of applyBorder. 488 continue; 489 } 490 bitmap = environment.applyRepresentation(representation, bitmap); 491 if (environment.getQuality() == FilterEnvironment.QUALITY_FINAL) { 492 UsageStatistics.onEvent(UsageStatistics.COMPONENT_EDITOR, 493 "SaveFilter", representation.getSerializationName(), 1); 494 } 495 if (environment.needsStop()) { 496 return bitmap; 497 } 498 } 499 } 500 501 return bitmap; 502 } 503 504 public void applyBorder(Allocation in, Allocation out, 505 boolean copyOut, FilterEnvironment environment) { 506 FilterRepresentation border = getFilterRepresentationForType( 507 FilterRepresentation.TYPE_BORDER); 508 if (border != null && mDoApplyGeometry) { 509 // TODO: should keep the bitmap around 510 Allocation bitmapIn = in; 511 if (copyOut) { 512 bitmapIn = Allocation.createTyped( 513 CachingPipeline.getRenderScriptContext(), in.getType()); 514 bitmapIn.copyFrom(out); 515 } 516 environment.applyRepresentation(border, bitmapIn, out); 517 } 518 } 519 520 public void applyFilters(int from, int to, Allocation in, Allocation out, 521 FilterEnvironment environment) { 522 if (mDoApplyFilters) { 523 if (from < 0) { 524 from = 0; 525 } 526 if (to == -1) { 527 to = mFilters.size(); 528 } 529 for (int i = from; i < to; i++) { 530 FilterRepresentation representation = mFilters.elementAt(i); 531 if (representation instanceof GeometryMetadata) { 532 // skip the geometry as it's already applied. 533 continue; 534 } 535 if (representation.getFilterType() == FilterRepresentation.TYPE_BORDER) { 536 // for now, let's skip the border as it will be applied in applyBorder() 537 continue; 538 } 539 if (i > from) { 540 in.copyFrom(out); 541 } 542 environment.applyRepresentation(representation, in, out); 543 } 544 } 545 } 546 547 public boolean canDoPartialRendering() { 548 if (MasterImage.getImage().getZoomOrientation() != ImageLoader.ORI_NORMAL) { 549 return false; 550 } 551 for (int i = 0; i < mFilters.size(); i++) { 552 FilterRepresentation representation = mFilters.elementAt(i); 553 if (representation instanceof GeometryMetadata 554 && ((GeometryMetadata) representation).hasModifications()) { 555 return false; 556 } 557 if (!representation.supportsPartialRendering()) { 558 return false; 559 } 560 } 561 return true; 562 } 563 564 public void fillImageStateAdapter(StateAdapter imageStateAdapter) { 565 if (imageStateAdapter == null) { 566 return; 567 } 568 Vector<State> states = new Vector<State>(); 569 for (FilterRepresentation filter : mFilters) { 570 if (filter instanceof GeometryMetadata) { 571 // TODO: supports Geometry representations in the state panel. 572 continue; 573 } 574 if (filter instanceof FilterUserPresetRepresentation) { 575 // do not show the user preset itself in the state panel 576 continue; 577 } 578 State state = new State(filter.getName()); 579 state.setFilterRepresentation(filter); 580 states.add(state); 581 } 582 imageStateAdapter.fill(states); 583 } 584 585 public void setPartialRendering(boolean partialRendering, Rect bounds) { 586 mPartialRendering = partialRendering; 587 mPartialRenderingBounds = bounds; 588 } 589 590 public boolean isPartialRendering() { 591 return mPartialRendering; 592 } 593 594 public Rect getPartialRenderingBounds() { 595 return mPartialRenderingBounds; 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 public String getJsonString(String name) { 609 StringWriter swriter = new StringWriter(); 610 try { 611 JsonWriter writer = new JsonWriter(swriter); 612 writeJson(writer, name); 613 writer.close(); 614 } catch (IOException e) { 615 return null; 616 } 617 return swriter.toString(); 618 } 619 620 public void writeJson(JsonWriter writer, String name) { 621 int numFilters = mFilters.size(); 622 try { 623 writer.beginObject(); 624 for (int i = 0; i < numFilters; i++) { 625 FilterRepresentation filter = mFilters.get(i); 626 if (filter instanceof FilterUserPresetRepresentation) { 627 continue; 628 } 629 String sname = filter.getSerializationName(); 630 if (DEBUG) { 631 Log.v(LOGTAG, "Serialization: " + sname); 632 if (sname == null) { 633 Log.v(LOGTAG, "Serialization name null for filter: " + filter); 634 } 635 } 636 writer.name(sname); 637 filter.serializeRepresentation(writer); 638 } 639 writer.endObject(); 640 641 } catch (IOException e) { 642 e.printStackTrace(); 643 } 644 } 645 646 /** 647 * populates preset from JSON string 648 * @param filterString a JSON string 649 * @return true on success if false ImagePreset is undefined 650 */ 651 public boolean readJsonFromString(String filterString) { 652 if (DEBUG) { 653 Log.v(LOGTAG,"reading preset: \""+filterString+"\""); 654 } 655 StringReader sreader = new StringReader(filterString); 656 try { 657 JsonReader reader = new JsonReader(sreader); 658 boolean ok = readJson(reader); 659 if (!ok) { 660 reader.close(); 661 return false; 662 } 663 reader.close(); 664 } catch (Exception e) { 665 Log.e(LOGTAG,"parsing the filter parameters:",e); 666 return false; 667 } 668 return true; 669 } 670 671 /** 672 * populates preset from JSON stream 673 * @param sreader a JSON string 674 * @return true on success if false ImagePreset is undefined 675 */ 676 public boolean readJson(JsonReader sreader) throws IOException { 677 sreader.beginObject(); 678 679 while (sreader.hasNext()) { 680 String name = sreader.nextName(); 681 FilterRepresentation filter = creatFilterFromName(name); 682 if (filter == null) { 683 Log.w(LOGTAG,"UNKNOWN FILTER! "+name); 684 return false; 685 } 686 filter.deSerializeRepresentation(sreader); 687 addFilter(filter); 688 } 689 sreader.endObject(); 690 return true; 691 } 692 693 FilterRepresentation creatFilterFromName(String name) { 694 // TODO: move these to FiltersManager pattern. 695 if (GeometryMetadata.SERIALIZATION_NAME.equalsIgnoreCase(name)) { 696 return new GeometryMetadata(); 697 } else if (FilterRotateRepresentation.SERIALIZATION_NAME.equals(name)) { 698 return new FilterRotateRepresentation(); 699 } else if (FilterMirrorRepresentation.SERIALIZATION_NAME.equals(name)) { 700 return new FilterMirrorRepresentation(); 701 } else if (FilterStraightenRepresentation.SERIALIZATION_NAME.equals(name)) { 702 return new FilterStraightenRepresentation(); 703 } else if (FilterCropRepresentation.SERIALIZATION_NAME.equals(name)) { 704 return new FilterCropRepresentation(); 705 } 706 FiltersManager filtersManager = FiltersManager.getManager(); 707 return filtersManager.createFilterFromName(name); 708 } 709 710 public void updateWith(ImagePreset preset) { 711 if (preset.mFilters.size() != mFilters.size()) { 712 Log.e(LOGTAG, "Updating a preset with an incompatible one"); 713 return; 714 } 715 for (int i = 0; i < mFilters.size(); i++) { 716 FilterRepresentation destRepresentation = mFilters.elementAt(i); 717 FilterRepresentation sourceRepresentation = preset.mFilters.elementAt(i); 718 destRepresentation.useParametersFrom(sourceRepresentation); 719 } 720 } 721} 722