MediaImageItem.java revision 6311d0a079702b29984c0d31937345be105e1a5e
1/* 2 * Copyright (C) 2011 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 18package android.media.videoeditor; 19 20import android.graphics.Bitmap; 21import android.graphics.BitmapFactory; 22import android.graphics.Canvas; 23import android.graphics.Paint; 24import android.graphics.Rect; 25import java.util.ArrayList; 26import android.media.videoeditor.MediaArtistNativeHelper.ClipSettings; 27import android.media.videoeditor.MediaArtistNativeHelper.EditSettings; 28import android.media.videoeditor.MediaArtistNativeHelper.FileType; 29import android.media.videoeditor.MediaArtistNativeHelper.Properties; 30import android.util.Log; 31import android.util.Pair; 32 33import java.io.DataOutputStream; 34import java.io.File; 35import java.io.FileOutputStream; 36import java.io.IOException; 37import java.nio.ByteBuffer; 38import java.nio.IntBuffer; 39import java.lang.Math; 40import java.util.List; 41 42/** 43 * This class represents an image item on the storyboard. Note that images are 44 * scaled down to the maximum supported resolution by preserving the native 45 * aspect ratio. To learn the scaled image dimensions use 46 * {@link #getScaledWidth()} and {@link #getScaledHeight()} respectively. 47 * 48 * {@hide} 49 */ 50public class MediaImageItem extends MediaItem { 51 /** 52 * Logging 53 */ 54 private static final String TAG = "MediaImageItem"; 55 56 /** 57 * The resize paint 58 */ 59 private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG); 60 61 /** 62 * Instance variables 63 */ 64 private final int mWidth; 65 private final int mHeight; 66 private final int mAspectRatio; 67 private long mDurationMs; 68 private int mScaledWidth, mScaledHeight; 69 private String mScaledFilename; 70 private final VideoEditorImpl mVideoEditor; 71 private String mDecodedFilename; 72 private int mGeneratedClipHeight; 73 private int mGeneratedClipWidth; 74 private String mFileName; 75 76 private final MediaArtistNativeHelper mMANativeHelper; 77 78 /** 79 * This class cannot be instantiated by using the default constructor 80 */ 81 @SuppressWarnings("unused") 82 private MediaImageItem() throws IOException { 83 this(null, null, null, 0, RENDERING_MODE_BLACK_BORDER); 84 } 85 86 /** 87 * Constructor 88 * 89 * @param editor The video editor reference 90 * @param mediaItemId The media item id 91 * @param filename The image file name 92 * @param durationMs The duration of the image on the storyboard 93 * @param renderingMode The rendering mode 94 * 95 * @throws IOException 96 */ 97 public MediaImageItem(VideoEditor editor, String mediaItemId, String filename, long durationMs, 98 int renderingMode) throws IOException { 99 100 super(editor, mediaItemId, filename, renderingMode); 101 102 mMANativeHelper = ((VideoEditorImpl)editor).getNativeContext(); 103 mVideoEditor = ((VideoEditorImpl)editor); 104 try { 105 final Properties properties = mMANativeHelper.getMediaProperties(filename); 106 107 switch (mMANativeHelper.getFileType(properties.fileType)) { 108 case MediaProperties.FILE_JPEG: 109 case MediaProperties.FILE_PNG: { 110 break; 111 } 112 113 default: { 114 throw new IllegalArgumentException("Unsupported Input File Type"); 115 } 116 } 117 } catch (Exception e) { 118 throw new IllegalArgumentException("Unsupported file or file not found: " + filename); 119 } 120 mFileName = filename; 121 /** 122 * Determine the dimensions of the image 123 */ 124 final BitmapFactory.Options dbo = new BitmapFactory.Options(); 125 dbo.inJustDecodeBounds = true; 126 BitmapFactory.decodeFile(filename, dbo); 127 128 mWidth = dbo.outWidth; 129 mHeight = dbo.outHeight; 130 mDurationMs = durationMs; 131 mDecodedFilename = String.format(mMANativeHelper.getProjectPath() + 132 "/" + "decoded" + getId()+ ".rgb"); 133 134 try { 135 mAspectRatio = mMANativeHelper.getAspectRatio(mWidth, mHeight); 136 } catch(IllegalArgumentException e) { 137 throw new IllegalArgumentException ("Null width and height"); 138 } 139 140 mGeneratedClipHeight = 0; 141 mGeneratedClipWidth = 0; 142 143 /** 144 * Images are stored in memory scaled to the maximum resolution to 145 * save memory. 146 */ 147 final Pair<Integer, Integer>[] resolutions = 148 MediaProperties.getSupportedResolutions(mAspectRatio); 149 150 /** 151 * Get the highest resolution 152 */ 153 final Pair<Integer, Integer> maxResolution = resolutions[resolutions.length - 1]; 154 155 final Bitmap imageBitmap; 156 157 if (mHeight > maxResolution.second) { 158 /** 159 * We need to scale the image 160 */ 161 imageBitmap = scaleImage(filename, maxResolution.first, 162 maxResolution.second); 163 mScaledFilename = String.format(mMANativeHelper.getProjectPath() + 164 "/" + "scaled" + getId()+ ".JPG"); 165 if (!((new File(mScaledFilename)).exists())) { 166 super.mRegenerateClip = true; 167 final FileOutputStream f1 = new FileOutputStream(mScaledFilename); 168 imageBitmap.compress(Bitmap.CompressFormat.JPEG, 50,f1); 169 f1.close(); 170 } 171 mScaledWidth = (imageBitmap.getWidth() >> 1) << 1; 172 mScaledHeight = (imageBitmap.getHeight() >> 1) << 1; 173 } else { 174 mScaledFilename = filename; 175 mScaledWidth = (mWidth >> 1) << 1; 176 mScaledHeight = (mHeight >> 1) << 1; 177 imageBitmap = BitmapFactory.decodeFile(mScaledFilename); 178 } 179 int newWidth = mScaledWidth; 180 int newHeight = mScaledHeight; 181 if (!((new File(mDecodedFilename)).exists())) { 182 final FileOutputStream fl = new FileOutputStream(mDecodedFilename); 183 final DataOutputStream dos = new DataOutputStream(fl); 184 final int [] framingBuffer = new int[newWidth]; 185 final ByteBuffer byteBuffer = ByteBuffer.allocate(framingBuffer.length * 4); 186 IntBuffer intBuffer; 187 final byte[] array = byteBuffer.array(); 188 int tmp = 0; 189 while (tmp < newHeight) { 190 imageBitmap.getPixels(framingBuffer, 0, mScaledWidth, 0, 191 tmp, newWidth, 1); 192 intBuffer = byteBuffer.asIntBuffer(); 193 intBuffer.put(framingBuffer, 0, newWidth); 194 dos.write(array); 195 tmp += 1; 196 } 197 fl.close(); 198 } 199 imageBitmap.recycle(); 200 } 201 202 /* 203 * {@inheritDoc} 204 */ 205 @Override 206 public int getFileType() { 207 if (mFilename.endsWith(".jpg") || mFilename.endsWith(".jpeg") 208 || mFilename.endsWith(".JPG") || mFilename.endsWith(".JPEG")) { 209 return MediaProperties.FILE_JPEG; 210 } else if (mFilename.endsWith(".png") || mFilename.endsWith(".PNG")) { 211 return MediaProperties.FILE_PNG; 212 } else { 213 return MediaProperties.FILE_UNSUPPORTED; 214 } 215 } 216 217 /** 218 * @return The scaled image file name 219 */ 220 String getScaledImageFileName() { 221 return mScaledFilename; 222 } 223 224 /** 225 * @return The generated Kenburns clip height. 226 */ 227 int getGeneratedClipHeight() { 228 return mGeneratedClipHeight; 229 } 230 231 /** 232 * @return The generated Kenburns clip width. 233 */ 234 int getGeneratedClipWidth() { 235 return mGeneratedClipWidth; 236 } 237 238 /** 239 * @return The file name of image which is decoded and stored 240 * in RGB format 241 */ 242 String getDecodedImageFileName() { 243 return mDecodedFilename; 244 } 245 246 /* 247 * {@inheritDoc} 248 */ 249 @Override 250 public int getWidth() { 251 return mWidth; 252 } 253 254 /* 255 * {@inheritDoc} 256 */ 257 @Override 258 public int getHeight() { 259 return mHeight; 260 } 261 262 /** 263 * @return The scaled width of the image. 264 */ 265 public int getScaledWidth() { 266 return mScaledWidth; 267 } 268 269 /** 270 * @return The scaled height of the image. 271 */ 272 public int getScaledHeight() { 273 return mScaledHeight; 274 } 275 276 /* 277 * {@inheritDoc} 278 */ 279 @Override 280 public int getAspectRatio() { 281 return mAspectRatio; 282 } 283 284 /** 285 * This method will adjust the duration of bounding transitions, effects 286 * and overlays if the current duration of the transactions become greater 287 * than the maximum allowable duration. 288 * 289 * @param durationMs The duration of the image in the storyboard timeline 290 */ 291 public void setDuration(long durationMs) { 292 if (durationMs == mDurationMs) { 293 return; 294 } 295 296 mMANativeHelper.setGeneratePreview(true); 297 298 /** 299 * Invalidate the end transitions if necessary. 300 * This invalidation is necessary for the case in which an effect or 301 * an overlay is overlapping with the end transition 302 * (before the duration is changed) and it no longer overlaps with the 303 * transition after the duration is increased. 304 * 305 * The beginning transition does not need to be invalidated at this time 306 * because an effect or an overlay overlaps with the beginning 307 * transition, the begin transition is unaffected by a media item 308 * duration change. 309 */ 310 invalidateEndTransition(); 311 312 mDurationMs = durationMs; 313 314 adjustTransitions(); 315 final List<Overlay> adjustedOverlays = adjustOverlays(); 316 final List<Effect> adjustedEffects = adjustEffects(); 317 318 /** 319 * Invalidate the beginning and end transitions after adjustments. 320 * This invalidation is necessary for the case in which an effect or 321 * an overlay was not overlapping with the beginning or end transitions 322 * before the setDuration reduces the duration of the media item and 323 * causes an overlap of the beginning and/or end transition with the 324 * effect. 325 */ 326 invalidateBeginTransition(adjustedEffects, adjustedOverlays); 327 invalidateEndTransition(); 328 if (getGeneratedImageClip() != null) { 329 /* 330 * Delete the file 331 */ 332 new File(getGeneratedImageClip()).delete(); 333 /* 334 * Invalidate the filename 335 */ 336 setGeneratedImageClip(null); 337 super.setRegenerateClip(true); 338 } 339 mVideoEditor.updateTimelineDuration(); 340 } 341 342 /** 343 * Invalidate the begin transition if any effects and overlays overlap 344 * with the begin transition. 345 * 346 * @param effects List of effects to check for transition overlap 347 * @param overlays List of overlays to check for transition overlap 348 */ 349 private void invalidateBeginTransition(List<Effect> effects, List<Overlay> overlays) { 350 if (mBeginTransition != null && mBeginTransition.isGenerated()) { 351 final long transitionDurationMs = mBeginTransition.getDuration(); 352 353 /** 354 * The begin transition must be invalidated if it overlaps with 355 * an effect. 356 */ 357 for (Effect effect : effects) { 358 /** 359 * Check if the effect overlaps with the begin transition 360 */ 361 if (effect.getStartTime() < transitionDurationMs) { 362 mBeginTransition.invalidate(); 363 break; 364 } 365 } 366 367 if (mBeginTransition.isGenerated()) { 368 /** 369 * The end transition must be invalidated if it overlaps with 370 * an overlay. 371 */ 372 for (Overlay overlay : overlays) { 373 /** 374 * Check if the overlay overlaps with the end transition 375 */ 376 if (overlay.getStartTime() < transitionDurationMs) { 377 mBeginTransition.invalidate(); 378 break; 379 } 380 } 381 } 382 } 383 } 384 385 /** 386 * Invalidate the end transition if any effects and overlays overlap 387 * with the end transition. 388 */ 389 private void invalidateEndTransition() { 390 if (mEndTransition != null && mEndTransition.isGenerated()) { 391 final long transitionDurationMs = mEndTransition.getDuration(); 392 393 /** 394 * The end transition must be invalidated if it overlaps with 395 * an effect. 396 */ 397 final List<Effect> effects = getAllEffects(); 398 for (Effect effect : effects) { 399 /** 400 * Check if the effect overlaps with the end transition 401 */ 402 if (effect.getStartTime() + effect.getDuration() > 403 mDurationMs - transitionDurationMs) { 404 mEndTransition.invalidate(); 405 break; 406 } 407 } 408 409 if (mEndTransition.isGenerated()) { 410 /** 411 * The end transition must be invalidated if it overlaps with 412 * an overlay. 413 */ 414 final List<Overlay> overlays = getAllOverlays(); 415 for (Overlay overlay : overlays) { 416 /** 417 * Check if the overlay overlaps with the end transition 418 */ 419 if (overlay.getStartTime() + overlay.getDuration() > 420 mDurationMs - transitionDurationMs) { 421 mEndTransition.invalidate(); 422 break; 423 } 424 } 425 } 426 } 427 } 428 429 /** 430 * Adjust the start time and/or duration of effects. 431 * 432 * @return The list of effects which were adjusted 433 */ 434 private List<Effect> adjustEffects() { 435 final List<Effect> adjustedEffects = new ArrayList<Effect>(); 436 final List<Effect> effects = getAllEffects(); 437 for (Effect effect : effects) { 438 /** 439 * Adjust the start time if necessary 440 */ 441 final long effectStartTimeMs; 442 if (effect.getStartTime() > getDuration()) { 443 effectStartTimeMs = 0; 444 } else { 445 effectStartTimeMs = effect.getStartTime(); 446 } 447 448 /** 449 * Adjust the duration if necessary 450 */ 451 final long effectDurationMs; 452 if (effectStartTimeMs + effect.getDuration() > getDuration()) { 453 effectDurationMs = getDuration() - effectStartTimeMs; 454 } else { 455 effectDurationMs = effect.getDuration(); 456 } 457 458 if (effectStartTimeMs != effect.getStartTime() || 459 effectDurationMs != effect.getDuration()) { 460 effect.setStartTimeAndDuration(effectStartTimeMs, effectDurationMs); 461 adjustedEffects.add(effect); 462 } 463 } 464 465 return adjustedEffects; 466 } 467 468 /** 469 * Adjust the start time and/or duration of overlays. 470 * 471 * @return The list of overlays which were adjusted 472 */ 473 private List<Overlay> adjustOverlays() { 474 final List<Overlay> adjustedOverlays = new ArrayList<Overlay>(); 475 final List<Overlay> overlays = getAllOverlays(); 476 for (Overlay overlay : overlays) { 477 /** 478 * Adjust the start time if necessary 479 */ 480 final long overlayStartTimeMs; 481 if (overlay.getStartTime() > getDuration()) { 482 overlayStartTimeMs = 0; 483 } else { 484 overlayStartTimeMs = overlay.getStartTime(); 485 } 486 487 /** 488 * Adjust the duration if necessary 489 */ 490 final long overlayDurationMs; 491 if (overlayStartTimeMs + overlay.getDuration() > getDuration()) { 492 overlayDurationMs = getDuration() - overlayStartTimeMs; 493 } else { 494 overlayDurationMs = overlay.getDuration(); 495 } 496 497 if (overlayStartTimeMs != overlay.getStartTime() || 498 overlayDurationMs != overlay.getDuration()) { 499 overlay.setStartTimeAndDuration(overlayStartTimeMs, overlayDurationMs); 500 adjustedOverlays.add(overlay); 501 } 502 } 503 504 return adjustedOverlays; 505 } 506 /** 507 * This function get the proper width by given aspect ratio 508 * and height. 509 * 510 * @param aspectRatio Given aspect ratio 511 * @param height Given height 512 */ 513 private int getWidthByAspectRatioAndHeight(int aspectRatio, int height) { 514 int width = 0; 515 516 switch (aspectRatio) { 517 case MediaProperties.ASPECT_RATIO_3_2: 518 if (height == MediaProperties.HEIGHT_480) 519 width = 720; 520 else if (height == MediaProperties.HEIGHT_720) 521 width = 1080; 522 break; 523 524 case MediaProperties.ASPECT_RATIO_16_9: 525 if (height == MediaProperties.HEIGHT_360) 526 width = 640; 527 else if (height == MediaProperties.HEIGHT_480) 528 width = 854; 529 else if (height == MediaProperties.HEIGHT_720) 530 width = 1280; 531 else if (height == MediaProperties.HEIGHT_1080) 532 width = 1920; 533 break; 534 535 case MediaProperties.ASPECT_RATIO_4_3: 536 if (height == MediaProperties.HEIGHT_480) 537 width = 640; 538 if (height == MediaProperties.HEIGHT_720) 539 width = 960; 540 break; 541 542 case MediaProperties.ASPECT_RATIO_5_3: 543 if (height == MediaProperties.HEIGHT_480) 544 width = 800; 545 break; 546 547 case MediaProperties.ASPECT_RATIO_11_9: 548 if (height == MediaProperties.HEIGHT_144) 549 width = 176; 550 break; 551 552 default : { 553 throw new IllegalArgumentException( 554 "Illegal arguments for aspectRatio"); 555 } 556 } 557 558 return width; 559 } 560 561 /** 562 * This function sets the Ken Burn effect generated clip 563 * name. 564 * 565 * @param generatedFilePath The name of the generated clip 566 */ 567 @Override 568 void setGeneratedImageClip(String generatedFilePath) { 569 super.setGeneratedImageClip(generatedFilePath); 570 571 // set the Kenburns clip width and height 572 mGeneratedClipHeight = getScaledHeight(); 573 mGeneratedClipWidth = getWidthByAspectRatioAndHeight( 574 mVideoEditor.getAspectRatio(), mGeneratedClipHeight); 575 } 576 577 /** 578 * @return The name of the image clip 579 * generated with ken burns effect. 580 */ 581 @Override 582 String getGeneratedImageClip() { 583 return super.getGeneratedImageClip(); 584 } 585 586 /* 587 * {@inheritDoc} 588 */ 589 @Override 590 public long getDuration() { 591 return mDurationMs; 592 } 593 594 /* 595 * {@inheritDoc} 596 */ 597 @Override 598 public long getTimelineDuration() { 599 return mDurationMs; 600 } 601 602 /* 603 * {@inheritDoc} 604 */ 605 @Override 606 public Bitmap getThumbnail(int width, int height, long timeMs) throws IOException { 607 if (getGeneratedImageClip() != null) { 608 return mMANativeHelper.getPixels(getGeneratedImageClip(), 609 width, height,timeMs); 610 } else { 611 return scaleImage(mFilename, width, height); 612 } 613 } 614 615 /* 616 * {@inheritDoc} 617 */ 618 @Override 619 public Bitmap[] getThumbnailList(int width, int height, long startMs, long endMs, 620 int thumbnailCount) throws IOException { 621 //KenBurns was not applied on this. 622 if (getGeneratedImageClip() == null) { 623 final Bitmap thumbnail = scaleImage(mFilename, width, height); 624 final Bitmap[] thumbnailArray = new Bitmap[thumbnailCount]; 625 for (int i = 0; i < thumbnailCount; i++) { 626 thumbnailArray[i] = thumbnail; 627 } 628 629 return thumbnailArray; 630 } else { 631 if (startMs > endMs) { 632 throw new IllegalArgumentException("Start time is greater than end time"); 633 } 634 635 if (endMs > mDurationMs) { 636 throw new IllegalArgumentException("End time is greater than file duration"); 637 } 638 639 if (startMs == endMs) { 640 Bitmap[] bitmap = new Bitmap[1]; 641 bitmap[0] = mMANativeHelper.getPixels(getGeneratedImageClip(), 642 width, height,startMs); 643 return bitmap; 644 } 645 646 return mMANativeHelper.getPixelsList(getGeneratedImageClip(), width, 647 height,startMs,endMs,thumbnailCount); 648 } 649 } 650 651 /* 652 * {@inheritDoc} 653 */ 654 @Override 655 void invalidateTransitions(long startTimeMs, long durationMs) { 656 /** 657 * Check if the item overlaps with the beginning and end transitions 658 */ 659 if (mBeginTransition != null) { 660 if (isOverlapping(startTimeMs, durationMs, 0, mBeginTransition.getDuration())) { 661 mBeginTransition.invalidate(); 662 } 663 } 664 665 if (mEndTransition != null) { 666 final long transitionDurationMs = mEndTransition.getDuration(); 667 if (isOverlapping(startTimeMs, durationMs, 668 getDuration() - transitionDurationMs, transitionDurationMs)) { 669 mEndTransition.invalidate(); 670 } 671 } 672 } 673 674 /* 675 * {@inheritDoc} 676 */ 677 @Override 678 void invalidateTransitions(long oldStartTimeMs, long oldDurationMs, long newStartTimeMs, 679 long newDurationMs) { 680 /** 681 * Check if the item overlaps with the beginning and end transitions 682 */ 683 if (mBeginTransition != null) { 684 final long transitionDurationMs = mBeginTransition.getDuration(); 685 final boolean oldOverlap = isOverlapping(oldStartTimeMs, oldDurationMs, 0, 686 transitionDurationMs); 687 final boolean newOverlap = isOverlapping(newStartTimeMs, newDurationMs, 0, 688 transitionDurationMs); 689 /** 690 * Invalidate transition if: 691 * 692 * 1. New item overlaps the transition, the old one did not 693 * 2. New item does not overlap the transition, the old one did 694 * 3. New and old item overlap the transition if begin or end 695 * time changed 696 */ 697 if (newOverlap != oldOverlap) { // Overlap has changed 698 mBeginTransition.invalidate(); 699 } else if (newOverlap) { // Both old and new overlap 700 if ((oldStartTimeMs != newStartTimeMs) || 701 !(oldStartTimeMs + oldDurationMs > transitionDurationMs && 702 newStartTimeMs + newDurationMs > transitionDurationMs)) { 703 mBeginTransition.invalidate(); 704 } 705 } 706 } 707 708 if (mEndTransition != null) { 709 final long transitionDurationMs = mEndTransition.getDuration(); 710 final boolean oldOverlap = isOverlapping(oldStartTimeMs, oldDurationMs, 711 mDurationMs - transitionDurationMs, transitionDurationMs); 712 final boolean newOverlap = isOverlapping(newStartTimeMs, newDurationMs, 713 mDurationMs - transitionDurationMs, transitionDurationMs); 714 /** 715 * Invalidate transition if: 716 * 717 * 1. New item overlaps the transition, the old one did not 718 * 2. New item does not overlap the transition, the old one did 719 * 3. New and old item overlap the transition if begin or end 720 * time changed 721 */ 722 if (newOverlap != oldOverlap) { // Overlap has changed 723 mEndTransition.invalidate(); 724 } else if (newOverlap) { // Both old and new overlap 725 if ((oldStartTimeMs + oldDurationMs != newStartTimeMs + newDurationMs) || 726 ((oldStartTimeMs > mDurationMs - transitionDurationMs) || 727 newStartTimeMs > mDurationMs - transitionDurationMs)) { 728 mEndTransition.invalidate(); 729 } 730 } 731 } 732 } 733 734 /** 735 * This function invalidates the rgb image clip,ken burns effect clip, 736 * and scaled image clip 737 */ 738 void invalidate() { 739 if (getGeneratedImageClip() != null) { 740 new File(getGeneratedImageClip()).delete(); 741 setGeneratedImageClip(null); 742 setRegenerateClip(true); 743 } 744 745 if (mScaledFilename != null) { 746 if(mFileName != mScaledFilename) { 747 new File(mScaledFilename).delete(); 748 } 749 mScaledFilename = null; 750 } 751 752 if (mDecodedFilename != null) { 753 new File(mDecodedFilename).delete(); 754 mDecodedFilename = null; 755 } 756 } 757 758 /** 759 * @param KenBurnEffect object. 760 * @return an Object of {@link ClipSettings} with Ken Burn settings 761 * needed to generate the clip 762 */ 763 private ClipSettings getKenBurns(EffectKenBurns effectKB) { 764 int PanZoomXa; 765 int PanZoomXb; 766 int width = 0, height = 0; 767 Rect start = new Rect(); 768 Rect end = new Rect(); 769 ClipSettings clipSettings = null; 770 clipSettings = new ClipSettings(); 771 /** 772 * image: 773 --------------------------------------- 774 | Xa | 775 | Ya --------------- | 776 | | | | 777 | | | | 778 | --------------- Xb ratioB | 779 | ratioA ------- | 780 | Yb | | | 781 | | | | 782 | ------- | 783 --------------------------------------- 784 */ 785 786 effectKB.getKenBurnsSettings(start, end); 787 width = getWidth(); 788 height = getHeight(); 789 if ((start.left < 0) || (start.left > width) || (start.right < 0) || (start.right > width) 790 || (start.top < 0) || (start.top > height) || (start.bottom < 0) 791 || (start.bottom > height) || (end.left < 0) || (end.left > width) 792 || (end.right < 0) || (end.right > width) || (end.top < 0) || (end.top > height) 793 || (end.bottom < 0) || (end.bottom > height)) { 794 throw new IllegalArgumentException("Illegal arguments for KebBurns"); 795 } 796 797 if (((width - (start.right - start.left) == 0) || (height - (start.bottom - start.top) == 0)) 798 && ((width - (end.right - end.left) == 0) || (height - (end.bottom - end.top) == 0))) { 799 setRegenerateClip(false); 800 clipSettings.clipPath = getDecodedImageFileName(); 801 clipSettings.fileType = FileType.JPG; 802 clipSettings.beginCutTime = 0; 803 clipSettings.endCutTime = (int)getTimelineDuration(); 804 clipSettings.beginCutPercent = 0; 805 clipSettings.endCutPercent = 0; 806 clipSettings.panZoomEnabled = false; 807 clipSettings.panZoomPercentStart = 0; 808 clipSettings.panZoomTopLeftXStart = 0; 809 clipSettings.panZoomTopLeftYStart = 0; 810 clipSettings.panZoomPercentEnd = 0; 811 clipSettings.panZoomTopLeftXEnd = 0; 812 clipSettings.panZoomTopLeftYEnd = 0; 813 clipSettings.mediaRendering = mMANativeHelper 814 .getMediaItemRenderingMode(getRenderingMode()); 815 816 clipSettings.rgbWidth = getScaledWidth(); 817 clipSettings.rgbHeight = getScaledHeight(); 818 819 return clipSettings; 820 } 821 822 PanZoomXa = (1000 * start.width()) / width; 823 PanZoomXb = (1000 * end.width()) / width; 824 825 clipSettings.clipPath = getDecodedImageFileName(); 826 clipSettings.fileType = mMANativeHelper.getMediaItemFileType(getFileType()); 827 clipSettings.beginCutTime = 0; 828 clipSettings.endCutTime = (int)getTimelineDuration(); 829 clipSettings.beginCutPercent = 0; 830 clipSettings.endCutPercent = 0; 831 clipSettings.panZoomEnabled = true; 832 clipSettings.panZoomPercentStart = PanZoomXa; 833 clipSettings.panZoomTopLeftXStart = (start.left * 1000) / width; 834 clipSettings.panZoomTopLeftYStart = (start.top * 1000) / height; 835 clipSettings.panZoomPercentEnd = PanZoomXb; 836 clipSettings.panZoomTopLeftXEnd = (end.left * 1000) / width; 837 clipSettings.panZoomTopLeftYEnd = (end.top * 1000) / height; 838 clipSettings.mediaRendering 839 = mMANativeHelper.getMediaItemRenderingMode(getRenderingMode()); 840 841 clipSettings.rgbWidth = getScaledWidth(); 842 clipSettings.rgbHeight = getScaledHeight(); 843 844 return clipSettings; 845 } 846 847 848 /** 849 * @param KenBurnEffect object. 850 * @return an Object of {@link ClipSettings} with Ken Burns 851 * generated clip name 852 */ 853 ClipSettings generateKenburnsClip(EffectKenBurns effectKB) { 854 EditSettings editSettings = new EditSettings(); 855 editSettings.clipSettingsArray = new ClipSettings[1]; 856 String output = null; 857 ClipSettings clipSettings = new ClipSettings(); 858 initClipSettings(clipSettings); 859 editSettings.clipSettingsArray[0] = getKenBurns(effectKB); 860 if ((getGeneratedImageClip() == null) && (getRegenerateClip())) { 861 output = mMANativeHelper.generateKenBurnsClip(editSettings, this); 862 setGeneratedImageClip(output); 863 setRegenerateClip(false); 864 clipSettings.clipPath = output; 865 clipSettings.fileType = FileType.THREE_GPP; 866 867 mGeneratedClipHeight = getScaledHeight(); 868 mGeneratedClipWidth = getWidthByAspectRatioAndHeight( 869 mVideoEditor.getAspectRatio(), mGeneratedClipHeight); 870 } else { 871 if (getGeneratedImageClip() == null) { 872 clipSettings.clipPath = getDecodedImageFileName(); 873 clipSettings.fileType = FileType.JPG; 874 875 clipSettings.rgbWidth = getScaledWidth(); 876 clipSettings.rgbHeight = getScaledHeight(); 877 878 } else { 879 clipSettings.clipPath = getGeneratedImageClip(); 880 clipSettings.fileType = FileType.THREE_GPP; 881 } 882 } 883 clipSettings.mediaRendering = mMANativeHelper.getMediaItemRenderingMode(getRenderingMode()); 884 clipSettings.beginCutTime = 0; 885 clipSettings.endCutTime = (int)getTimelineDuration(); 886 887 return clipSettings; 888 } 889 890 /** 891 * @return an Object of {@link ClipSettings} with Image Clip 892 * properties data populated.If the image has Ken Burns effect applied, 893 * then file path contains generated image clip name with Ken Burns effect 894 */ 895 ClipSettings getImageClipProperties() { 896 ClipSettings clipSettings = new ClipSettings(); 897 List<Effect> effects = null; 898 EffectKenBurns effectKB = null; 899 boolean effectKBPresent = false; 900 901 effects = getAllEffects(); 902 for (Effect effect : effects) { 903 if (effect instanceof EffectKenBurns) { 904 effectKB = (EffectKenBurns)effect; 905 effectKBPresent = true; 906 break; 907 } 908 } 909 910 if (effectKBPresent) { 911 clipSettings = generateKenburnsClip(effectKB); 912 } else { 913 /** 914 * Init the clip settings object 915 */ 916 initClipSettings(clipSettings); 917 clipSettings.clipPath = getDecodedImageFileName(); 918 clipSettings.fileType = FileType.JPG; 919 clipSettings.beginCutTime = 0; 920 clipSettings.endCutTime = (int)getTimelineDuration(); 921 clipSettings.mediaRendering = mMANativeHelper 922 .getMediaItemRenderingMode(getRenderingMode()); 923 clipSettings.rgbWidth = getScaledWidth(); 924 clipSettings.rgbHeight = getScaledHeight(); 925 926 } 927 return clipSettings; 928 } 929 930 /** 931 * Resize a bitmap to the specified width and height 932 * 933 * @param filename The filename 934 * @param width The thumbnail width 935 * @param height The thumbnail height 936 * 937 * @return The resized bitmap 938 */ 939 private Bitmap scaleImage(String filename, int width, int height) 940 throws IOException { 941 final BitmapFactory.Options dbo = new BitmapFactory.Options(); 942 dbo.inJustDecodeBounds = true; 943 BitmapFactory.decodeFile(filename, dbo); 944 945 final int nativeWidth = dbo.outWidth; 946 final int nativeHeight = dbo.outHeight; 947 if (Log.isLoggable(TAG, Log.DEBUG)) { 948 Log.d(TAG, "generateThumbnail: Input: " + nativeWidth + "x" + nativeHeight 949 + ", resize to: " + width + "x" + height); 950 } 951 952 final Bitmap srcBitmap; 953 float bitmapWidth, bitmapHeight; 954 if (nativeWidth > width || nativeHeight > height) { 955 float dx = ((float)nativeWidth) / ((float)width); 956 float dy = ((float)nativeHeight) / ((float)height); 957 958 if (dx > dy) { 959 bitmapWidth = width; 960 961 if (((float)nativeHeight / dx) < (float)height) { 962 bitmapHeight = (float)Math.ceil(nativeHeight / dx); 963 } else { // value equals the requested height 964 bitmapHeight = (float)Math.floor(nativeHeight / dx); 965 } 966 967 } else { 968 if (((float)nativeWidth / dy) > (float)width) { 969 bitmapWidth = (float)Math.floor(nativeWidth / dy); 970 } else { // value equals the requested width 971 bitmapWidth = (float)Math.ceil(nativeWidth / dy); 972 } 973 974 bitmapHeight = height; 975 } 976 977 /** 978 * Create the bitmap from file 979 */ 980 if (nativeWidth / bitmapWidth > 1) { 981 982 final BitmapFactory.Options options = new BitmapFactory.Options(); 983 options.inSampleSize = nativeWidth / (int)bitmapWidth; 984 srcBitmap = BitmapFactory.decodeFile(filename, options); 985 } else { 986 srcBitmap = BitmapFactory.decodeFile(filename); 987 } 988 } else { 989 bitmapWidth = width; 990 bitmapHeight = height; 991 srcBitmap = BitmapFactory.decodeFile(filename); 992 993 } 994 995 if (srcBitmap == null) { 996 Log.e(TAG, "generateThumbnail: Cannot decode image bytes"); 997 throw new IOException("Cannot decode file: " + mFilename); 998 } 999 1000 /** 1001 * Create the canvas bitmap 1002 */ 1003 final Bitmap bitmap = Bitmap.createBitmap((int)bitmapWidth, 1004 (int)bitmapHeight, 1005 Bitmap.Config.ARGB_8888); 1006 final Canvas canvas = new Canvas(bitmap); 1007 canvas.drawBitmap(srcBitmap, new Rect(0, 0, srcBitmap.getWidth(), 1008 srcBitmap.getHeight()), 1009 new Rect(0, 0, (int)bitmapWidth, 1010 (int)bitmapHeight), sResizePaint); 1011 canvas.setBitmap(null); 1012 /** 1013 * Release the source bitmap 1014 */ 1015 srcBitmap.recycle(); 1016 return bitmap; 1017 } 1018} 1019