Home | History | Annotate | Download | only in carousel
      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 package com.android.ex.carousel;
     18 
     19 import android.content.res.Resources;
     20 import android.graphics.Bitmap;
     21 import android.graphics.Rect;
     22 import android.renderscript.*;
     23 import static android.renderscript.Element.*;
     24 import android.renderscript.Program.TextureType;
     25 import android.renderscript.RenderScript.RSMessageHandler;
     26 import android.util.Log;
     27 
     28 /**
     29  * This is a support class for Carousel renderscript.  It handles most of the low-level interactions
     30  * with Renderscript as well as dispatching events.
     31  *
     32  */
     33 public class CarouselRS  {
     34     private static final int DEFAULT_VISIBLE_SLOTS = 1;
     35     private static final int DEFAULT_CARD_COUNT = 0;
     36     private static final int DEFAULT_ROW_COUNT = 1;
     37 
     38     // Client messages *** THIS LIST MUST MATCH THOSE IN carousel.rs ***
     39     public static final int CMD_CARD_SELECTED = 100;
     40     public static final int CMD_DETAIL_SELECTED = 105;
     41     public static final int CMD_CARD_LONGPRESS = 110;
     42     public static final int CMD_REQUEST_TEXTURE = 200;
     43     public static final int CMD_INVALIDATE_TEXTURE = 210;
     44     public static final int CMD_REQUEST_GEOMETRY = 300;
     45     public static final int CMD_INVALIDATE_GEOMETRY = 310;
     46     public static final int CMD_ANIMATION_STARTED = 400;
     47     public static final int CMD_ANIMATION_FINISHED = 500;
     48     public static final int CMD_REQUEST_DETAIL_TEXTURE = 600;
     49     public static final int CMD_INVALIDATE_DETAIL_TEXTURE = 610;
     50     public static final int CMD_PING = 1000; // for debugging
     51 
     52     // Drag models *** THIS LIST MUST MATCH THOSE IN carousel.rs ***
     53     public static final int DRAG_MODEL_SCREEN_DELTA = 0;
     54     public static final int DRAG_MODEL_PLANE = 1;
     55     public static final int DRAG_MODEL_CYLINDER_INSIDE = 2;
     56     public static final int DRAG_MODEL_CYLINDER_OUTSIDE = 3;
     57 
     58     public static final int FILL_DIRECTION_CCW = +1;
     59     public static final int FILL_DIRECTION_CW = -1;
     60 
     61     private static final String TAG = "CarouselRS";
     62     private static final int DEFAULT_SLOT_COUNT = 10;
     63     private static final Allocation.MipmapControl MIPMAP =
     64         Allocation.MipmapControl.MIPMAP_NONE;
     65     private static final boolean DBG = false;
     66 
     67     private RenderScriptGL mRS;
     68     private Resources mRes;
     69     private ScriptC_carousel mScript;
     70     private ScriptField_Card mCards;
     71     private ScriptField_FragmentShaderConstants_s mFSConst;
     72     private ScriptField_ProgramStore_s mProgramStoresCard;
     73     private ProgramFragment mSingleTextureFragmentProgram;
     74     private ProgramFragment mSingleTextureBlendingFragmentProgram;
     75     private ProgramFragment mMultiTextureFragmentProgram;
     76     private ProgramFragment mMultiTextureBlendingFragmentProgram;
     77     private ProgramVertex mVertexProgram;
     78     private ProgramRaster mRasterProgram;
     79     private Allocation[] mAllocationPool;
     80     private boolean mForceBlendCardsWithZ;
     81     private int mVisibleSlots;
     82     private int mRowCount;
     83     private int mPrefetchCardCount;
     84     private CarouselCallback mCallback;
     85     private float[] mEyePoint = new float[] { 2.0f, 0.0f, 0.0f };
     86     private float[] mAtPoint = new float[] { 0.0f, 0.0f, 0.0f };
     87     private float[] mUp = new float[] { 0.0f, 1.0f, 0.0f };
     88 
     89     private static final String mSingleTextureShader = new String(
     90             "varying vec2 varTex0;" +
     91             "void main() {" +
     92             "vec2 t0 = varTex0.xy;" +
     93             "vec4 col = texture2D(UNI_Tex0, t0);" +
     94             "gl_FragColor = col; " +
     95             "}");
     96 
     97     private static final String mSingleTextureBlendingShader = new String(
     98             "varying vec2 varTex0;" +
     99             "void main() {" +
    100             "vec2 t0 = varTex0.xy;" +
    101             "vec4 col = texture2D(UNI_Tex0, t0);" +
    102             "gl_FragColor = col * UNI_overallAlpha; " +
    103             "}");
    104 
    105     private static final String mMultiTextureShader = new String(
    106             "varying vec2 varTex0;" +
    107             "void main() {" +
    108             "vec2 t0 = varTex0.xy;" +
    109             "vec4 col = texture2D(UNI_Tex0, t0);" +
    110             "vec4 col2 = texture2D(UNI_Tex1, t0);" +
    111             "gl_FragColor = mix(col, col2, UNI_fadeAmount);}");
    112 
    113     private static final String mMultiTextureBlendingShader = new String(
    114             "varying vec2 varTex0;" +
    115             "void main() {" +
    116             "vec2 t0 = varTex0.xy;" +
    117             "vec4 col = texture2D(UNI_Tex0, t0);" +
    118             "vec4 col2 = texture2D(UNI_Tex1, t0);" +
    119             "gl_FragColor = mix(col, col2, UNI_fadeAmount) * UNI_overallAlpha;" +
    120             "}"
    121     );
    122 
    123     public static interface CarouselCallback {
    124         /**
    125          * Called when a card is selected
    126          * @param n the id of the card
    127          */
    128         void onCardSelected(int n);
    129 
    130         /**
    131          * Called when the detail texture for a card is tapped
    132          * @param n the id of the card
    133          * @param x how far the user tapped from the left edge of the card, in pixels
    134          * @param y how far the user tapped from the top edge of the card, in pixels
    135          */
    136         void onDetailSelected(int n, int x, int y);
    137 
    138         /**
    139          * Called when a card is long-pressed
    140          * @param n the id of the card
    141          * @param touchPosition position of where the user pressed, in screen coordinates
    142          * @param detailCoordinates position of detail texture, in screen coordinates
    143          */
    144         void onCardLongPress(int n, int touchPosition[], Rect detailCoordinates);
    145 
    146         /**
    147          * Called when texture is needed for card n.  This happens when the given card becomes
    148          * visible.
    149          * @param n the id of the card
    150          */
    151         void onRequestTexture(int n);
    152 
    153         /**
    154          * Called when a texture is no longer needed for card n.  This happens when the card
    155          * goes out of view.
    156          * @param n the id of the card
    157          */
    158         void onInvalidateTexture(int n);
    159 
    160         /**
    161          * Called when detail texture is needed for card n.  This happens when the given card
    162          * becomes visible.
    163          * @param n the id of the card
    164          */
    165         void onRequestDetailTexture(int n);
    166 
    167         /**
    168          * Called when a detail texture is no longer needed for card n.  This happens when the card
    169          * goes out of view.
    170          * @param n the id of the card
    171          */
    172         void onInvalidateDetailTexture(int n);
    173 
    174         /**
    175          * Called when geometry is needed for card n.
    176          * @param n the id of the card.
    177          */
    178         void onRequestGeometry(int n);
    179 
    180         /**
    181          * Called when geometry is no longer needed for card n. This happens when the card goes
    182          * out of view.
    183          * @param n the id of the card
    184          */
    185         void onInvalidateGeometry(int n);
    186 
    187         /**
    188          * Called when card animation (e.g. a fling) has started.
    189          */
    190         void onAnimationStarted();
    191 
    192         /**
    193          * Called when card animation has stopped.
    194          * @param carouselRotationAngle the angle of rotation, in radians, at which the animation
    195          * stopped.
    196          */
    197         void onAnimationFinished(float carouselRotationAngle);
    198     };
    199 
    200     private RSMessageHandler mRsMessage = new RSMessageHandler() {
    201         public void run() {
    202             if (mCallback == null) return;
    203             switch (mID) {
    204                 case CMD_CARD_SELECTED:
    205                     mCallback.onCardSelected(mData[0]);
    206                     break;
    207 
    208                 case CMD_DETAIL_SELECTED:
    209                     mCallback.onDetailSelected(mData[0], mData[1], mData[2]);
    210                     break;
    211 
    212                 case CMD_CARD_LONGPRESS:
    213                     int touchPosition[] = { mData[1], mData[2] };
    214                     Rect detailCoordinates = new Rect(mData[3], mData[4], mData[5], mData[6]);
    215                     mCallback.onCardLongPress(mData[0], touchPosition, detailCoordinates);
    216                     break;
    217 
    218                 case CMD_REQUEST_TEXTURE:
    219                     mCallback.onRequestTexture(mData[0]);
    220                     break;
    221 
    222                 case CMD_INVALIDATE_TEXTURE:
    223                     setTexture(mData[0], null);
    224                     mCallback.onInvalidateTexture(mData[0]);
    225                     break;
    226 
    227                 case CMD_REQUEST_DETAIL_TEXTURE:
    228                     mCallback.onRequestDetailTexture(mData[0]);
    229                     break;
    230 
    231                 case CMD_INVALIDATE_DETAIL_TEXTURE:
    232                     setDetailTexture(mData[0], 0.0f, 0.0f, 0.0f, 0.0f, null);
    233                     mCallback.onInvalidateDetailTexture(mData[0]);
    234                     break;
    235 
    236                 case CMD_REQUEST_GEOMETRY:
    237                     mCallback.onRequestGeometry(mData[0]);
    238                     break;
    239 
    240                 case CMD_INVALIDATE_GEOMETRY:
    241                     setGeometry(mData[0], null);
    242                     mCallback.onInvalidateGeometry(mData[0]);
    243                     break;
    244 
    245                 case CMD_ANIMATION_STARTED:
    246                     mCallback.onAnimationStarted();
    247                     break;
    248 
    249                 case CMD_ANIMATION_FINISHED:
    250                     mCallback.onAnimationFinished(Float.intBitsToFloat(mData[0]));
    251                     break;
    252 
    253                 case CMD_PING:
    254                     if (DBG) Log.v(TAG, "PING...");
    255                     break;
    256 
    257                 default:
    258                     Log.e(TAG, "Unknown RSMessage: " + mID);
    259             }
    260         }
    261     };
    262 
    263     public CarouselRS(RenderScriptGL rs, Resources res, int resId) {
    264         mRS = rs;
    265         mRes = res;
    266 
    267         // create the script object
    268         mScript = new ScriptC_carousel(mRS, mRes, resId);
    269         mRS.setMessageHandler(mRsMessage);
    270         initProgramStore();
    271         initFragmentProgram();
    272         initRasterProgram();
    273         initVertexProgram();
    274         setSlotCount(DEFAULT_SLOT_COUNT);
    275         setVisibleSlots(DEFAULT_VISIBLE_SLOTS);
    276         setRowCount(DEFAULT_ROW_COUNT);
    277         createCards(DEFAULT_CARD_COUNT);
    278         setStartAngle(0.0f);
    279         setCarouselRotationAngle(0.0f);
    280         setRadius(1.0f);
    281         setLookAt(mEyePoint, mAtPoint, mUp);
    282         setRadius(20.0f);
    283         // Fov: 25
    284     }
    285 
    286     public void setLookAt(float[] eye, float[] at, float[] up) {
    287         for (int i = 0; i < 3; i++) {
    288             mEyePoint[i] = eye[i];
    289             mAtPoint[i] = at[i];
    290             mUp[i] = up[i];
    291         }
    292         mScript.invoke_lookAt(eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0], up[1], up[2]);
    293     }
    294 
    295     public void setRadius(float radius) {
    296         mScript.invoke_setRadius(radius);
    297     }
    298 
    299     public void setCardRotation(float cardRotation) {
    300         mScript.set_cardRotation(cardRotation);
    301     }
    302 
    303     public void setCardsFaceTangent(boolean faceTangent) {
    304         mScript.set_cardsFaceTangent(faceTangent);
    305     }
    306 
    307     public void setSwaySensitivity(float swaySensitivity) {
    308         mScript.set_swaySensitivity(swaySensitivity);
    309     }
    310 
    311     public void setFrictionCoefficient(float frictionCoeff) {
    312         mScript.set_frictionCoeff(frictionCoeff);
    313     }
    314 
    315     public void setDragFactor(float dragFactor) {
    316         mScript.set_dragFactor(dragFactor);
    317     }
    318 
    319     public void setDragModel(int model) {
    320         mScript.set_dragModel(model);
    321     }
    322 
    323     public void setFillDirection(int direction) {
    324         mScript.set_fillDirection(direction);
    325     }
    326 
    327     private Matrix4f matrixFromFloat(float[] matrix) {
    328         int dimensions;
    329         if (matrix == null || matrix.length == 0) {
    330           dimensions = 0;
    331         } else if (matrix.length == 16) {
    332           dimensions = 4;
    333         } else if (matrix.length == 9) {
    334           dimensions = 3;
    335         } else {
    336           throw new IllegalArgumentException("matrix length not 0,9 or 16");
    337         }
    338 
    339         Matrix4f rsMatrix = new Matrix4f();  // initialized as identity.
    340         for (int i = 0; i < dimensions; i++) {
    341             for (int j = 0; j < dimensions; j++) {
    342                 rsMatrix.set(i, j, matrix[i*dimensions + j]);
    343             }
    344         }
    345 
    346         return rsMatrix;
    347     }
    348 
    349     public void setDefaultCardMatrix(float[] matrix) {
    350         mScript.set_defaultCardMatrix(matrixFromFloat(matrix));
    351     }
    352 
    353     private void initVertexProgram() {
    354         ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
    355         mVertexProgram = pvb.create();
    356         ProgramVertexFixedFunction.Constants pva = new ProgramVertexFixedFunction.Constants(mRS);
    357         ((ProgramVertexFixedFunction)mVertexProgram).bindConstants(pva);
    358         Matrix4f proj = new Matrix4f();
    359         proj.loadProjectionNormalized(1, 1);
    360         pva.setProjection(proj);
    361         mScript.set_vertexProgram(mVertexProgram);
    362     }
    363 
    364     private void initRasterProgram() {
    365         ProgramRaster.Builder programRasterBuilder = new ProgramRaster.Builder(mRS);
    366         mRasterProgram = programRasterBuilder.create();
    367         //mRasterProgram.setCullMode(CullMode.NONE);
    368         mScript.set_rasterProgram(mRasterProgram);
    369     }
    370 
    371     private void initFragmentProgram() {
    372         //
    373         // Single texture program
    374         //
    375         ProgramFragment.Builder pfbSingle = new ProgramFragment.Builder(mRS);
    376         // Specify the resource that contains the shader string
    377         pfbSingle.setShader(mSingleTextureShader);
    378         // Tell the builder how many textures we have
    379         pfbSingle.addTexture(Program.TextureType.TEXTURE_2D);
    380         mSingleTextureFragmentProgram = pfbSingle.create();
    381         // Bind the source of constant data
    382         mSingleTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
    383 
    384         //
    385         // Single texture program, plus blending
    386         //
    387         mFSConst = new ScriptField_FragmentShaderConstants_s(mRS, 1);
    388         mScript.bind_shaderConstants(mFSConst);
    389         ProgramFragment.Builder pfbSingleBlend = new ProgramFragment.Builder(mRS);
    390         // Specify the resource that contains the shader string
    391         pfbSingleBlend.setShader(mSingleTextureBlendingShader);
    392         // Tell the builder how many textures we have
    393         pfbSingleBlend.addTexture(Program.TextureType.TEXTURE_2D);
    394         // Define the constant input layout
    395         pfbSingleBlend.addConstant(mFSConst.getAllocation().getType());
    396         mSingleTextureBlendingFragmentProgram = pfbSingleBlend.create();
    397         // Bind the source of constant data
    398         mSingleTextureBlendingFragmentProgram.bindConstants(mFSConst.getAllocation(), 0);
    399         mSingleTextureBlendingFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
    400 
    401         //
    402         // Multi texture program
    403         //
    404         ProgramFragment.Builder pfbMulti = new ProgramFragment.Builder(mRS);
    405         // Specify the resource that contains the shader string
    406         pfbMulti.setShader(mMultiTextureShader);
    407         // Tell the builder how many textures we have
    408         pfbMulti.addTexture(Program.TextureType.TEXTURE_2D);
    409         pfbMulti.addTexture(Program.TextureType.TEXTURE_2D);
    410         // Define the constant input layout
    411         pfbMulti.addConstant(mFSConst.getAllocation().getType());
    412         mMultiTextureFragmentProgram = pfbMulti.create();
    413         // Bind the source of constant data
    414         mMultiTextureFragmentProgram.bindConstants(mFSConst.getAllocation(), 0);
    415         mMultiTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
    416         mMultiTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 1);
    417 
    418         //
    419         // Multi texture program, plus blending
    420         //
    421         ProgramFragment.Builder pfbMultiBlend = new ProgramFragment.Builder(mRS);
    422         // Specify the resource that contains the shader string
    423         pfbMultiBlend.setShader(mMultiTextureBlendingShader);
    424         // Tell the builder how many textures we have
    425         pfbMultiBlend.addTexture(Program.TextureType.TEXTURE_2D);
    426         pfbMultiBlend.addTexture(Program.TextureType.TEXTURE_2D);
    427         // Define the constant input layout
    428         pfbMultiBlend.addConstant(mFSConst.getAllocation().getType());
    429         mMultiTextureBlendingFragmentProgram = pfbMultiBlend.create();
    430         // Bind the source of constant data
    431         mMultiTextureBlendingFragmentProgram.bindConstants(mFSConst.getAllocation(), 0);
    432         mMultiTextureBlendingFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
    433         mMultiTextureBlendingFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 1);
    434 
    435         mScript.set_linearClamp(Sampler.CLAMP_LINEAR(mRS));
    436         mScript.set_singleTextureFragmentProgram(mSingleTextureFragmentProgram);
    437         mScript.set_singleTextureBlendingFragmentProgram(mSingleTextureBlendingFragmentProgram);
    438         mScript.set_multiTextureFragmentProgram(mMultiTextureFragmentProgram);
    439         mScript.set_multiTextureBlendingFragmentProgram(mMultiTextureBlendingFragmentProgram);
    440     }
    441 
    442     private void initProgramStore() {
    443         resizeProgramStoresCard(1);
    444 
    445         final boolean dither = true;
    446         final ProgramStore.DepthFunc depthFunc = mForceBlendCardsWithZ ?
    447                 ProgramStore.DepthFunc.LESS : ProgramStore.DepthFunc.ALWAYS;
    448 
    449         // Background: Alpha disabled, depth optional
    450         mScript.set_programStoreBackground(new ProgramStore.Builder(mRS)
    451             .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ZERO)
    452             .setDitherEnabled(dither)
    453             .setDepthFunc(depthFunc)
    454             .setDepthMaskEnabled(mForceBlendCardsWithZ)
    455             .create());
    456 
    457         // Card: Alpha enabled, depth optional
    458         setProgramStoreCard(0, new ProgramStore.Builder(mRS)
    459             .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
    460                 ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
    461             .setDitherEnabled(dither)
    462             .setDepthFunc(depthFunc)
    463             .setDepthMaskEnabled(mForceBlendCardsWithZ)
    464             .create());
    465 
    466         // Detail: Alpha enabled, depth disabled
    467         mScript.set_programStoreDetail(new ProgramStore.Builder(mRS)
    468             .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
    469                 ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
    470             .setDitherEnabled(dither)
    471             .setDepthFunc(ProgramStore.DepthFunc.ALWAYS)
    472             .setDepthMaskEnabled(false)
    473             .create());
    474     }
    475 
    476     public void createCards(int count)
    477     {
    478         // Because RenderScript can't have allocations with 0 dimensions, we always create
    479         // an allocation of at least one card. This relies on invoke_createCards() to keep
    480         // track of when the allocation is not valid.
    481         if (mCards != null && count > 0) {
    482             // resize the array
    483             int oldSize = mCards.getAllocation().getType().getX();
    484             mCards.resize(count);
    485             mScript.invoke_createCards(oldSize, count);
    486         } else {
    487             // create array from scratch
    488             mCards = new ScriptField_Card(mRS, count > 0 ? count : 1);
    489             mScript.bind_cards(mCards);
    490             mScript.invoke_createCards(0, count);
    491         }
    492     }
    493 
    494     public void setVisibleSlots(int count)
    495     {
    496         mVisibleSlots = count;
    497         mScript.set_visibleSlotCount(count);
    498     }
    499 
    500     public void setVisibleDetails(int count) {
    501         mScript.set_visibleDetailCount(count);
    502     }
    503 
    504     public void setRowCount(int count) {
    505         mRowCount = count;
    506         mScript.set_rowCount(count);
    507     }
    508 
    509     public void setRowSpacing(float spacing) {
    510         mScript.set_rowSpacing(spacing);
    511     }
    512 
    513     public void setOverscrollSlots(float slots) {
    514         mScript.set_overscrollSlots(slots);
    515     }
    516 
    517     public void setFirstCardTop(boolean first) {
    518         mScript.set_firstCardTop(first);
    519     }
    520 
    521     public void setPrefetchCardCount(int count) {
    522         mPrefetchCardCount = count;
    523         mScript.set_prefetchCardCount(count);
    524     }
    525 
    526     public void setDetailTextureAlignment(int alignment) {
    527         mScript.set_detailTextureAlignment(alignment);
    528     }
    529 
    530     private void resizeProgramStoresCard(int count) {
    531         // enableResize works around a Renderscript bug that keeps resizes from being propagated.
    532         // TODO(jshuma): Remove enableResize once the Renderscript bug is fixed
    533         final boolean enableResize = false;
    534 
    535         if (mProgramStoresCard != null && enableResize) {
    536             int newSize = count > 0 ? count : 1;
    537             mProgramStoresCard.resize(newSize);
    538         } else {
    539             mProgramStoresCard = new ScriptField_ProgramStore_s(mRS, count > 0 ? count : 1);
    540             mScript.bind_programStoresCard(mProgramStoresCard);
    541         }
    542     }
    543 
    544     private void setProgramStoreCard(int n, ProgramStore programStore) {
    545         ScriptField_ProgramStore_s.Item item = mProgramStoresCard.get(n);
    546         if (item == null) {
    547             item = new ScriptField_ProgramStore_s.Item();
    548         }
    549         item.programStore = programStore;
    550         mProgramStoresCard.set(item, n, false);
    551         mScript.invoke_setProgramStoresCard(n, programStore);
    552     }
    553 
    554     public void setStoreConfigs(int configs[]) {
    555         if (configs == null) {
    556             initProgramStore();
    557             return;
    558         }
    559 
    560         final int count = configs.length;
    561 
    562         resizeProgramStoresCard(count);
    563         for (int i=0; i<count; ++i) {
    564             final int config = configs[i];
    565 
    566             final boolean alpha = (config & CarouselController.STORE_CONFIG_ALPHA) != 0;
    567             final boolean depthReads = (config & CarouselController.STORE_CONFIG_DEPTH_READS) != 0;
    568             final boolean depthWrites =
    569                     (config & CarouselController.STORE_CONFIG_DEPTH_WRITES) != 0;
    570 
    571             final boolean dither = true;
    572             final ProgramStore.BlendDstFunc dstFunc = alpha ?
    573                     ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA :
    574                     ProgramStore.BlendDstFunc.ZERO;
    575             final ProgramStore.DepthFunc depthFunc = depthReads ?
    576                     ProgramStore.DepthFunc.LESS :
    577                     ProgramStore.DepthFunc.ALWAYS;
    578 
    579             final ProgramStore ps = new ProgramStore.Builder(mRS)
    580                     .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, dstFunc)
    581                     .setDitherEnabled(dither)
    582                     .setDepthFunc(depthFunc)
    583                     .setDepthMaskEnabled(depthWrites)
    584                     .create();
    585 
    586             setProgramStoreCard(i, ps);
    587         }
    588     }
    589 
    590     /**
    591      * Sets whether the background texture and default card geometry are to be drawn with respect
    592      * to the depth buffer (both reading from it and writing to it).
    593      *
    594      * This method is a specialization of functionality that can be done with greater flexibility
    595      * by setStoreConfigs. Calling setForceBlendCardsWithZ() after calling setStoreConfigs()
    596      * results in the values set in setStoreConfigs() being discarded.
    597      *
    598      * @param enabled true to read from and write to the depth buffer, false to ignore it
    599      */
    600     public void setForceBlendCardsWithZ(boolean enabled) {
    601         mForceBlendCardsWithZ = enabled;
    602         initProgramStore();
    603     }
    604 
    605     public void setDrawRuler(boolean drawRuler) {
    606         mScript.set_drawRuler(drawRuler);
    607     }
    608 
    609     public void setDefaultBitmap(Bitmap bitmap)
    610     {
    611         mScript.set_defaultTexture(allocationFromBitmap(bitmap, MIPMAP));
    612     }
    613 
    614     public void setLoadingBitmap(Bitmap bitmap)
    615     {
    616         mScript.set_loadingTexture(allocationFromBitmap(bitmap, MIPMAP));
    617     }
    618 
    619     public void setDefaultGeometry(Mesh mesh)
    620     {
    621         mScript.set_defaultGeometry(mesh);
    622     }
    623 
    624     public void setLoadingGeometry(Mesh mesh)
    625     {
    626         mScript.set_loadingGeometry(mesh);
    627     }
    628 
    629     public void setStartAngle(float theta)
    630     {
    631         mScript.set_startAngle(theta);
    632     }
    633 
    634     public void setCarouselRotationAngle(float theta) {
    635         mScript.invoke_setCarouselRotationAngle(theta);
    636     }
    637 
    638     public void setCarouselRotationAngle(float endAngle, int milliseconds, int interpolationMode,
    639             float maxAnimatedArc) {
    640         mScript.invoke_setCarouselRotationAngle2(endAngle, milliseconds, interpolationMode,
    641                 maxAnimatedArc);
    642     }
    643 
    644     public void setCallback(CarouselCallback callback)
    645     {
    646         mCallback = callback;
    647     }
    648 
    649     private Allocation allocationFromBitmap(Bitmap bitmap, Allocation.MipmapControl mipmap)
    650     {
    651         if (bitmap == null) return null;
    652         Allocation allocation = Allocation.createFromBitmap(mRS, bitmap,
    653                 mipmap, Allocation.USAGE_GRAPHICS_TEXTURE);
    654         return allocation;
    655     }
    656 
    657     private Allocation allocationFromPool(int n, Bitmap bitmap, Allocation.MipmapControl mipmap)
    658     {
    659         int count = (mVisibleSlots + 2*mPrefetchCardCount) * mRowCount;
    660         if (mAllocationPool == null || mAllocationPool.length != count) {
    661             Allocation[] tmp = new Allocation[count];
    662             int oldsize = mAllocationPool == null ? 0 : mAllocationPool.length;
    663             for (int i = 0; i < Math.min(count, oldsize); i++) {
    664                 tmp[i] = mAllocationPool[i];
    665             }
    666             mAllocationPool = tmp;
    667         }
    668         Allocation allocation = mAllocationPool[n % count];
    669         if (allocation == null) {
    670             allocation = allocationFromBitmap(bitmap, mipmap);
    671             mAllocationPool[n % count]  = allocation;
    672         } else if (bitmap != null) {
    673             if (bitmap.getWidth() == allocation.getType().getX()
    674                 && bitmap.getHeight() == allocation.getType().getY()) {
    675                 allocation.copyFrom(bitmap);
    676             } else {
    677                 Log.v(TAG, "Warning, bitmap has different size. Taking slow path");
    678                 allocation = allocationFromBitmap(bitmap, mipmap);
    679                 mAllocationPool[n % count]  = allocation;
    680             }
    681         }
    682         return allocation;
    683     }
    684 
    685     private ScriptField_Card.Item getCard(int n) {
    686         ScriptField_Card.Item item;
    687         try {
    688             item = mCards.get(n);
    689         }
    690         catch (ArrayIndexOutOfBoundsException e) {
    691             if (DBG) Log.v(TAG, "getCard(): no item at index " + n);
    692             item = null;
    693         }
    694         return item;
    695     }
    696 
    697     private ScriptField_Card.Item getOrCreateCard(int n) {
    698         ScriptField_Card.Item item = getCard(n);
    699         if (item == null) {
    700             if (DBG) Log.v(TAG, "getOrCreateCard(): no item at index " + n + "; creating new");
    701             item = new ScriptField_Card.Item();
    702         }
    703         return item;
    704     }
    705 
    706     private void setCard(int n, ScriptField_Card.Item item) {
    707         try {
    708             mCards.set(item, n, false); // This is primarily used for reference counting.
    709         }
    710         catch (ArrayIndexOutOfBoundsException e) {
    711             // The specified index didn't exist. This can happen when a stale invalidate
    712             // request outlived an array resize request. Something might be getting dropped,
    713             // but there's not much we can do about this at this point to recover.
    714             Log.w(TAG, "setCard(" + n + "): Texture " + n + " doesn't exist");
    715         }
    716     }
    717 
    718     public void setTexture(int n, Bitmap bitmap)
    719     {
    720         if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
    721 
    722         synchronized(this) {
    723             ScriptField_Card.Item item = getOrCreateCard(n);
    724             if (bitmap != null) {
    725                 item.texture = allocationFromPool(n, bitmap, MIPMAP);
    726             } else {
    727                 if (item.texture != null) {
    728                     if (DBG) Log.v(TAG, "unloading texture " + n);
    729                     item.texture = null;
    730                 }
    731             }
    732             setCard(n, item);
    733             mScript.invoke_setTexture(n, item.texture);
    734         }
    735     }
    736 
    737     void setDetailTexture(int n, float offx, float offy, float loffx, float loffy, Bitmap bitmap)
    738     {
    739         if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
    740 
    741         synchronized(this) {
    742             ScriptField_Card.Item item = getOrCreateCard(n);
    743             float width = 0.0f;
    744             float height = 0.0f;
    745             if (bitmap != null) {
    746                 item.detailTexture = allocationFromBitmap(bitmap, MIPMAP);
    747                 width = bitmap.getWidth();
    748                 height = bitmap.getHeight();
    749             } else {
    750                 if (item.detailTexture != null) {
    751                     if (DBG) Log.v(TAG, "unloading detail texture " + n);
    752                     // Don't wait for GC to free native memory.
    753                     // Only works if textures are not shared.
    754                     item.detailTexture.destroy();
    755                     item.detailTexture = null;
    756                 }
    757             }
    758             setCard(n, item);
    759             mScript.invoke_setDetailTexture(n, offx, offy, loffx, loffy, item.detailTexture);
    760         }
    761     }
    762 
    763     void invalidateTexture(int n, boolean eraseCurrent)
    764     {
    765         if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
    766 
    767         synchronized(this) {
    768             ScriptField_Card.Item item = getCard(n);
    769             if (item == null) {
    770                 // This card was never created, so there's nothing to invalidate.
    771                 return;
    772             }
    773             if (eraseCurrent && item.texture != null) {
    774                 if (DBG) Log.v(TAG, "unloading texture " + n);
    775                 // Don't wait for GC to free native memory.
    776                 // Only works if textures are not shared.
    777                 item.texture.destroy();
    778                 item.texture = null;
    779             }
    780             setCard(n, item);
    781             mScript.invoke_invalidateTexture(n, eraseCurrent);
    782         }
    783     }
    784 
    785     void invalidateDetailTexture(int n, boolean eraseCurrent)
    786     {
    787         if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
    788 
    789         synchronized(this) {
    790             ScriptField_Card.Item item = getCard(n);
    791             if (item == null) {
    792                 // This card was never created, so there's nothing to invalidate.
    793                 return;
    794             }
    795             if (eraseCurrent && item.detailTexture != null) {
    796                 if (DBG) Log.v(TAG, "unloading detail texture " + n);
    797                 // Don't wait for GC to free native memory.
    798                 // Only works if textures are not shared.
    799                 item.detailTexture.destroy();
    800                 item.detailTexture = null;
    801             }
    802             setCard(n, item);
    803             mScript.invoke_invalidateDetailTexture(n, eraseCurrent);
    804         }
    805     }
    806 
    807     public void setGeometry(int n, Mesh geometry)
    808     {
    809         if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
    810 
    811         synchronized(this) {
    812             final boolean mipmap = false;
    813             ScriptField_Card.Item item = getOrCreateCard(n);
    814             if (geometry != null) {
    815                 item.geometry = geometry;
    816             } else {
    817                 if (DBG) Log.v(TAG, "unloading geometry " + n);
    818                 if (item.geometry != null) {
    819                     // item.geometry.destroy();
    820                     item.geometry = null;
    821                 }
    822             }
    823             setCard(n, item);
    824             mScript.invoke_setGeometry(n, item.geometry);
    825         }
    826     }
    827 
    828     public void setMatrix(int n, float[] matrix) {
    829         if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
    830 
    831         synchronized(this) {
    832             final boolean mipmap = false;
    833             ScriptField_Card.Item item = getOrCreateCard(n);
    834             if (matrix != null) {
    835                 item.matrix = matrixFromFloat(matrix);
    836             } else {
    837                 if (DBG) Log.v(TAG, "unloading matrix " + n);
    838                 item.matrix = null;
    839             }
    840             setCard(n, item);
    841             mScript.invoke_setMatrix(n, item.matrix);
    842         }
    843     }
    844 
    845     public void setBackgroundColor(Float4 color) {
    846         mScript.set_backgroundColor(color);
    847     }
    848 
    849     public void setBackgroundTexture(Bitmap bitmap) {
    850         Allocation texture = null;
    851         if (bitmap != null) {
    852             texture = Allocation.createFromBitmap(mRS, bitmap,
    853                     MIPMAP, Allocation.USAGE_GRAPHICS_TEXTURE);
    854         }
    855         mScript.set_backgroundTexture(texture);
    856     }
    857 
    858     public void setDetailLineTexture(Bitmap bitmap) {
    859         Allocation texture = null;
    860         if (bitmap != null) {
    861             texture = Allocation.createFromBitmap(mRS, bitmap,
    862                     MIPMAP, Allocation.USAGE_GRAPHICS_TEXTURE);
    863         }
    864         mScript.set_detailLineTexture(texture);
    865     }
    866 
    867     public void setDetailLoadingTexture(Bitmap bitmap) {
    868         Allocation texture = null;
    869         if (bitmap != null) {
    870             texture = Allocation.createFromBitmap(mRS, bitmap,
    871                     MIPMAP, Allocation.USAGE_GRAPHICS_TEXTURE);
    872         }
    873         mScript.set_detailLoadingTexture(texture);
    874     }
    875 
    876     public void pauseRendering() {
    877         // Used to update multiple states at once w/o redrawing for each.
    878         mRS.bindRootScript(null);
    879     }
    880 
    881     public void resumeRendering() {
    882         mRS.bindRootScript(mScript);
    883     }
    884 
    885     public void doLongPress() {
    886         mScript.invoke_doLongPress();
    887     }
    888 
    889     public void doMotion(float x, float y, long t) {
    890         mScript.invoke_doMotion(x, y, t);
    891     }
    892 
    893     public void doStart(float x, float y, long t) {
    894         mScript.invoke_doStart(x, y, t);
    895     }
    896 
    897     public void doStop(float x, float y, long t) {
    898         mScript.invoke_doStop(x, y, t);
    899     }
    900 
    901     public void setSlotCount(int n) {
    902         mScript.set_slotCount(n);
    903     }
    904 
    905     public void setRezInCardCount(float alpha) {
    906         mScript.set_rezInCardCount(alpha);
    907     }
    908 
    909     public void setFadeInDuration(long t) {
    910         mScript.set_fadeInDuration((int)t); // TODO: Remove cast when RS supports exporting longs
    911     }
    912 
    913     public void setCardCreationFadeDuration(long t) {
    914         mScript.set_cardCreationFadeDuration((int)t);
    915     }
    916 
    917     private Element elementForBitmap(Bitmap bitmap, Bitmap.Config defaultConfig) {
    918         Bitmap.Config config = bitmap.getConfig();
    919         if (config == null) {
    920             config = defaultConfig;
    921         }
    922         if (config == Bitmap.Config.ALPHA_8) {
    923             return A_8(mRS);
    924         } else if (config == Bitmap.Config.RGB_565) {
    925             return RGB_565(mRS);
    926         } else if (config == Bitmap.Config.ARGB_4444) {
    927             return RGBA_4444(mRS);
    928         } else if (config == Bitmap.Config.ARGB_8888) {
    929             return RGBA_8888(mRS);
    930         } else {
    931             throw new IllegalArgumentException("Unknown configuration");
    932         }
    933     }
    934 
    935     public Mesh loadGeometry(int resId) {
    936         if (resId == 0) {
    937           return null;
    938         }
    939         FileA3D model = FileA3D.createFromResource(mRS, mRes, resId);
    940         if (model == null) {
    941           return null;
    942         }
    943         FileA3D.IndexEntry entry = model.getIndexEntry(0);
    944         if(entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) {
    945             return null;
    946         }
    947         return (Mesh) entry.getObject();
    948     }
    949 }
    950