Home | History | Annotate | Download | only in videoeditor
      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