1 /* 2 * Copyright (C) 2010 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 18 package android.media.videoeditor; 19 20 import java.io.File; 21 import java.io.FileNotFoundException; 22 import java.io.FileOutputStream; 23 import java.io.IOException; 24 import java.io.DataOutputStream; 25 import java.nio.ByteBuffer; 26 import java.nio.IntBuffer; 27 28 import android.graphics.Canvas; 29 import android.graphics.Paint; 30 import android.graphics.Rect; 31 import android.graphics.Bitmap; 32 import android.graphics.BitmapFactory; 33 import android.graphics.Bitmap.CompressFormat; 34 import android.util.Pair; 35 36 37 /** 38 * This class is used to overlay an image on top of a media item. 39 * {@hide} 40 */ 41 public class OverlayFrame extends Overlay { 42 /** 43 * Instance variables 44 */ 45 private Bitmap mBitmap; 46 private String mFilename; 47 private String mBitmapFileName; 48 49 private int mOFWidth; 50 private int mOFHeight; 51 52 /** 53 * resized RGB Image dimensions 54 */ 55 private int mResizedRGBWidth; 56 private int mResizedRGBHeight; 57 58 /** 59 * The resize paint 60 */ 61 private static final Paint sResizePaint = new Paint(Paint.FILTER_BITMAP_FLAG); 62 63 /** 64 * An object of this type cannot be instantiated by using the default 65 * constructor 66 */ 67 @SuppressWarnings("unused") 68 private OverlayFrame() { 69 this(null, null, (String)null, 0, 0); 70 } 71 72 /** 73 * Constructor for an OverlayFrame 74 * 75 * @param mediaItem The media item owner 76 * @param overlayId The overlay id 77 * @param bitmap The bitmap to be used as an overlay. The size of the 78 * bitmap must equal to the size of the media item to which it is 79 * added. The bitmap is typically a decoded PNG file. 80 * @param startTimeMs The overlay start time in milliseconds 81 * @param durationMs The overlay duration in milliseconds 82 * 83 * @throws IllegalArgumentException if the file type is not PNG or the 84 * startTimeMs and durationMs are incorrect. 85 */ 86 public OverlayFrame(MediaItem mediaItem, String overlayId, Bitmap bitmap, 87 long startTimeMs,long durationMs) { 88 super(mediaItem, overlayId, startTimeMs, durationMs); 89 mBitmap = bitmap; 90 mFilename = null; 91 mBitmapFileName = null; 92 mResizedRGBWidth = 0; 93 mResizedRGBHeight = 0; 94 } 95 96 /** 97 * Constructor for an OverlayFrame. This constructor can be used to 98 * restore the overlay after it was saved internally by the video editor. 99 * 100 * @param mediaItem The media item owner 101 * @param overlayId The overlay id 102 * @param filename The file name that contains the overlay. 103 * @param startTimeMs The overlay start time in milliseconds 104 * @param durationMs The overlay duration in milliseconds 105 * 106 * @throws IllegalArgumentException if the file type is not PNG or the 107 * startTimeMs and durationMs are incorrect. 108 */ 109 OverlayFrame(MediaItem mediaItem, String overlayId, String filename, 110 long startTimeMs,long durationMs) { 111 super(mediaItem, overlayId, startTimeMs, durationMs); 112 mBitmapFileName = filename; 113 mBitmap = BitmapFactory.decodeFile(mBitmapFileName); 114 mFilename = null; 115 mResizedRGBWidth = 0; 116 mResizedRGBHeight = 0; 117 } 118 119 /** 120 * Get the overlay bitmap. 121 * 122 * @return Get the overlay bitmap 123 */ 124 public Bitmap getBitmap() { 125 return mBitmap; 126 } 127 128 /** 129 * Get the overlay bitmap. 130 * 131 * @return Get the overlay bitmap as png file. 132 */ 133 String getBitmapImageFileName() { 134 return mBitmapFileName; 135 } 136 /** 137 * Set the overlay bitmap. 138 * 139 * @param bitmap The overlay bitmap. 140 */ 141 public void setBitmap(Bitmap bitmap) { 142 getMediaItem().getNativeContext().setGeneratePreview(true); 143 144 invalidate(); 145 146 mBitmap = bitmap; 147 if (mFilename != null) { 148 /** 149 * Delete the file 150 */ 151 new File(mFilename).delete(); 152 /** 153 * Invalidate the filename 154 */ 155 mFilename = null; 156 } 157 158 /** 159 * Invalidate the transitions if necessary 160 */ 161 getMediaItem().invalidateTransitions(mStartTimeMs, mDurationMs); 162 } 163 164 /** 165 * Get the file name of this overlay 166 */ 167 String getFilename() { 168 return mFilename; 169 } 170 171 /* 172 * Set the file name of this overlay 173 */ 174 void setFilename(String filename) { 175 mFilename = filename; 176 } 177 /** 178 * Save the overlay to the project folder 179 * 180 * @param path The path where the overlay will be saved 181 * 182 * @return The filename 183 * @throws FileNotFoundException if the bitmap cannot be saved 184 * @throws IOException if the bitmap file cannot be saved 185 */ 186 String save(String path) throws FileNotFoundException, IOException { 187 if (mFilename != null) { 188 return mFilename; 189 } 190 191 // Create the compressed PNG file 192 mBitmapFileName = path + "/" + "Overlay" + getId() + ".png"; 193 if (!(new File(mBitmapFileName).exists())) { 194 final FileOutputStream out = new FileOutputStream (mBitmapFileName); 195 mBitmap.compress(CompressFormat.PNG, 100, out); 196 out.flush(); 197 out.close(); 198 } 199 200 mOFWidth = mBitmap.getWidth(); 201 mOFHeight = mBitmap.getHeight(); 202 203 mFilename = path + "/" + "Overlay" + getId() + ".rgb"; 204 205 /* resize and save rgb as per project aspect ratio */ 206 MediaArtistNativeHelper nativeHelper = (super.getMediaItem()).getNativeContext(); 207 208 /* get height and width for story board aspect ratio */ 209 final Pair<Integer, Integer> maxResolution; 210 final Pair<Integer, Integer>[] resolutions; 211 resolutions = MediaProperties.getSupportedResolutions(nativeHelper.nativeHelperGetAspectRatio()); 212 213 // Get the highest resolution 214 maxResolution = resolutions[resolutions.length - 1]; 215 216 /* Generate the rgb file with rendering mode */ 217 generateOverlayWithRenderingMode (super.getMediaItem(), this, 218 maxResolution.second /* max Height */ , 219 maxResolution.first /* max Width */); 220 221 return mFilename; 222 } 223 224 /** 225 * Get the OverlayFrame Height 226 */ 227 int getOverlayFrameHeight() { 228 return mOFHeight; 229 } 230 231 /** 232 * Get the OverlayFrame Width 233 */ 234 int getOverlayFrameWidth() { 235 return mOFWidth; 236 } 237 238 /* 239 * Set the OverlayFrame Height 240 */ 241 void setOverlayFrameHeight(int height) { 242 mOFHeight = height; 243 } 244 245 /* 246 * Set the OverlayFrame Width 247 */ 248 void setOverlayFrameWidth(int width) { 249 mOFWidth = width; 250 } 251 252 /* 253 * Set the resized RGB widht and height 254 */ 255 void setResizedRGBSize(int width, int height) { 256 mResizedRGBWidth = width; 257 mResizedRGBHeight = height; 258 } 259 260 /* 261 * Get the resized RGB Height 262 */ 263 int getResizedRGBSizeHeight() { 264 return mResizedRGBHeight; 265 } 266 267 /* 268 * Get the resized RGB Width 269 */ 270 int getResizedRGBSizeWidth() { 271 return mResizedRGBWidth; 272 } 273 274 275 /** 276 * Delete the overlay files 277 */ 278 void invalidate() { 279 if (mBitmap != null) { 280 mBitmap.recycle(); 281 mBitmap = null; 282 } 283 284 if (mFilename != null) { 285 new File(mFilename).delete(); 286 mFilename = null; 287 } 288 289 if (mBitmapFileName != null) { 290 new File(mBitmapFileName).delete(); 291 mBitmapFileName = null; 292 } 293 } 294 295 /** 296 * Delete the overlay related files 297 */ 298 void invalidateGeneratedFiles() { 299 if (mFilename != null) { 300 new File(mFilename).delete(); 301 mFilename = null; 302 } 303 304 if (mBitmapFileName != null) { 305 new File(mBitmapFileName).delete(); 306 mBitmapFileName = null; 307 } 308 } 309 310 void generateOverlayWithRenderingMode (MediaItem mediaItemsList, OverlayFrame overlay, int height , int width) 311 throws FileNotFoundException, IOException { 312 313 final MediaItem t = mediaItemsList; 314 315 /* get the rendering mode */ 316 int renderMode = t.getRenderingMode(); 317 318 Bitmap overlayBitmap = ((OverlayFrame)overlay).getBitmap(); 319 320 /* 321 * Check if the resize of Overlay is needed with rendering mode applied 322 * because of change in export dimensions 323 */ 324 int resizedRGBFileHeight = ((OverlayFrame)overlay).getResizedRGBSizeHeight(); 325 int resizedRGBFileWidth = ((OverlayFrame)overlay).getResizedRGBSizeWidth(); 326 327 /* Get original bitmap width if it is not resized */ 328 if(resizedRGBFileWidth == 0) { 329 resizedRGBFileWidth = overlayBitmap.getWidth(); 330 } 331 /* Get original bitmap height if it is not resized */ 332 if(resizedRGBFileHeight == 0) { 333 resizedRGBFileHeight = overlayBitmap.getHeight(); 334 } 335 336 if (resizedRGBFileWidth != width || resizedRGBFileHeight != height 337 || (!(new File(((OverlayFrame)overlay).getFilename()).exists()))) { 338 /* 339 * Create the canvas bitmap 340 */ 341 final Bitmap destBitmap = Bitmap.createBitmap((int)width, 342 (int)height, 343 Bitmap.Config.ARGB_8888); 344 final Canvas overlayCanvas = new Canvas(destBitmap); 345 final Rect destRect; 346 final Rect srcRect; 347 348 switch (renderMode) { 349 case MediaItem.RENDERING_MODE_STRETCH: { 350 destRect = new Rect(0, 0, overlayCanvas.getWidth(), 351 overlayCanvas.getHeight()); 352 srcRect = new Rect(0, 0, overlayBitmap.getWidth(), 353 overlayBitmap.getHeight()); 354 break; 355 } 356 357 case MediaItem.RENDERING_MODE_BLACK_BORDER: { 358 int left, right, top, bottom; 359 float aROverlayImage, aRCanvas; 360 aROverlayImage = (float)(overlayBitmap.getWidth()) / 361 (float)(overlayBitmap.getHeight()); 362 363 aRCanvas = (float)(overlayCanvas.getWidth()) / 364 (float)(overlayCanvas.getHeight()); 365 366 if (aROverlayImage > aRCanvas) { 367 int newHeight = ((overlayCanvas.getWidth() * overlayBitmap.getHeight()) 368 / overlayBitmap.getWidth()); 369 left = 0; 370 top = (overlayCanvas.getHeight() - newHeight) / 2; 371 right = overlayCanvas.getWidth(); 372 bottom = top + newHeight; 373 } else { 374 int newWidth = ((overlayCanvas.getHeight() * overlayBitmap.getWidth()) 375 / overlayBitmap.getHeight()); 376 left = (overlayCanvas.getWidth() - newWidth) / 2; 377 top = 0; 378 right = left + newWidth; 379 bottom = overlayCanvas.getHeight(); 380 } 381 382 destRect = new Rect(left, top, right, bottom); 383 srcRect = new Rect(0, 0, overlayBitmap.getWidth(), overlayBitmap.getHeight()); 384 break; 385 } 386 387 case MediaItem.RENDERING_MODE_CROPPING: { 388 // Calculate the source rect 389 int left, right, top, bottom; 390 float aROverlayImage, aRCanvas; 391 aROverlayImage = (float)(overlayBitmap.getWidth()) / 392 (float)(overlayBitmap.getHeight()); 393 aRCanvas = (float)(overlayCanvas.getWidth()) / 394 (float)(overlayCanvas.getHeight()); 395 if (aROverlayImage < aRCanvas) { 396 int newHeight = ((overlayBitmap.getWidth() * overlayCanvas.getHeight()) 397 / overlayCanvas.getWidth()); 398 399 left = 0; 400 top = (overlayBitmap.getHeight() - newHeight) / 2; 401 right = overlayBitmap.getWidth(); 402 bottom = top + newHeight; 403 } else { 404 int newWidth = ((overlayBitmap.getHeight() * overlayCanvas.getWidth()) 405 / overlayCanvas.getHeight()); 406 left = (overlayBitmap.getWidth() - newWidth) / 2; 407 top = 0; 408 right = left + newWidth; 409 bottom = overlayBitmap.getHeight(); 410 } 411 412 srcRect = new Rect(left, top, right, bottom); 413 destRect = new Rect(0, 0, overlayCanvas.getWidth(), overlayCanvas.getHeight()); 414 break; 415 } 416 417 default: { 418 throw new IllegalStateException("Rendering mode: " + renderMode); 419 } 420 } 421 422 overlayCanvas.drawBitmap(overlayBitmap, srcRect, destRect, sResizePaint); 423 overlayCanvas.setBitmap(null); 424 425 /* 426 * Write to the dest file 427 */ 428 String outFileName = ((OverlayFrame)overlay).getFilename(); 429 430 /* 431 * Save the image to same rgb file 432 */ 433 if (outFileName != null) { 434 new File(outFileName).delete(); 435 } 436 437 final FileOutputStream fl = new FileOutputStream(outFileName); 438 final DataOutputStream dos = new DataOutputStream(fl); 439 440 /* 441 * Populate the rgb file with bitmap data 442 */ 443 final int [] framingBuffer = new int[width]; 444 ByteBuffer byteBuffer = ByteBuffer.allocate(framingBuffer.length * 4); 445 IntBuffer intBuffer; 446 447 byte[] array = byteBuffer.array(); 448 int tmp = 0; 449 while(tmp < height) { 450 destBitmap.getPixels(framingBuffer,0,width,0,tmp,width,1); 451 intBuffer = byteBuffer.asIntBuffer(); 452 intBuffer.put(framingBuffer,0,width); 453 dos.write(array); 454 tmp += 1; 455 } 456 fl.flush(); 457 fl.close(); 458 459 /* 460 * Set the resized RGB width and height 461 */ 462 ((OverlayFrame)overlay).setResizedRGBSize(width, height); 463 } 464 } 465 } 466