CarouselRS.java revision e8cab95c5f73ddf6843d82793decc3adb4692860
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
17package com.android.ex.carousel;
18
19import android.content.res.Resources;
20import android.graphics.Bitmap;
21import android.graphics.Rect;
22import android.renderscript.*;
23import android.renderscript.RenderScript.RSMessageHandler;
24import android.util.Log;
25
26import static android.renderscript.Element.*;
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 */
33public 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 boolean MIPMAP = false;
64    private static final boolean DBG = false;
65
66    private RenderScriptGL mRS;
67    private Resources mRes;
68    private ScriptC_carousel mScript;
69    private ScriptField_Card mCards;
70    private ScriptField_FragmentShaderConstants_s mFSConst;
71    private ProgramStore mProgramStoreAlphaZ;
72    private ProgramStore mProgramStoreAlphaNoZ;
73    private ProgramStore mProgramStoreNoAlphaZ;
74    private ProgramStore mProgramStoreNoAlphaNoZ;
75    private ProgramFragment mSingleTextureFragmentProgram;
76    private ProgramFragment mMultiTextureFragmentProgram;
77    private ProgramVertex mVertexProgram;
78    private ProgramRaster mRasterProgram;
79    private Allocation[] mAllocationPool;
80    private int mVisibleSlots;
81    private int mRowCount;
82    private int mPrefetchCardCount;
83    private CarouselCallback mCallback;
84    private float[] mEyePoint = new float[] { 2.0f, 0.0f, 0.0f };
85    private float[] mAtPoint = new float[] { 0.0f, 0.0f, 0.0f };
86    private float[] mUp = new float[] { 0.0f, 1.0f, 0.0f };
87
88    private static final String mSingleTextureShader = new String(
89            "varying vec2 varTex0;" +
90            "void main() {" +
91            "vec2 t0 = varTex0.xy;" +
92            "vec4 col = texture2D(UNI_Tex0, t0);" +
93            "gl_FragColor = col; " +
94            "}");
95
96    private static final String mMultiTextureShader = new String(
97            "varying vec2 varTex0;" +
98            "void main() {" +
99            "vec2 t0 = varTex0.xy;" +
100            "vec4 col = texture2D(UNI_Tex0, t0);" +
101            "vec4 col2 = texture2D(UNI_Tex1, t0);" +
102            "gl_FragColor = mix(col, col2, UNI_fadeAmount);}");
103
104    public static interface CarouselCallback {
105        /**
106         * Called when a card is selected
107         * @param n the id of the card
108         */
109        void onCardSelected(int n);
110
111        /**
112         * Called when the detail texture for a card is tapped
113         * @param n the id of the card
114         * @param x how far the user tapped from the left edge of the card, in pixels
115         * @param y how far the user tapped from the top edge of the card, in pixels
116         */
117        void onDetailSelected(int n, int x, int y);
118
119        /**
120         * Called when a card is long-pressed
121         * @param n the id of the card
122         * @param touchPosition position of where the user pressed, in screen coordinates
123         * @param detailCoordinates position of detail texture, in screen coordinates
124         */
125        void onCardLongPress(int n, int touchPosition[], Rect detailCoordinates);
126
127        /**
128         * Called when texture is needed for card n.  This happens when the given card becomes
129         * visible.
130         * @param n the id of the card
131         */
132        void onRequestTexture(int n);
133
134        /**
135         * Called when a texture is no longer needed for card n.  This happens when the card
136         * goes out of view.
137         * @param n the id of the card
138         */
139        void onInvalidateTexture(int n);
140
141        /**
142         * Called when detail texture is needed for card n.  This happens when the given card
143         * becomes visible.
144         * @param n the id of the card
145         */
146        void onRequestDetailTexture(int n);
147
148        /**
149         * Called when a detail texture is no longer needed for card n.  This happens when the card
150         * goes out of view.
151         * @param n the id of the card
152         */
153        void onInvalidateDetailTexture(int n);
154
155        /**
156         * Called when geometry is needed for card n.
157         * @param n the id of the card.
158         */
159        void onRequestGeometry(int n);
160
161        /**
162         * Called when geometry is no longer needed for card n. This happens when the card goes
163         * out of view.
164         * @param n the id of the card
165         */
166        void onInvalidateGeometry(int n);
167
168        /**
169         * Called when card animation (e.g. a fling) has started.
170         */
171        void onAnimationStarted();
172
173        /**
174         * Called when card animation has stopped.
175         * @param carouselRotationAngle the angle of rotation, in radians, at which the animation
176         * stopped.
177         */
178        void onAnimationFinished(float carouselRotationAngle);
179    };
180
181    private RSMessageHandler mRsMessage = new RSMessageHandler() {
182        public void run() {
183            if (mCallback == null) return;
184            switch (mID) {
185                case CMD_CARD_SELECTED:
186                    mCallback.onCardSelected(mData[0]);
187                    break;
188
189                case CMD_DETAIL_SELECTED:
190                    mCallback.onDetailSelected(mData[0], mData[1], mData[2]);
191                    break;
192
193                case CMD_CARD_LONGPRESS:
194                    int touchPosition[] = { mData[1], mData[2] };
195                    Rect detailCoordinates = new Rect(mData[3], mData[4], mData[5], mData[6]);
196                    mCallback.onCardLongPress(mData[0], touchPosition, detailCoordinates);
197                    break;
198
199                case CMD_REQUEST_TEXTURE:
200                    mCallback.onRequestTexture(mData[0]);
201                    break;
202
203                case CMD_INVALIDATE_TEXTURE:
204                    setTexture(mData[0], null);
205                    mCallback.onInvalidateTexture(mData[0]);
206                    break;
207
208                case CMD_REQUEST_DETAIL_TEXTURE:
209                    mCallback.onRequestDetailTexture(mData[0]);
210                    break;
211
212                case CMD_INVALIDATE_DETAIL_TEXTURE:
213                    setDetailTexture(mData[0], 0.0f, 0.0f, 0.0f, 0.0f, null);
214                    mCallback.onInvalidateDetailTexture(mData[0]);
215                    break;
216
217                case CMD_REQUEST_GEOMETRY:
218                    mCallback.onRequestGeometry(mData[0]);
219                    break;
220
221                case CMD_INVALIDATE_GEOMETRY:
222                    setGeometry(mData[0], null);
223                    mCallback.onInvalidateGeometry(mData[0]);
224                    break;
225
226                case CMD_ANIMATION_STARTED:
227                    mCallback.onAnimationStarted();
228                    break;
229
230                case CMD_ANIMATION_FINISHED:
231                    mCallback.onAnimationFinished(Float.intBitsToFloat(mData[0]));
232                    break;
233
234                case CMD_PING:
235                    if (DBG) Log.v(TAG, "PING...");
236                    break;
237
238                default:
239                    Log.e(TAG, "Unknown RSMessage: " + mID);
240            }
241        }
242    };
243
244    public CarouselRS(RenderScriptGL rs, Resources res, int resId) {
245        mRS = rs;
246        mRes = res;
247
248        // create the script object
249        mScript = new ScriptC_carousel(mRS, mRes, resId);
250        mRS.setMessageHandler(mRsMessage);
251        initProgramStore();
252        initFragmentProgram();
253        initRasterProgram();
254        initVertexProgram();
255        setSlotCount(DEFAULT_SLOT_COUNT);
256        setVisibleSlots(DEFAULT_VISIBLE_SLOTS);
257        setRowCount(DEFAULT_ROW_COUNT);
258        createCards(DEFAULT_CARD_COUNT);
259        setStartAngle(0.0f);
260        setCarouselRotationAngle(0.0f);
261        setRadius(1.0f);
262        setLookAt(mEyePoint, mAtPoint, mUp);
263        setRadius(20.0f);
264        // Fov: 25
265    }
266
267    public void setLookAt(float[] eye, float[] at, float[] up) {
268        for (int i = 0; i < 3; i++) {
269            mEyePoint[i] = eye[i];
270            mAtPoint[i] = at[i];
271            mUp[i] = up[i];
272        }
273        mScript.invoke_lookAt(eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0], up[1], up[2]);
274    }
275
276    public void setRadius(float radius) {
277        mScript.invoke_setRadius(radius);
278    }
279
280    public void setCardRotation(float cardRotation) {
281        mScript.set_cardRotation(cardRotation);
282    }
283
284    public void setCardsFaceTangent(boolean faceTangent) {
285        mScript.set_cardsFaceTangent(faceTangent);
286    }
287
288    public void setSwaySensitivity(float swaySensitivity) {
289        mScript.set_swaySensitivity(swaySensitivity);
290    }
291
292    public void setFrictionCoefficient(float frictionCoeff) {
293        mScript.set_frictionCoeff(frictionCoeff);
294    }
295
296    public void setDragFactor(float dragFactor) {
297        mScript.set_dragFactor(dragFactor);
298    }
299
300    public void setDragModel(int model) {
301        mScript.set_dragModel(model);
302    }
303
304    public void setFillDirection(int direction) {
305        mScript.set_fillDirection(direction);
306    }
307
308    public void setDefaultCardMatrix(float[] matrix) {
309        int dimensions;
310        if (matrix == null || matrix.length == 0) {
311          dimensions = 0;
312        } else if (matrix.length == 16) {
313          dimensions = 4;
314        } else if (matrix.length == 9) {
315          dimensions = 3;
316        } else {
317          throw new IllegalArgumentException("matrix length not 0,9 or 16");
318        }
319
320        Matrix4f rsMatrix = new Matrix4f();  // initialized as identity.
321        for (int i = 0; i < dimensions; i++) {
322            for (int j = 0; j < dimensions; j++) {
323                rsMatrix.set(i, j, matrix[i*dimensions + j]);
324            }
325        }
326        mScript.set_defaultCardMatrix(rsMatrix);
327    }
328
329    private void initVertexProgram() {
330        ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
331        mVertexProgram = pvb.create();
332        ProgramVertex.MatrixAllocation pva = new ProgramVertex.MatrixAllocation(mRS);
333        mVertexProgram.bindAllocation(pva);
334        pva.setupProjectionNormalized(1, 1);
335        mScript.set_vertexProgram(mVertexProgram);
336    }
337
338    private void initRasterProgram() {
339        ProgramRaster.Builder programRasterBuilder = new ProgramRaster.Builder(mRS);
340        mRasterProgram = programRasterBuilder.create();
341        //mRasterProgram.setCullMode(CullMode.NONE);
342        mScript.set_rasterProgram(mRasterProgram);
343    }
344
345    private void initFragmentProgram() {
346        //
347        // Single texture program
348        //
349        ProgramFragment.ShaderBuilder pfbSingle = new ProgramFragment.ShaderBuilder(mRS);
350        // Specify the resource that contains the shader string
351        pfbSingle.setShader(mSingleTextureShader);
352        // Tell the builder how many textures we have
353        pfbSingle.setTextureCount(1);
354        mSingleTextureFragmentProgram = pfbSingle.create();
355        // Bind the source of constant data
356        mSingleTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
357
358        //
359        // Multi texture program
360        //
361        mFSConst = new ScriptField_FragmentShaderConstants_s(mRS, 1);
362        mScript.bind_shaderConstants(mFSConst);
363        ProgramFragment.ShaderBuilder pfbMulti = new ProgramFragment.ShaderBuilder(mRS);
364        // Specify the resource that contains the shader string
365        pfbMulti.setShader(mMultiTextureShader);
366        // Tell the builder how many textures we have
367        pfbMulti.setTextureCount(2);
368        // Define the constant input layout
369        pfbMulti.addConstant(mFSConst.getAllocation().getType());
370        mMultiTextureFragmentProgram = pfbMulti.create();
371        // Bind the source of constant data
372        mMultiTextureFragmentProgram.bindConstants(mFSConst.getAllocation(), 0);
373        mMultiTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0);
374        mMultiTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 1);
375
376        mScript.set_linearClamp(Sampler.CLAMP_LINEAR(mRS));
377        mScript.set_singleTextureFragmentProgram(mSingleTextureFragmentProgram);
378        mScript.set_multiTextureFragmentProgram(mMultiTextureFragmentProgram);
379    }
380
381    private void initProgramStore() {
382        final boolean dither = true;
383        mProgramStoreAlphaZ = new ProgramStore.Builder(mRS)
384                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
385                        ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
386                .setDitherEnable(dither)
387                .setDepthFunc(ProgramStore.DepthFunc.LESS)
388                .setDepthMask(true)
389                .create();
390        mScript.set_programStoreAlphaZ(mProgramStoreAlphaZ);
391
392        mProgramStoreAlphaNoZ = new ProgramStore.Builder(mRS)
393                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE,
394                        ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA)
395                .setDitherEnable(dither)
396                .setDepthFunc(ProgramStore.DepthFunc.ALWAYS)
397                .setDepthMask(false)
398                .create();
399        mScript.set_programStoreAlphaNoZ(mProgramStoreAlphaNoZ);
400
401        mProgramStoreNoAlphaZ = new ProgramStore.Builder(mRS)
402                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ZERO)
403                .setDitherEnable(dither)
404                .setDepthFunc(ProgramStore.DepthFunc.LESS)
405                .setDepthMask(true)
406                .create();
407        mScript.set_programStoreNoAlphaZ(mProgramStoreNoAlphaZ);
408
409        mProgramStoreNoAlphaNoZ = new ProgramStore.Builder(mRS)
410                .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ZERO)
411                .setDitherEnable(dither)
412                .setDepthFunc(ProgramStore.DepthFunc.ALWAYS)
413                .setDepthMask(false)
414                .create();
415        mScript.set_programStoreNoAlphaNoZ(mProgramStoreNoAlphaNoZ);
416    }
417
418    public void createCards(int count)
419    {
420        // Because RenderScript can't have allocations with 0 dimensions, we always create
421        // an allocation of at least one card. This relies on invoke_createCards() to keep
422        // track of when the allocation is not valid.
423        if (mCards != null) {
424            // resize the array
425            int oldSize = mCards.getAllocation().getType().getX();
426            int newSize = count > 0 ? count : 1;
427            mCards.resize(newSize);
428            mScript.invoke_createCards(oldSize, count);
429        } else {
430            // create array from scratch
431            mCards = new ScriptField_Card(mRS, count > 0 ? count : 1);
432            mScript.bind_cards(mCards);
433            mScript.invoke_createCards(0, count);
434        }
435    }
436
437    public void setVisibleSlots(int count)
438    {
439        mVisibleSlots = count;
440        mScript.set_visibleSlotCount(count);
441    }
442
443    public void setVisibleDetails(int count) {
444        mScript.set_visibleDetailCount(count);
445    }
446
447    public void setRowCount(int count) {
448        mRowCount = count;
449        mScript.set_rowCount(count);
450    }
451
452    public void setRowSpacing(float spacing) {
453        mScript.set_rowSpacing(spacing);
454    }
455
456    public void setPrefetchCardCount(int count) {
457        mPrefetchCardCount = count;
458        mScript.set_prefetchCardCount(count);
459    }
460
461    public void setDetailTextureAlignment(int alignment) {
462        mScript.set_detailTextureAlignment(alignment);
463    }
464
465    public void setForceBlendCardsWithZ(boolean enabled) {
466        mScript.set_forceBlendCardsWithZ(enabled);
467    }
468
469    public void setDrawRuler(boolean drawRuler) {
470        mScript.set_drawRuler(drawRuler);
471    }
472
473    public void setDefaultBitmap(Bitmap bitmap)
474    {
475        mScript.set_defaultTexture(allocationFromBitmap(bitmap, MIPMAP));
476    }
477
478    public void setLoadingBitmap(Bitmap bitmap)
479    {
480        mScript.set_loadingTexture(allocationFromBitmap(bitmap, MIPMAP));
481    }
482
483    public void setDefaultGeometry(Mesh mesh)
484    {
485        mScript.set_defaultGeometry(mesh);
486    }
487
488    public void setLoadingGeometry(Mesh mesh)
489    {
490        mScript.set_loadingGeometry(mesh);
491    }
492
493    public void setStartAngle(float theta)
494    {
495        mScript.set_startAngle(theta);
496    }
497
498    public void setCarouselRotationAngle(float theta) {
499        mScript.invoke_setCarouselRotationAngle(theta);
500    }
501
502    public void setCallback(CarouselCallback callback)
503    {
504        mCallback = callback;
505    }
506
507    private Allocation allocationFromBitmap(Bitmap bitmap, boolean mipmap)
508    {
509        if (bitmap == null) return null;
510        Allocation allocation = Allocation.createFromBitmap(mRS, bitmap,
511                elementForBitmap(bitmap, Bitmap.Config.ARGB_4444), mipmap);
512        allocation.uploadToTexture(0);
513        return allocation;
514    }
515
516    private Allocation allocationFromPool(int n, Bitmap bitmap, boolean mipmap)
517    {
518        int count = (mVisibleSlots + mPrefetchCardCount) * mRowCount;
519        if (mAllocationPool == null || mAllocationPool.length != count) {
520            Allocation[] tmp = new Allocation[count];
521            int oldsize = mAllocationPool == null ? 0 : mAllocationPool.length;
522            for (int i = 0; i < Math.min(count, oldsize); i++) {
523                tmp[i] = mAllocationPool[i];
524            }
525            mAllocationPool = tmp;
526        }
527        Allocation allocation = mAllocationPool[n % count];
528        if (allocation == null) {
529            allocation = allocationFromBitmap(bitmap, mipmap);
530            mAllocationPool[n % count]  = allocation;
531        } else if (bitmap != null) {
532            if (bitmap.getWidth() == allocation.getType().getX()
533                && bitmap.getHeight() == allocation.getType().getY()) {
534                allocation.copyFrom(bitmap);
535                allocation.uploadToTexture(0);
536            } else {
537                Log.v(TAG, "Warning, bitmap has different size. Taking slow path");
538                allocation = allocationFromBitmap(bitmap, mipmap);
539                mAllocationPool[n % count]  = allocation;
540            }
541        }
542        return allocation;
543    }
544
545    public void setTexture(int n, Bitmap bitmap)
546    {
547        if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
548
549        synchronized(this) {
550            ScriptField_Card.Item item = mCards.get(n);
551            if (item == null) {
552                if (DBG) Log.v(TAG, "setTexture(): no item at index " + n);
553                item = new ScriptField_Card.Item();
554            }
555            if (bitmap != null) {
556                item.texture = allocationFromPool(n, bitmap, MIPMAP);
557            } else {
558                if (item.texture != null) {
559                    if (DBG) Log.v(TAG, "unloading texture " + n);
560                    item.texture = null;
561                }
562            }
563            mCards.set(item, n, false); // This is primarily used for reference counting.
564            mScript.invoke_setTexture(n, item.texture);
565        }
566    }
567
568    void setDetailTexture(int n, float offx, float offy, float loffx, float loffy, Bitmap bitmap)
569    {
570        if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
571
572        synchronized(this) {
573            ScriptField_Card.Item item = mCards.get(n);
574            if (item == null) {
575                if (DBG) Log.v(TAG, "setDetailTexture(): no item at index " + n);
576                item = new ScriptField_Card.Item();
577            }
578            float width = 0.0f;
579            float height = 0.0f;
580            if (bitmap != null) {
581                item.detailTexture = allocationFromBitmap(bitmap, MIPMAP);
582                width = bitmap.getWidth();
583                height = bitmap.getHeight();
584            } else {
585                if (item.detailTexture != null) {
586                    if (DBG) Log.v(TAG, "unloading detail texture " + n);
587                    // Don't wait for GC to free native memory.
588                    // Only works if textures are not shared.
589                    item.detailTexture.destroy();
590                    item.detailTexture = null;
591                }
592            }
593            mCards.set(item, n, false); // This is primarily used for reference counting.
594            mScript.invoke_setDetailTexture(n, offx, offy, loffx, loffy, item.detailTexture);
595        }
596    }
597
598    void invalidateTexture(int n, boolean eraseCurrent)
599    {
600        if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
601
602        synchronized(this) {
603            ScriptField_Card.Item item = mCards.get(n);
604            if (item == null) {
605                // This card was never created, so there's nothing to invalidate.
606                return;
607            }
608            if (eraseCurrent && item.texture != null) {
609                if (DBG) Log.v(TAG, "unloading texture " + n);
610                // Don't wait for GC to free native memory.
611                // Only works if textures are not shared.
612                item.texture.destroy();
613                item.texture = null;
614            }
615            mCards.set(item, n, false); // This is primarily used for reference counting.
616            mScript.invoke_invalidateTexture(n, eraseCurrent);
617        }
618    }
619
620    void invalidateDetailTexture(int n, boolean eraseCurrent)
621    {
622        if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
623
624        synchronized(this) {
625            ScriptField_Card.Item item = mCards.get(n);
626            if (item == null) {
627                // This card was never created, so there's nothing to invalidate.
628                return;
629            }
630            if (eraseCurrent && item.detailTexture != null) {
631                if (DBG) Log.v(TAG, "unloading detail texture " + n);
632                // Don't wait for GC to free native memory.
633                // Only works if textures are not shared.
634                item.detailTexture.destroy();
635                item.detailTexture = null;
636            }
637            mCards.set(item, n, false); // This is primarily used for reference counting.
638            mScript.invoke_invalidateDetailTexture(n, eraseCurrent);
639        }
640    }
641
642    public void setGeometry(int n, Mesh geometry)
643    {
644        if (n < 0) throw new IllegalArgumentException("Index cannot be negative");
645
646        synchronized(this) {
647            final boolean mipmap = false;
648            ScriptField_Card.Item item = mCards.get(n);
649            if (item == null) {
650                if (DBG) Log.v(TAG, "setGeometry(): no item at index " + n);
651                item = new ScriptField_Card.Item();
652            }
653            if (geometry != null) {
654                item.geometry = geometry;
655            } else {
656                if (DBG) Log.v(TAG, "unloading geometry " + n);
657                if (item.geometry != null) {
658                    // item.geometry.destroy();
659                    item.geometry = null;
660                }
661            }
662            mCards.set(item, n, false);
663            mScript.invoke_setGeometry(n, item.geometry);
664        }
665    }
666
667    public void setBackgroundColor(Float4 color) {
668        mScript.set_backgroundColor(color);
669    }
670
671    public void setBackgroundTexture(Bitmap bitmap) {
672        Allocation texture = null;
673        if (bitmap != null) {
674            texture = Allocation.createFromBitmap(mRS, bitmap,
675                    elementForBitmap(bitmap, Bitmap.Config.RGB_565), MIPMAP);
676            texture.uploadToTexture(0);
677        }
678        mScript.set_backgroundTexture(texture);
679    }
680
681    public void setDetailLineTexture(Bitmap bitmap) {
682        Allocation texture = null;
683        if (bitmap != null) {
684            texture = Allocation.createFromBitmap(mRS, bitmap,
685                    elementForBitmap(bitmap, Bitmap.Config.ARGB_4444), MIPMAP);
686            texture.uploadToTexture(0);
687        }
688        mScript.set_detailLineTexture(texture);
689    }
690
691    public void setDetailLoadingTexture(Bitmap bitmap) {
692        Allocation texture = null;
693        if (bitmap != null) {
694            texture = Allocation.createFromBitmap(mRS, bitmap,
695                    elementForBitmap(bitmap, Bitmap.Config.ARGB_4444), MIPMAP);
696            texture.uploadToTexture(0);
697        }
698        mScript.set_detailLoadingTexture(texture);
699    }
700
701    public void pauseRendering() {
702        // Used to update multiple states at once w/o redrawing for each.
703        mRS.bindRootScript(null);
704    }
705
706    public void resumeRendering() {
707        mRS.bindRootScript(mScript);
708    }
709
710    public void doLongPress() {
711        mScript.invoke_doLongPress();
712    }
713
714    public void doMotion(float x, float y, long t) {
715        mScript.invoke_doMotion(x, y, t);
716    }
717
718    public void doStart(float x, float y, long t) {
719        mScript.invoke_doStart(x, y, t);
720    }
721
722    public void doStop(float x, float y, long t) {
723        mScript.invoke_doStop(x, y, t);
724    }
725
726    public void setSlotCount(int n) {
727        mScript.set_slotCount(n);
728    }
729
730    public void setRezInCardCount(float alpha) {
731        mScript.set_rezInCardCount(alpha);
732    }
733
734    public void setFadeInDuration(long t) {
735        mScript.set_fadeInDuration((int)t); // TODO: Remove cast when RS supports exporting longs
736    }
737
738    private Element elementForBitmap(Bitmap bitmap, Bitmap.Config defaultConfig) {
739        Bitmap.Config config = bitmap.getConfig();
740        if (config == null) {
741            config = defaultConfig;
742        }
743        if (config == Bitmap.Config.ALPHA_8) {
744            return A_8(mRS);
745        } else if (config == Bitmap.Config.RGB_565) {
746            return RGB_565(mRS);
747        } else if (config == Bitmap.Config.ARGB_4444) {
748            return RGBA_4444(mRS);
749        } else if (config == Bitmap.Config.ARGB_8888) {
750            return RGBA_8888(mRS);
751        } else {
752            throw new IllegalArgumentException("Unknown configuration");
753        }
754    }
755}
756