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.Arrays;
     20 import java.util.Comparator;
     21 import java.util.HashMap;
     22 
     23 import javax.microedition.khronos.opengles.GL10;
     24 import javax.microedition.khronos.opengles.GL11;
     25 
     26 import android.content.Context;
     27 
     28 import com.cooliris.app.App;
     29 import com.cooliris.app.Res;
     30 
     31 public final class GridDrawManager {
     32     public static final int PASS_THUMBNAIL_CONTENT = 0;
     33     public static final int PASS_FOCUS_CONTENT = 1;
     34     public static final int PASS_FRAME = 2;
     35     public static final int PASS_PLACEHOLDER = 3;
     36     public static final int PASS_FRAME_PLACEHOLDER = 4;
     37     public static final int PASS_TEXT_LABEL = 5;
     38     public static final int PASS_SELECTION_LABEL = 6;
     39     public static final int PASS_VIDEO_LABEL = 7;
     40     public static final int PASS_LOCATION_LABEL = 8;
     41     public static final int PASS_MEDIASET_SOURCE_LABEL = 9;
     42 
     43     private static final MediaItemTexture.Config sThumbnailConfig = new MediaItemTexture.Config();
     44     private final DisplayItem[] mDisplayItems;
     45     private final DisplaySlot[] mDisplaySlots;
     46     private final DisplayList mDisplayList;
     47     private final GridCamera mCamera;
     48     private final GridDrawables mDrawables;
     49     private IndexRange mBufferedVisibleRange;
     50     private IndexRange mVisibleRange;
     51     private int mSelectedSlot;
     52     private int mCurrentFocusSlot;
     53     private DisplayItem[] mItemsDrawn;
     54     private int mDrawnCounter;
     55     private float mTargetFocusMixRatio = 0.0f;
     56     private float mFocusMixRatio = 0.0f;
     57     private final FloatAnim mSelectedMixRatio = new FloatAnim(0f);
     58     private float mCurrentFocusItemWidth;
     59     private float mCurrentFocusItemHeight;
     60     private boolean mCurrentFocusIsPressed;
     61     private final Texture mNoItemsTexture;
     62     private int mCurrentScaleSlot;
     63     private float mSpreadValue;
     64     private ScaleGestureDetector mScaleGestureDetector;
     65     private boolean mHoldPosition;
     66 
     67     private static final Comparator<DisplayItem> sDisplayItemComparator = new Comparator<DisplayItem>() {
     68         public int compare(DisplayItem a, DisplayItem b) {
     69             if (a == null || b == null) {
     70                 return 0;
     71             }
     72             float delta = (a.mAnimatedPosition.z - b.mAnimatedPosition.z);
     73             if (delta > 0) {
     74                 return 1;
     75             } else if (delta < 0) {
     76                 return -1;
     77             } else {
     78                 return 0;
     79             }
     80         }
     81     };
     82 
     83     public GridDrawManager(Context context, GridCamera camera, GridDrawables drawables, DisplayList displayList,
     84             DisplayItem[] displayItems, DisplaySlot[] displaySlots) {
     85         sThumbnailConfig.thumbnailWidth = 128;
     86         sThumbnailConfig.thumbnailHeight = 96;
     87         mDisplayItems = displayItems;
     88         mDisplaySlots = displaySlots;
     89         mDisplayList = displayList;
     90         mDrawables = drawables;
     91         mCamera = camera;
     92         mItemsDrawn = new DisplayItem[GridLayer.MAX_ITEMS_DRAWABLE];
     93 
     94         StringTexture.Config stc = new StringTexture.Config();
     95         stc.bold = true;
     96         stc.fontSize = 16 * App.PIXEL_DENSITY;
     97         stc.sizeMode = StringTexture.Config.SIZE_EXACT;
     98         stc.overflowMode = StringTexture.Config.OVERFLOW_FADE;
     99         mNoItemsTexture = new StringTexture(context.getResources().getString(Res.string.no_items), stc);
    100 
    101     }
    102 
    103     public void prepareDraw(IndexRange bufferedVisibleRange, IndexRange visibleRange, int selectedSlot, int currentFocusSlot,
    104             int currentScaleSlot, boolean currentFocusIsPressed, float spreadValue, ScaleGestureDetector scaleGestureDetector,
    105             boolean holdPosition) {
    106         mBufferedVisibleRange = bufferedVisibleRange;
    107         mVisibleRange = visibleRange;
    108         mSelectedSlot = selectedSlot;
    109         mCurrentFocusSlot = currentFocusSlot;
    110         mCurrentFocusIsPressed = currentFocusIsPressed;
    111         mCurrentScaleSlot = currentScaleSlot;
    112         mScaleGestureDetector = scaleGestureDetector;
    113         mSpreadValue = spreadValue;
    114         mHoldPosition = holdPosition;
    115     }
    116 
    117     public boolean update(float timeElapsed) {
    118         mFocusMixRatio = FloatUtils.animate(mFocusMixRatio, mTargetFocusMixRatio, timeElapsed);
    119         mTargetFocusMixRatio = 0.0f;
    120         if (mFocusMixRatio != mTargetFocusMixRatio || mSelectedMixRatio.isAnimating()) {
    121             return true;
    122         }
    123         return false;
    124     }
    125 
    126     public void drawThumbnails(RenderView view, GL11 gl, int state) {
    127         final GridDrawables drawables = mDrawables;
    128         final DisplayList displayList = mDisplayList;
    129         final DisplayItem[] displayItems = mDisplayItems;
    130         final int firstBufferedVisibleSlot = mBufferedVisibleRange.begin;
    131         final int lastBufferedVisibleSlot = mBufferedVisibleRange.end;
    132         final int firstVisibleSlot = mVisibleRange.begin;
    133         final int lastVisibleSlot = mVisibleRange.end;
    134         final int selectedSlotIndex = mSelectedSlot;
    135         final int currentFocusSlot = mCurrentFocusSlot;
    136         final int currentScaleSlot = mCurrentScaleSlot;
    137         final DisplayItem[] itemsDrawn = mItemsDrawn;
    138         itemsDrawn[0] = null; // No items drawn yet.
    139         int drawnCounter = 0;
    140         final GridQuad grid = GridDrawables.sGrid;
    141         grid.bindArrays(gl);
    142         int numTexturesQueued = 0;
    143         Context context = view.getContext();
    144         for (int itrSlotIndex = firstBufferedVisibleSlot; itrSlotIndex <= lastBufferedVisibleSlot; ++itrSlotIndex) {
    145             int index = itrSlotIndex;
    146             boolean priority = !(index < firstVisibleSlot || index > lastVisibleSlot);
    147             int startSlotIndex = 0;
    148             final int maxDisplayedItemsPerSlot = (index == mCurrentScaleSlot) ? GridLayer.MAX_DISPLAYED_ITEMS_PER_FOCUSED_SLOT
    149                     : GridLayer.MAX_DISPLAYED_ITEMS_PER_SLOT;
    150             if (index != mCurrentScaleSlot) {
    151                 for (int j = maxDisplayedItemsPerSlot - 1; j >= 0; --j) {
    152                     DisplayItem displayItem = displayItems[(index - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT + j];
    153                     if (displayItem == null) {
    154                         continue;
    155                     } else {
    156                         Texture texture = displayItem.getThumbnailImage(context, sThumbnailConfig);
    157                         if (texture != null && texture.isLoaded() == false) {
    158                             startSlotIndex = j;
    159                             break;
    160                         }
    161                     }
    162                 }
    163             }
    164             // Prime the textures in the reverse order.
    165             for (int j = 0; j < maxDisplayedItemsPerSlot; ++j) {
    166                 int stackIndex = (index == mCurrentScaleSlot) ? maxDisplayedItemsPerSlot - j - 1 : j;
    167                 DisplayItem displayItem = displayItems[(index - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT
    168                         + stackIndex];
    169                 if (displayItem == null) {
    170                     continue;
    171                 } else {
    172                     displayItem.mCurrentSlotIndex = index;
    173                     if (selectedSlotIndex != Shared.INVALID && (index <= selectedSlotIndex - 2 || index >= selectedSlotIndex + 2)) {
    174                         displayItem.clearScreennailImage();
    175                     }
    176                     Texture texture = displayItem.getThumbnailImage(context, sThumbnailConfig);
    177                     if (index == mCurrentScaleSlot && texture != null && !texture.isLoaded()) {
    178                         view.prime(texture, true);
    179                         view.bind(texture);
    180                     } else if (texture != null && !texture.isLoaded() && numTexturesQueued <= 6) {
    181                         boolean isCached = texture.isCached();
    182                         view.prime(texture, priority);
    183                         view.bind(texture);
    184                         if (priority && isCached && texture.mState != Texture.STATE_ERROR)
    185                             ++numTexturesQueued;
    186                     }
    187                 }
    188             }
    189             if (itrSlotIndex == selectedSlotIndex) {
    190                 continue;
    191             }
    192             view.prime(drawables.mTexturePlaceholder, true);
    193             Texture placeholder = (state == GridLayer.STATE_GRID_VIEW) ? drawables.mTexturePlaceholder : null;
    194             final boolean pushDown = (state == GridLayer.STATE_GRID_VIEW || state == GridLayer.STATE_FULL_SCREEN) ? false : true;
    195             for (int j = 0; j < GridLayer.MAX_ITEMS_PER_SLOT; ++j) {
    196                 DisplayItem displayItem = displayItems[(index - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT + j];
    197                 if (displayItem == null)
    198                     continue;
    199                 Texture texture = displayItem.getThumbnailImage(context, sThumbnailConfig);
    200                 if (texture == null || !texture.isLoaded()) {
    201                     if (currentScaleSlot != index) {
    202                         if (j == 0) {
    203                             final MediaSet parentSet = displayItem.mItemRef.mParentMediaSet;
    204                             if (parentSet != null && parentSet.getNumItems() <= 1) {
    205                                 displayList.setAlive(displayItem, false);
    206                             }
    207                         } else {
    208                             displayList.setAlive(displayItem, false);
    209                         }
    210                     }
    211                 }
    212                 final float dx1 = mScaleGestureDetector.getTopFingerDeltaX();
    213                 final float dy1 = mScaleGestureDetector.getTopFingerDeltaY();
    214                 final float dx2 = mScaleGestureDetector.getBottomFingerDeltaX();
    215                 final float dy2 = mScaleGestureDetector.getBottomFingerDeltaY();
    216                 final float span = mScaleGestureDetector.getCurrentSpan();
    217                 if (state == GridLayer.STATE_FULL_SCREEN) {
    218                     displayList.setOffset(displayItem, false, true, span, dx1, dy1, dx2, dy2);
    219                 } else {
    220                     if (!mHoldPosition) {
    221                         if (state != GridLayer.STATE_GRID_VIEW) {
    222                             if (currentScaleSlot == index) {
    223                                 displayList.setOffset(displayItem, true, false, span, dx1, dy1, dx2, dy2);
    224                             } else if (currentScaleSlot != Shared.INVALID) {
    225                                 displayList.setOffset(displayItem, true, true, span, dx1, dy1, dx2, dy2);
    226                             } else {
    227                                 displayList.setOffset(displayItem, false, false, span, dx1, dy1, dx2, dy2);
    228                             }
    229                         } else {
    230                             float minVal = -1.0f;
    231                             float maxVal = GridCamera.EYE_Z * 0.5f;
    232                             float zVal = minVal + mSpreadValue;
    233                             zVal = FloatUtils.clamp(zVal, minVal, maxVal);
    234                             if (Float.isInfinite(zVal) || Float.isNaN(zVal)) {
    235                                 mCamera.moveZTo(0);
    236                             } else {
    237                                 mCamera.moveZTo(-zVal);
    238                             }
    239                         }
    240                     }
    241                 }
    242             }
    243             for (int j = startSlotIndex; j < GridLayer.MAX_ITEMS_PER_SLOT; ++j) {
    244                 DisplayItem displayItem = displayItems[(index - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT + j];
    245                 if (displayItem == null) {
    246                     break;
    247                 } else {
    248                     if (currentFocusSlot == index) {
    249                         displayList.setHasFocus(displayItem, true, pushDown);
    250                         mTargetFocusMixRatio = 1.0f;
    251                     } else {
    252                         displayList.setHasFocus(displayItem, false, pushDown);
    253                     }
    254                     if (j >= maxDisplayedItemsPerSlot)
    255                         continue;
    256                     Texture texture = displayItem.getThumbnailImage(view.getContext(), sThumbnailConfig);
    257                     if (texture != null) {
    258                         if (index == mCurrentScaleSlot)
    259                             displayItem.mAlive = true;
    260                         if ((!displayItem.isAnimating() || !texture.isLoaded())
    261                                 && displayItem.getStackIndex() > GridLayer.MAX_ITEMS_PER_SLOT) {
    262                             displayList.setAlive(displayItem, true);
    263                             continue;
    264                         }
    265                         if (index < firstVisibleSlot || index > lastVisibleSlot) {
    266                             if (view.bind(texture)) {
    267                                 displayList.setAlive(displayItem, true);
    268                             }
    269                             continue;
    270                         }
    271                         drawDisplayItem(view, gl, displayItem, texture, PASS_THUMBNAIL_CONTENT, placeholder,
    272                                 displayItem.mAnimatedPlaceholderFade);
    273                     } else {
    274                         // Move on to the next stack.
    275                         break;
    276                     }
    277                     if (drawnCounter >= GridLayer.MAX_ITEMS_DRAWABLE - 1 || drawnCounter < 0) {
    278                         break;
    279                     }
    280                     // Insert in order of z.
    281                     itemsDrawn[drawnCounter++] = displayItem;
    282                     itemsDrawn[drawnCounter] = null;
    283                 }
    284             }
    285         }
    286         mDrawnCounter = drawnCounter;
    287         grid.unbindArrays(gl);
    288     }
    289 
    290     public float getFocusQuadWidth() {
    291         return mCurrentFocusItemWidth;
    292     }
    293 
    294     public float getFocusQuadHeight() {
    295         return mCurrentFocusItemHeight;
    296     }
    297 
    298     public void drawFocusItems(RenderView view, GL11 gl, float zoomValue, boolean slideshowMode, float timeElapsedSinceView) {
    299         int selectedSlotIndex = mSelectedSlot;
    300         GridDrawables drawables = mDrawables;
    301         GridCamera camera = mCamera;
    302         DisplayItem[] displayItems = mDisplayItems;
    303         int firstBufferedVisibleSlot = mBufferedVisibleRange.begin;
    304         int lastBufferedVisibleSlot = mBufferedVisibleRange.end;
    305         boolean isCameraZAnimating = mCamera.isZAnimating();
    306         for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    307             if (selectedSlotIndex != Shared.INVALID && (i >= selectedSlotIndex - 2 && i <= selectedSlotIndex + 2)) {
    308                 continue;
    309             }
    310             DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    311             if (displayItem != null) {
    312                 displayItem.clearScreennailImage();
    313             }
    314         }
    315         if (selectedSlotIndex != Shared.INVALID) {
    316             float camX = camera.mLookAtX * camera.mScale;
    317             int centerIndexInDrawnArray = (selectedSlotIndex - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT;
    318             if (centerIndexInDrawnArray < 0 || centerIndexInDrawnArray >= displayItems.length) {
    319                 return;
    320             }
    321             DisplayItem centerDisplayItem = displayItems[centerIndexInDrawnArray];
    322             if (centerDisplayItem == null || centerDisplayItem.mItemRef.mId == Shared.INVALID) {
    323                 return;
    324             }
    325             boolean focusItemTextureLoaded = false;
    326             Texture centerTexture = centerDisplayItem.getScreennailImage(view.getContext());
    327             if (centerTexture != null && centerTexture.isLoaded()) {
    328                 focusItemTextureLoaded = true;
    329             }
    330             float centerTranslateX = centerDisplayItem.mAnimatedPosition.x;
    331             final boolean skipPrevious = centerTranslateX < camX;
    332             view.setAlpha(1.0f);
    333             gl.glEnable(GL11.GL_BLEND);
    334             gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
    335             float backupImageTheta = 0.0f;
    336             for (int i = -1; i <= 1; ++i) {
    337                 if (slideshowMode && timeElapsedSinceView > 1.0f && i != 0)
    338                     continue;
    339                 float viewAspect = camera.mAspectRatio;
    340                 int selectedSlotToUse = selectedSlotIndex + i;
    341                 if (selectedSlotToUse >= 0 && selectedSlotToUse <= lastBufferedVisibleSlot) {
    342                     int indexInDrawnArray = (selectedSlotToUse - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT;
    343                     if (indexInDrawnArray < 0 || indexInDrawnArray >= displayItems.length) {
    344                         return;
    345                     }
    346                     DisplayItem displayItem = displayItems[indexInDrawnArray];
    347                     MediaItem item = displayItem.mItemRef;
    348                     final Texture thumbnailTexture = displayItem.getThumbnailImage(view.getContext(), sThumbnailConfig);
    349                     Texture texture = displayItem.getScreennailImage(view.getContext());
    350                     if (isCameraZAnimating && (texture == null || !texture.isLoaded())) {
    351                         texture = thumbnailTexture;
    352                         mSelectedMixRatio.setValue(0f);
    353                         mSelectedMixRatio.animateValue(1f, 0.75f, view.getFrameTime());
    354                     }
    355                     Texture hiRes = (zoomValue != 1.0f && i == 0 && item.getMediaType() != MediaItem.MEDIA_TYPE_VIDEO) ? displayItem
    356                             .getHiResImage(view.getContext())
    357                             : null;
    358                     if (App.PIXEL_DENSITY > 1.0f) {
    359                         hiRes = texture;
    360                     }
    361                     if (i != 0) {
    362                         displayItem.clearHiResImage();
    363                     }
    364                     if (hiRes != null) {
    365                         if (!hiRes.isLoaded()) {
    366                             view.bind(hiRes);
    367                             view.prime(hiRes, true);
    368                         } else {
    369                             texture = hiRes;
    370                         }
    371                     }
    372                     final Texture fsTexture = texture;
    373                     if (texture == null || !texture.isLoaded()) {
    374                         if (Math.abs(centerTranslateX - camX) < 0.1f) {
    375                             if (focusItemTextureLoaded && i != 0) {
    376                                 view.bind(texture);
    377                             }
    378                             if (i == 0) {
    379                                 view.bind(texture);
    380                                 view.prime(texture, true);
    381                             }
    382                         }
    383                         texture = thumbnailTexture;
    384                         if (i == 0) {
    385                             mSelectedMixRatio.setValue(0f);
    386                             mSelectedMixRatio.animateValue(1f, 0.75f, view.getFrameTime());
    387                         }
    388                     }
    389                     if (mCamera.isAnimating() || slideshowMode) {
    390                         if (!slideshowMode && skipPrevious && i == -1) {
    391                             continue;
    392                         }
    393                         if (!skipPrevious && i == 1) {
    394                             continue;
    395                         }
    396                     }
    397                     int theta = (int) displayItem.getImageTheta();
    398                     // If it is in slideshow mode, we draw the previous item in
    399                     // the next item's position.
    400                     if (slideshowMode && timeElapsedSinceView < 1.0f && timeElapsedSinceView != 0) {
    401                         if (i == -1) {
    402                             int nextSlotToUse = selectedSlotToUse + 1;
    403                             if (nextSlotToUse >= 0 && nextSlotToUse <= lastBufferedVisibleSlot) {
    404                                 int nextIndexInDrawnArray = (nextSlotToUse - firstBufferedVisibleSlot)
    405                                         * GridLayer.MAX_ITEMS_PER_SLOT;
    406                                 if (nextIndexInDrawnArray >= 0 && nextIndexInDrawnArray < displayItems.length) {
    407                                     float currentImageTheta = displayItem.mAnimatedImageTheta;
    408                                     displayItem = displayItems[nextIndexInDrawnArray];
    409                                     backupImageTheta = displayItem.mAnimatedImageTheta;
    410                                     displayItem.mAnimatedImageTheta = currentImageTheta;
    411                                     view.setAlpha(1.0f - timeElapsedSinceView);
    412                                 }
    413                             }
    414                         } else if (i == 0) {
    415                             displayItem.mAnimatedImageTheta = backupImageTheta;
    416                             view.setAlpha(timeElapsedSinceView);
    417                         }
    418                     }
    419                     if (texture != null) {
    420                         int vboIndex = i + 1;
    421                         float alpha = view.getAlpha();
    422                         float selectedMixRatio = mSelectedMixRatio.getValue(view.getFrameTime());
    423                         if (selectedMixRatio != 1f) {
    424                             texture = thumbnailTexture;
    425                             view.setAlpha(alpha * (1.0f - selectedMixRatio));
    426                         }
    427                         GridQuad quad = GridDrawables.sFullscreenGrid[vboIndex];
    428                         float u = texture.getNormalizedWidth();
    429                         float v = texture.getNormalizedHeight();
    430                         float imageWidth = texture.getWidth();
    431                         float imageHeight = texture.getHeight();
    432                         boolean portrait = ((theta / 90) % 2 == 1);
    433                         if (portrait) {
    434                             viewAspect = 1.0f / viewAspect;
    435                         }
    436                         quad.resizeQuad(viewAspect, u, v, imageWidth, imageHeight);
    437                         quad.bindArrays(gl);
    438                         drawDisplayItem(view, gl, displayItem, texture, PASS_FOCUS_CONTENT, null, 0.0f);
    439                         quad.unbindArrays(gl);
    440                         if (selectedMixRatio != 0.0f && selectedMixRatio != 1.0f) {
    441                             texture = fsTexture;
    442                             if (texture != null) {
    443                                 float drawAlpha = selectedMixRatio;
    444                                 view.setAlpha(alpha * drawAlpha);
    445                                 u = texture.getNormalizedWidth();
    446                                 v = texture.getNormalizedHeight();
    447                                 imageWidth = texture.getWidth();
    448                                 imageHeight = texture.getHeight();
    449                                 quad.resizeQuad(viewAspect, u, v, imageWidth, imageHeight);
    450                                 quad.bindArrays(gl);
    451                                 drawDisplayItem(view, gl, displayItem, fsTexture, PASS_FOCUS_CONTENT, null, 1.0f);
    452                                 quad.unbindArrays(gl);
    453                             }
    454                         }
    455                         if (i == 0 || slideshowMode) {
    456                             mCurrentFocusItemWidth = quad.getWidth();
    457                             mCurrentFocusItemHeight = quad.getHeight();
    458                             if (portrait) {
    459                                 // Swap these values.
    460                                 float itemWidth = mCurrentFocusItemWidth;
    461                                 mCurrentFocusItemWidth = mCurrentFocusItemHeight;
    462                                 mCurrentFocusItemHeight = itemWidth;
    463                             }
    464                         }
    465                         view.setAlpha(alpha);
    466                         if (item.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO) {
    467                             // The play graphic overlay.
    468                             GridDrawables.sVideoGrid.bindArrays(gl);
    469                             drawDisplayItem(view, gl, displayItem, drawables.mTextureVideo, PASS_VIDEO_LABEL, null, 0);
    470                             GridDrawables.sVideoGrid.unbindArrays(gl);
    471                         }
    472                     }
    473                 }
    474             }
    475         }
    476     }
    477 
    478     public void drawBlendedComponents(RenderView view, GL11 gl, float alpha, int state, int hudMode, float stackMixRatio,
    479             float gridMixRatio, MediaBucketList selectedBucketList, MediaBucketList markedBucketList, boolean isFeedLoading) {
    480         int firstBufferedVisibleSlot = mBufferedVisibleRange.begin;
    481         int lastBufferedVisibleSlot = mBufferedVisibleRange.end;
    482         int firstVisibleSlot = mVisibleRange.begin;
    483         int lastVisibleSlot = mVisibleRange.end;
    484         DisplayItem[] displayItems = mDisplayItems;
    485         GridDrawables drawables = mDrawables;
    486 
    487         // We draw the frames around the drawn items.
    488         boolean currentFocusIsPressed = mCurrentFocusIsPressed;
    489         if (state != GridLayer.STATE_FULL_SCREEN) {
    490             GridDrawables.sFrame.bindArrays(gl);
    491             Texture texturePlaceHolder = (state == GridLayer.STATE_GRID_VIEW) ? drawables.mTextureGridFrame
    492                     : drawables.mTextureFrame;
    493             for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    494                 if (i < firstVisibleSlot || i > lastVisibleSlot) {
    495                     continue;
    496                 }
    497                 boolean slotIsAlive = false;
    498                 final int maxDisplayedItemsPerSlot = (i == mCurrentScaleSlot) ? GridLayer.MAX_DISPLAYED_ITEMS_PER_FOCUSED_SLOT
    499                         : GridLayer.MAX_DISPLAYED_ITEMS_PER_SLOT;
    500                 if (state != GridLayer.STATE_MEDIA_SETS && state != GridLayer.STATE_TIMELINE) {
    501                     for (int j = 0; j < maxDisplayedItemsPerSlot; ++j) {
    502                         DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT + j];
    503                         if (displayItem != null) {
    504                             slotIsAlive |= displayItem.mAlive;
    505                         }
    506                     }
    507                     if (!slotIsAlive) {
    508                         DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    509                         if (displayItem != null) {
    510                             drawDisplayItem(view, gl, displayItem, texturePlaceHolder, PASS_FRAME_PLACEHOLDER, null, 0);
    511                         }
    512                     }
    513                 }
    514             }
    515             Texture texturePressed = drawables.mTextureFramePressed;
    516             Texture textureFocus = drawables.mTextureFrameFocus;
    517             Texture textureGrid = drawables.mTextureGridFrame;
    518             Texture texture = drawables.mTextureFrame;
    519 
    520             int drawnCounter = mDrawnCounter;
    521             DisplayItem[] itemsDrawn = mItemsDrawn;
    522             if (texture != null) {
    523                 if (drawnCounter > 0) {
    524                     Arrays.sort(itemsDrawn, 0, drawnCounter, sDisplayItemComparator);
    525                     float timeElapsedSinceGridView = gridMixRatio;
    526                     float timeElapsedSinceStackView = stackMixRatio;
    527                     for (int i = drawnCounter - 1; i >= 0; --i) {
    528                         DisplayItem itemDrawn = itemsDrawn[i];
    529                         if (itemDrawn == null) {
    530                             continue;
    531                         }
    532                         boolean displayItemPresentInSelectedItems = selectedBucketList.find(itemDrawn.mItemRef);
    533                         boolean displayItemPresentInMarkedItems = markedBucketList.find(itemDrawn.mItemRef);
    534                         Texture previousTexture = (displayItemPresentInSelectedItems) ? texturePressed : texture;
    535                         Texture textureToUse = (itemDrawn.getHasFocus()) ? (currentFocusIsPressed ? texturePressed : textureFocus)
    536                                 : ((displayItemPresentInSelectedItems) ? texturePressed
    537                                         : (displayItemPresentInMarkedItems) ? texture : textureGrid);
    538                         float ratio = timeElapsedSinceGridView;
    539                         if (itemDrawn.mAlive) {
    540                             if (state != GridLayer.STATE_GRID_VIEW) {
    541                                 previousTexture = (displayItemPresentInSelectedItems) ? texturePressed : texture;
    542                                 textureToUse = (itemDrawn.getHasFocus()) ? (currentFocusIsPressed ? texturePressed : textureFocus)
    543                                         : previousTexture;
    544                                 if (timeElapsedSinceStackView == 1.0f) {
    545                                     ratio = mFocusMixRatio;
    546                                 } else {
    547                                     ratio = timeElapsedSinceStackView;
    548                                     previousTexture = textureGrid;
    549                                 }
    550                             }
    551                             drawDisplayItem(view, gl, itemDrawn, textureToUse, PASS_FRAME, previousTexture, ratio);
    552                         }
    553                     }
    554                 }
    555             }
    556             GridDrawables.sFrame.unbindArrays(gl);
    557             if (mSpreadValue <= 1.0f)
    558                 gl.glDepthFunc(GL10.GL_ALWAYS);
    559             if (state == GridLayer.STATE_MEDIA_SETS || state == GridLayer.STATE_TIMELINE) {
    560                 DisplaySlot[] displaySlots = mDisplaySlots;
    561                 GridDrawables.sTextGrid.bindArrays(gl);
    562                 final float textOffsetY = 0.82f;
    563                 gl.glTranslatef(0.0f, -textOffsetY, 0.0f);
    564                 HashMap<String, StringTexture> stringTextureTable = GridDrawables.sStringTextureTable;
    565                 ReverseGeocoder reverseGeocoder = App.get(view.getContext()).getReverseGeocoder();
    566 
    567                 boolean itemsPresent = false;
    568 
    569                 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    570                     itemsPresent = true;
    571                     if (mSpreadValue > 1.0f)
    572                         continue;
    573                     DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    574                     if (displayItem != null) {
    575                         DisplaySlot displaySlot = displaySlots[i - firstBufferedVisibleSlot];
    576                         Texture textureString = displaySlot.getTitleImage(stringTextureTable);
    577                         view.loadTexture(textureString);
    578                         if (textureString != null) {
    579                             if (i < firstVisibleSlot || i > lastVisibleSlot) {
    580                                 continue;
    581                             }
    582                             drawDisplayItem(view, gl, displayItem, textureString, PASS_TEXT_LABEL, null, 0);
    583                         }
    584                     }
    585                 }
    586 
    587                 if (!itemsPresent && !isFeedLoading) {
    588                     // Draw the no items texture.
    589                     int wWidth = view.getWidth();
    590                     int wHeight = view.getHeight();
    591 
    592                     // Size this to be 40 pxls less than screen width
    593                     mNoItemsTexture.mWidth = wWidth - 40;
    594 
    595                     int x = (int) Math.floor((wWidth / 2) - (mNoItemsTexture.getWidth() / 2));
    596                     int y = (int) Math.floor((wHeight / 2) - (mNoItemsTexture.getHeight() / 2));
    597                     view.draw2D(mNoItemsTexture, x, y);
    598                 }
    599 
    600                 float yLocOffset = 0.2f;
    601                 gl.glTranslatef(0.0f, -yLocOffset, 0.0f);
    602                 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    603                     if (mSpreadValue > 1.0f)
    604                         continue;
    605                     DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    606                     if (displayItem != null) {
    607                         DisplaySlot displaySlot = displaySlots[i - firstBufferedVisibleSlot];
    608                         StringTexture textureString = displaySlot.getLocationImage(reverseGeocoder, stringTextureTable);
    609                         if (textureString != null) {
    610                             view.loadTexture(textureString);
    611                             drawDisplayItem(view, gl, displayItem, textureString, PASS_TEXT_LABEL, null, 0);
    612                         }
    613                     }
    614                 }
    615                 if (state == GridLayer.STATE_TIMELINE) {
    616                     GridDrawables.sLocationGrid.bindArrays(gl);
    617                     Texture locationTexture = drawables.mTextureLocation;
    618                     final float yLocationLabelOffset = 0.19f;
    619                     for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    620                         if (mCurrentScaleSlot != Shared.INVALID)
    621                             continue;
    622                         DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    623                         if (displayItem != null) {
    624                             if (displayItem.mAlive == true) {
    625                                 DisplaySlot displaySlot = displaySlots[i - firstBufferedVisibleSlot];
    626                                 if (displaySlot.hasValidLocation()) {
    627                                     StringTexture textureString = displaySlot.getLocationImage(reverseGeocoder, stringTextureTable);
    628                                     float textWidth = (textureString != null) ? textureString.computeTextWidth() : 0;
    629                                     textWidth *= (mCamera.mOneByScale * 0.5f);
    630                                     if (textWidth == 0.0f) {
    631                                         textWidth -= 0.18f;
    632                                     }
    633                                     textWidth += 0.1f;
    634                                     gl.glTranslatef(textWidth, -yLocationLabelOffset, 0.0f);
    635                                     drawDisplayItem(view, gl, displayItem, locationTexture, PASS_LOCATION_LABEL, null, 0);
    636                                     gl.glTranslatef(-textWidth, yLocationLabelOffset, 0.0f);
    637                                 }
    638                             }
    639                         }
    640                     }
    641 
    642                     GridDrawables.sLocationGrid.unbindArrays(gl);
    643                 } else if (state == GridLayer.STATE_MEDIA_SETS && stackMixRatio > 0.0f) {
    644                     GridDrawables.sSourceIconGrid.bindArrays(gl);
    645                     Texture transparentTexture = drawables.mTextureTransparent;
    646                     for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    647                         DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    648                         if (mCurrentScaleSlot != Shared.INVALID)
    649                             continue;
    650                         if (displayItem != null) {
    651                             if (displayItem.mAlive == true) {
    652                                 DisplaySlot displaySlot = displaySlots[i - firstBufferedVisibleSlot];
    653                                 Texture locationTexture = view.getResource(drawables
    654                                         .getIconForSet(displaySlot.getMediaSet(), false), false);
    655 
    656                                 // Draw the icon at 0.85 alpha over the top item
    657                                 // in the stack.
    658                                 gl.glTranslatef(0.24f, 0.5f, 0);
    659                                 drawDisplayItem(view, gl, displayItem, locationTexture, PASS_MEDIASET_SOURCE_LABEL,
    660                                         transparentTexture, 0.85f);
    661                                 gl.glTranslatef(-0.24f, -0.5f, 0);
    662                             }
    663                         }
    664                     }
    665                     GridDrawables.sSourceIconGrid.unbindArrays(gl);
    666                 }
    667                 gl.glTranslatef(0.0f, yLocOffset, 0.0f);
    668                 gl.glTranslatef(0.0f, textOffsetY, 0.0f);
    669                 GridDrawables.sTextGrid.unbindArrays(gl);
    670             }
    671             if (hudMode == HudLayer.MODE_SELECT && state != GridLayer.STATE_FULL_SCREEN) {
    672                 Texture textureSelectedOn = drawables.mTextureCheckmarkOn;
    673                 Texture textureSelectedOff = drawables.mTextureCheckmarkOff;
    674                 view.prime(textureSelectedOn, true);
    675                 view.prime(textureSelectedOff, true);
    676                 GridDrawables.sSelectedGrid.bindArrays(gl);
    677                 for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    678                     DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    679                     if (displayItem != null) {
    680                         Texture textureToUse = selectedBucketList.find(displayItem.mItemRef) ? textureSelectedOn
    681                                 : textureSelectedOff;
    682                         drawDisplayItem(view, gl, displayItem, textureToUse, PASS_SELECTION_LABEL, null, 0);
    683                     }
    684                 }
    685                 GridDrawables.sSelectedGrid.unbindArrays(gl);
    686             }
    687             GridDrawables.sVideoGrid.bindArrays(gl);
    688             Texture videoTexture = drawables.mTextureVideo;
    689             for (int i = firstBufferedVisibleSlot; i <= lastBufferedVisibleSlot; ++i) {
    690                 DisplayItem displayItem = displayItems[(i - firstBufferedVisibleSlot) * GridLayer.MAX_ITEMS_PER_SLOT];
    691                 if (displayItem != null && displayItem.mAlive) {
    692                     if (displayItem.mItemRef.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO) {
    693                         drawDisplayItem(view, gl, displayItem, videoTexture, PASS_VIDEO_LABEL, null, 0);
    694                     }
    695                 }
    696             }
    697             GridDrawables.sVideoGrid.unbindArrays(gl);
    698             gl.glDepthFunc(GL10.GL_LEQUAL);
    699         }
    700     }
    701 
    702     private void drawDisplayItem(RenderView view, GL11 gl, DisplayItem displayItem, Texture texture, int pass,
    703             Texture previousTexture, float mixRatio) {
    704         GridCamera camera = mCamera;
    705         Vector3f animatedPosition = displayItem.mAnimatedPosition;
    706         float translateXf = animatedPosition.x * camera.mOneByScale;
    707         float translateYf = animatedPosition.y * camera.mOneByScale;
    708         float translateZf = -animatedPosition.z;
    709         int stackId = displayItem.getStackIndex();
    710         final int maxDisplayedItemsPerSlot = (displayItem.mCurrentSlotIndex == mCurrentScaleSlot && mCurrentScaleSlot != Shared.INVALID) ? GridLayer.MAX_DISPLAYED_ITEMS_PER_FOCUSED_SLOT
    711                 : GridLayer.MAX_DISPLAYED_ITEMS_PER_SLOT;
    712         if (pass == PASS_PLACEHOLDER || pass == PASS_FRAME_PLACEHOLDER) {
    713             translateZf = -0.04f;
    714         } else {
    715             if (pass == PASS_FRAME)
    716                 translateZf += 0.02f;
    717             if ((pass == PASS_TEXT_LABEL || pass == PASS_LOCATION_LABEL || pass == PASS_SELECTION_LABEL) && !displayItem.isAlive()) {
    718                 translateZf = 0.0f;
    719             }
    720             if (pass == PASS_TEXT_LABEL && translateZf > 0) {
    721                 translateZf = 0.0f;
    722             }
    723         }
    724         boolean usingMixedTextures = false;
    725         boolean bind = false;
    726         if ((pass != PASS_THUMBNAIL_CONTENT)
    727                 || (stackId < maxDisplayedItemsPerSlot && texture.isLoaded() && (previousTexture == null || previousTexture
    728                         .isLoaded()))) {
    729             if (mixRatio == 1.0f || previousTexture == null || texture == previousTexture) {
    730                 bind = view.bind(texture);
    731             } else if (mixRatio != 0.0f) {
    732                 if (!texture.isLoaded() || !previousTexture.isLoaded()) {
    733                     // Submit the previous texture to the load queue
    734                     view.bind(previousTexture);
    735                     bind = view.bind(texture);
    736                 } else {
    737                     usingMixedTextures = true;
    738                     bind = view.bindMixed(previousTexture, texture, mixRatio);
    739                 }
    740             } else {
    741                 bind = view.bind(previousTexture);
    742             }
    743         } else if (stackId >= maxDisplayedItemsPerSlot && pass == PASS_THUMBNAIL_CONTENT) {
    744             mDisplayList.setAlive(displayItem, true);
    745         }
    746         if (!texture.isLoaded() || !bind) {
    747             if (pass == PASS_THUMBNAIL_CONTENT) {
    748                 if (previousTexture != null && previousTexture.isLoaded() && translateZf == 0.0f) {
    749                     translateZf = -0.08f;
    750                     bind |= view.bind(previousTexture);
    751                 }
    752                 if (!bind) {
    753                     return;
    754                 }
    755             } else {
    756                 return;
    757             }
    758         } else {
    759             if (pass == PASS_THUMBNAIL_CONTENT || pass == PASS_FOCUS_CONTENT) {
    760                 if (!displayItem.mAlive) {
    761                     mDisplayList.setAlive(displayItem, true);
    762                 }
    763             }
    764         }
    765         gl.glTranslatef(-translateXf, -translateYf, -translateZf);
    766         float theta = (pass == PASS_FOCUS_CONTENT) ? displayItem.mAnimatedImageTheta + displayItem.mAnimatedTheta
    767                 : displayItem.mAnimatedTheta;
    768         if (theta != 0.0f) {
    769             gl.glRotatef(theta, 0.0f, 0.0f, 1.0f);
    770         }
    771         float orientation = 0.0f;
    772         if (pass == PASS_THUMBNAIL_CONTENT && displayItem.mAnimatedImageTheta != 0.0f) {
    773             orientation = displayItem.mAnimatedImageTheta;
    774         }
    775         if (pass == PASS_FRAME || pass == PASS_FRAME_PLACEHOLDER) {
    776             GridQuadFrame.draw(gl);
    777         } else {
    778             GridQuad.draw(gl, orientation);
    779         }
    780         if (theta != 0.0f) {
    781             gl.glRotatef(-theta, 0.0f, 0.0f, 1.0f);
    782         }
    783         gl.glTranslatef(translateXf, translateYf, translateZf);
    784         if (usingMixedTextures) {
    785             view.unbindMixed();
    786         }
    787     }
    788 }
    789