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 static android.renderscript.Element.*;
24import android.renderscript.Program.TextureType;
25import android.renderscript.RenderScript.RSMessageHandler;
26import 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 */
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 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