Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2009 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.cooliris.media;
     18 
     19 import java.util.Random;
     20 
     21 import android.content.Context;
     22 
     23 import com.cooliris.app.App;
     24 import com.cooliris.media.FloatUtils;
     25 
     26 /**
     27  * A simple structure for a MediaItem that can be rendered.
     28  */
     29 public final class DisplayItem {
     30     private static final float STACK_SPACING = 0.2f;
     31     private DirectLinkedList.Entry<DisplayItem> mAnimatablesEntry = new DirectLinkedList.Entry<DisplayItem>(this);
     32     private static final Random random = new Random();
     33     private Vector3f mStacktopPosition = new Vector3f(-1.0f, -1.0f, -1.0f);
     34     private Vector3f mJitteredPosition = new Vector3f();
     35     private boolean mHasFocus;
     36     private Vector3f mTargetPosition = new Vector3f();
     37     private float mTargetTheta;
     38     private float mImageTheta;
     39     private int mStackId;
     40     private MediaItemTexture mThumbnailImage = null;
     41     private Texture mScreennailImage = null;
     42     private UriTexture mHiResImage = null;
     43     private float mConvergenceSpeed = 1.0f;
     44 
     45     public final MediaItem mItemRef;
     46     public float mAnimatedTheta;
     47     public float mAnimatedImageTheta;
     48     public float mAnimatedPlaceholderFade = 0f;
     49     public boolean mAlive;
     50     public Vector3f mAnimatedPosition = new Vector3f();
     51     public int mCurrentSlotIndex;
     52     private boolean mPerformingScale;
     53     private float mSpan;
     54     private float mSpanDirection;
     55     private float mStartOffset;
     56     private float mSpanSpeed;
     57     private static final String TAG = "DisplayItem";
     58 
     59     public DisplayItem(MediaItem item) {
     60         mItemRef = item;
     61         mAnimatedImageTheta = item.mRotation;
     62         mImageTheta = item.mRotation;
     63         if (item == null)
     64             throw new UnsupportedOperationException("Cannot create a displayitem from a null MediaItem.");
     65         mCurrentSlotIndex = Shared.INVALID;
     66     }
     67 
     68     public DirectLinkedList.Entry<DisplayItem> getAnimatablesEntry() {
     69         return mAnimatablesEntry;
     70     }
     71 
     72     public final void rotateImageBy(float theta) {
     73         mImageTheta += theta;
     74     }
     75 
     76     public final void set(Vector3f position, int stackIndex, boolean performTransition) {
     77         mConvergenceSpeed = 1.0f;
     78         Vector3f animatedPosition = mAnimatedPosition;
     79         Vector3f targetPosition = mTargetPosition;
     80         int seed = stackIndex;
     81         int randomSeed = stackIndex;
     82 
     83         if (seed > 3) {
     84             seed = 3;
     85             randomSeed = 0;
     86         }
     87 
     88         if (!mAlive) {
     89             animatedPosition.set(position);
     90             animatedPosition.z = -3.0f + stackIndex * STACK_SPACING;
     91         }
     92 
     93         targetPosition.set(position);
     94         if (mStackId != stackIndex && stackIndex >= 0) {
     95             mStackId = stackIndex;
     96         }
     97 
     98         if (randomSeed == 0) {
     99             if (stackIndex == 0) {
    100                 mTargetTheta = 0.0f;
    101             } else if (mTargetTheta == 0.0f){
    102                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
    103             }
    104             mTargetPosition.z = seed * STACK_SPACING;
    105             mJitteredPosition.set(0, 0, seed * STACK_SPACING);
    106         } else {
    107             int sign = (seed % 2 == 0) ? 1 : -1;
    108             if (seed != 0 && !mStacktopPosition.equals(position) && mTargetTheta == 0) {
    109                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
    110                 mJitteredPosition.x = sign * 12.0f * seed + (0.5f - random.nextFloat()) * 4 * seed;
    111                 mJitteredPosition.y = sign * 4 + ((sign == 1) ? -8.0f : sign * (random.nextFloat()) * 16.0f);
    112                 mJitteredPosition.x *= App.PIXEL_DENSITY;
    113                 mJitteredPosition.y *= App.PIXEL_DENSITY;
    114                 mJitteredPosition.z = seed * STACK_SPACING;
    115             }
    116         }
    117         mTargetPosition.add(mJitteredPosition);
    118         mStacktopPosition.set(position);
    119         mStartOffset = 0.0f;
    120     }
    121 
    122     public int getStackIndex() {
    123         return mStackId;
    124     }
    125 
    126     public Texture getThumbnailImage(Context context, MediaItemTexture.Config config) {
    127         MediaItemTexture texture = mThumbnailImage;
    128         if (texture == null && config != null) {
    129             if (mItemRef.mId != Shared.INVALID) {
    130                 texture = new MediaItemTexture(context, config, mItemRef);
    131             }
    132             mThumbnailImage = texture;
    133         }
    134         return texture;
    135     }
    136 
    137     public Texture getScreennailImage(Context context) {
    138         Texture texture = mScreennailImage;
    139         if (texture == null || texture.mState == Texture.STATE_ERROR) {
    140             MediaSet parentMediaSet = mItemRef.mParentMediaSet;
    141             if (parentMediaSet != null && parentMediaSet.mDataSource.getThumbnailCache() == LocalDataSource.sThumbnailCache) {
    142                 if (mItemRef.mId != Shared.INVALID && mItemRef.mId != 0) {
    143                     texture = new MediaItemTexture(context, null, mItemRef);
    144                 } else if (mItemRef.mContentUri != null) {
    145                     texture = new UriTexture(mItemRef.mContentUri);
    146                 }
    147             } else {
    148                 texture = new UriTexture(mItemRef.mScreennailUri);
    149                 ((UriTexture) texture).setCacheId(Utils.Crc64Long(mItemRef.mFilePath));
    150             }
    151             mScreennailImage = texture;
    152         }
    153         return texture;
    154     }
    155 
    156     public void clearScreennailImage() {
    157         if (mScreennailImage != null) {
    158             mScreennailImage = null;
    159             mHiResImage = null;
    160         }
    161     }
    162 
    163     public void clearHiResImage() {
    164         mHiResImage = null;
    165     }
    166 
    167     public void clearThumbnail() {
    168         mThumbnailImage = null;
    169     }
    170 
    171     /**
    172      * Use this function to query the animation state of the display item
    173      *
    174      * @return true if the display item is animating
    175      */
    176     public boolean isAnimating() {
    177         return mAlive
    178                 && (mPerformingScale ||
    179                         !mAnimatedPosition.equals(mTargetPosition) || mAnimatedTheta != mTargetTheta
    180                         || mAnimatedImageTheta != mImageTheta || mAnimatedPlaceholderFade != 1f);
    181     }
    182 
    183     /**
    184      * This function should be called every time the frame needs to be updated.
    185      */
    186     public final void update(float timeElapsedInSec) {
    187         if (mAlive) {
    188             timeElapsedInSec *= 1.25f;
    189             Vector3f animatedPosition = mAnimatedPosition;
    190             Vector3f targetPosition = mTargetPosition;
    191             timeElapsedInSec *= mConvergenceSpeed;
    192             animatedPosition.x = FloatUtils.animate(animatedPosition.x, targetPosition.x, timeElapsedInSec);
    193             animatedPosition.y = FloatUtils.animate(animatedPosition.y, targetPosition.y, timeElapsedInSec);
    194             mAnimatedTheta = FloatUtils.animate(mAnimatedTheta, mTargetTheta, timeElapsedInSec);
    195             mAnimatedImageTheta = FloatUtils.animate(mAnimatedImageTheta, mImageTheta, timeElapsedInSec);
    196             mAnimatedPlaceholderFade = FloatUtils.animate(mAnimatedPlaceholderFade, 1f, timeElapsedInSec);
    197             animatedPosition.z = FloatUtils.animate(animatedPosition.z, targetPosition.z, timeElapsedInSec);
    198         }
    199     }
    200 
    201     /**
    202      * Commits all animations for the Display Item
    203      */
    204     public final void commit() {
    205         mAnimatedPosition.set(mTargetPosition);
    206         mAnimatedTheta = mTargetTheta;
    207         mAnimatedImageTheta = mImageTheta;
    208     }
    209 
    210     public final void setHasFocus(boolean hasFocus, boolean pushDown) {
    211         mConvergenceSpeed = 2.0f;
    212         mHasFocus = hasFocus;
    213         int seed = mStackId;
    214         if (seed > 3) {
    215             seed = 3;
    216         }
    217         if (hasFocus) {
    218             mTargetPosition.set(mStacktopPosition);
    219             mTargetPosition.add(mJitteredPosition);
    220             mTargetPosition.add(mJitteredPosition);
    221             mTargetPosition.z = seed * STACK_SPACING + (pushDown ? 1.0f : -0.5f);
    222         } else {
    223             mTargetPosition.set(mStacktopPosition);
    224             mTargetPosition.add(mJitteredPosition);
    225             mTargetPosition.z = seed * STACK_SPACING;
    226         }
    227     }
    228 
    229     public final void setSingleOffset(boolean useOffset, boolean pushAway, float x, float y, float z, float spreadValue) {
    230         int seed = mStackId;
    231         if (useOffset) {
    232             mTargetPosition.set(mStacktopPosition);
    233             if (spreadValue > 4.0f)
    234                 spreadValue = 4.0f + 0.1f * spreadValue;
    235             if (spreadValue < 1.0f) {
    236                 spreadValue = 1.0f / spreadValue;
    237                 pushAway = true;
    238             }
    239             if (!pushAway) {
    240                 if (seed == 0) {
    241                     mTargetPosition.add(0, -spreadValue * 14, 0);
    242                 }
    243                 if (seed == 1) {
    244                     mTargetPosition.add(-spreadValue * 32, 0, 0);
    245                 }
    246                 if (seed == 2) {
    247                     mTargetPosition.add(0, spreadValue * 14, 0);
    248                 }
    249                 if (seed == 3) {
    250                     mTargetPosition.add(spreadValue * 32, 0, 0);
    251                 }
    252                 mTargetPosition.z = -1.0f * spreadValue + seed * STACK_SPACING * spreadValue;
    253                 mTargetTheta = 0.0f;
    254             } else {
    255                 mTargetPosition.z = seed * STACK_SPACING + spreadValue * 0.5f;
    256             }
    257         } else {
    258             if (seed > 3) {
    259                 seed = 3;
    260             }
    261             mTargetPosition.set(mStacktopPosition);
    262             mTargetPosition.add(mJitteredPosition);
    263             mTargetPosition.z = seed * STACK_SPACING;
    264             if (seed != 0 && mTargetTheta == 0.0f) {
    265                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
    266             }
    267             mStartOffset = 0.0f;
    268         }
    269     }
    270 
    271     public final void setOffset(boolean useOffset, boolean pushDown, float span, float dx1, float dy1, float dx2, float dy2) {
    272         int seed = mStackId;
    273         if (useOffset) {
    274             mPerformingScale = true;
    275             float spanDelta = span - mSpan;
    276             float maxSlots = mItemRef.mParentMediaSet.getNumExpectedItems();
    277             maxSlots = FloatUtils.clamp(maxSlots, 0, GridLayer.MAX_ITEMS_PER_SLOT);
    278             if (Math.abs(spanDelta) < 10 * App.PIXEL_DENSITY) {
    279                 // almost the same span
    280                 mStartOffset += (mSpanDirection * mSpanSpeed);
    281                 mStartOffset = FloatUtils.clamp(mStartOffset, 0, maxSlots);
    282             } else {
    283                 mSpanSpeed = Math.abs(span / (600 * App.PIXEL_DENSITY));
    284                 if (mSpanSpeed > 2.0f) {
    285                     mSpanSpeed = 2.0f;
    286                 }
    287                 mSpanSpeed *= 0.1f;
    288                 mSpanDirection = Math.signum(spanDelta);
    289             }
    290             mSpan = span;
    291             mTargetPosition.set(mStacktopPosition);
    292             if (!pushDown) {
    293                 if (maxSlots < 2)
    294                     return;
    295                 // If it is the stacktop, we track the top finger, ie, x1, y1
    296                 // else
    297                 // we track bottom finger x2, y2
    298                 // Instead of using linear interpolation, we will also try to
    299                 // look at the spread value to decide how many move at a given
    300                 // point of time.
    301                 int maxSeedVal = (int)(span / (125 * App.PIXEL_DENSITY));
    302                 maxSeedVal = (int)FloatUtils.clamp(maxSeedVal, 2, maxSlots - 1);
    303                 float startOffset = FloatUtils.clamp(mStartOffset, 0, maxSlots - maxSeedVal - 1);
    304                 float offsetSeed = seed - startOffset;
    305                 float seedFactor = offsetSeed / maxSeedVal;
    306                 seedFactor = FloatUtils.clamp(seedFactor, 0.0f, 1.0f);
    307                 float dx = dx2 * seedFactor + (1.0f - seedFactor) * dx1;
    308                 float dy = dy2 * seedFactor + (1.0f - seedFactor) * dy1;
    309                 mTargetPosition.add(dx, dy, seed * 0.1f);
    310                 mTargetTheta = 0.0f;
    311             } else {
    312                 mStartOffset = 0.0f;
    313                 mTargetPosition.z = seed * STACK_SPACING + 3.0f;
    314             }
    315         } else {
    316             mPerformingScale = false;
    317             mStartOffset = 0.0f;
    318             if (seed > 3) {
    319                 seed = 3;
    320             }
    321             mTargetPosition.set(mStacktopPosition);
    322             mTargetPosition.add(mJitteredPosition);
    323             mTargetPosition.z = seed * STACK_SPACING;
    324             if (seed != 0 && mTargetTheta == 0.0f) {
    325                 mTargetTheta = 30.0f * (0.5f - (float) Math.random());
    326             }
    327         }
    328     }
    329 
    330     public final boolean getHasFocus() {
    331         return mHasFocus;
    332     }
    333 
    334     public final Texture getHiResImage(Context context) {
    335         UriTexture texture = mHiResImage;
    336         if (texture == null) {
    337             texture = new UriTexture(mItemRef.mContentUri);
    338             texture.setCacheId(Utils.Crc64Long(mItemRef.mFilePath));
    339             mHiResImage = texture;
    340         }
    341         return texture;
    342     }
    343 
    344     public boolean isAlive() {
    345         return mAlive;
    346     }
    347 
    348     public float getImageTheta() {
    349         return mImageTheta;
    350     }
    351 }
    352