/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ex.carousel; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Rect; import android.renderscript.*; import static android.renderscript.Element.*; import android.renderscript.Program.TextureType; import android.renderscript.RenderScript.RSMessageHandler; import android.util.Log; /** * This is a support class for Carousel renderscript. It handles most of the low-level interactions * with Renderscript as well as dispatching events. * */ public class CarouselRS { private static final int DEFAULT_VISIBLE_SLOTS = 1; private static final int DEFAULT_CARD_COUNT = 0; private static final int DEFAULT_ROW_COUNT = 1; // Client messages *** THIS LIST MUST MATCH THOSE IN carousel.rs *** public static final int CMD_CARD_SELECTED = 100; public static final int CMD_DETAIL_SELECTED = 105; public static final int CMD_CARD_LONGPRESS = 110; public static final int CMD_REQUEST_TEXTURE = 200; public static final int CMD_INVALIDATE_TEXTURE = 210; public static final int CMD_REQUEST_GEOMETRY = 300; public static final int CMD_INVALIDATE_GEOMETRY = 310; public static final int CMD_ANIMATION_STARTED = 400; public static final int CMD_ANIMATION_FINISHED = 500; public static final int CMD_REQUEST_DETAIL_TEXTURE = 600; public static final int CMD_INVALIDATE_DETAIL_TEXTURE = 610; public static final int CMD_PING = 1000; // for debugging // Drag models *** THIS LIST MUST MATCH THOSE IN carousel.rs *** public static final int DRAG_MODEL_SCREEN_DELTA = 0; public static final int DRAG_MODEL_PLANE = 1; public static final int DRAG_MODEL_CYLINDER_INSIDE = 2; public static final int DRAG_MODEL_CYLINDER_OUTSIDE = 3; public static final int FILL_DIRECTION_CCW = +1; public static final int FILL_DIRECTION_CW = -1; private static final String TAG = "CarouselRS"; private static final int DEFAULT_SLOT_COUNT = 10; private static final Allocation.MipmapControl MIPMAP = Allocation.MipmapControl.MIPMAP_NONE; private static final boolean DBG = false; private RenderScriptGL mRS; private Resources mRes; private ScriptC_carousel mScript; private ScriptField_Card mCards; private ScriptField_FragmentShaderConstants_s mFSConst; private ScriptField_ProgramStore_s mProgramStoresCard; private ProgramFragment mSingleTextureFragmentProgram; private ProgramFragment mSingleTextureBlendingFragmentProgram; private ProgramFragment mMultiTextureFragmentProgram; private ProgramFragment mMultiTextureBlendingFragmentProgram; private ProgramVertex mVertexProgram; private ProgramRaster mRasterProgram; private Allocation[] mAllocationPool; private boolean mForceBlendCardsWithZ; private int mVisibleSlots; private int mRowCount; private int mPrefetchCardCount; private CarouselCallback mCallback; private float[] mEyePoint = new float[] { 2.0f, 0.0f, 0.0f }; private float[] mAtPoint = new float[] { 0.0f, 0.0f, 0.0f }; private float[] mUp = new float[] { 0.0f, 1.0f, 0.0f }; private static final String mSingleTextureShader = new String( "varying vec2 varTex0;" + "void main() {" + "vec2 t0 = varTex0.xy;" + "vec4 col = texture2D(UNI_Tex0, t0);" + "gl_FragColor = col; " + "}"); private static final String mSingleTextureBlendingShader = new String( "varying vec2 varTex0;" + "void main() {" + "vec2 t0 = varTex0.xy;" + "vec4 col = texture2D(UNI_Tex0, t0);" + "gl_FragColor = col * UNI_overallAlpha; " + "}"); private static final String mMultiTextureShader = new String( "varying vec2 varTex0;" + "void main() {" + "vec2 t0 = varTex0.xy;" + "vec4 col = texture2D(UNI_Tex0, t0);" + "vec4 col2 = texture2D(UNI_Tex1, t0);" + "gl_FragColor = mix(col, col2, UNI_fadeAmount);}"); private static final String mMultiTextureBlendingShader = new String( "varying vec2 varTex0;" + "void main() {" + "vec2 t0 = varTex0.xy;" + "vec4 col = texture2D(UNI_Tex0, t0);" + "vec4 col2 = texture2D(UNI_Tex1, t0);" + "gl_FragColor = mix(col, col2, UNI_fadeAmount) * UNI_overallAlpha;" + "}" ); public static interface CarouselCallback { /** * Called when a card is selected * @param n the id of the card */ void onCardSelected(int n); /** * Called when the detail texture for a card is tapped * @param n the id of the card * @param x how far the user tapped from the left edge of the card, in pixels * @param y how far the user tapped from the top edge of the card, in pixels */ void onDetailSelected(int n, int x, int y); /** * Called when a card is long-pressed * @param n the id of the card * @param touchPosition position of where the user pressed, in screen coordinates * @param detailCoordinates position of detail texture, in screen coordinates */ void onCardLongPress(int n, int touchPosition[], Rect detailCoordinates); /** * Called when texture is needed for card n. This happens when the given card becomes * visible. * @param n the id of the card */ void onRequestTexture(int n); /** * Called when a texture is no longer needed for card n. This happens when the card * goes out of view. * @param n the id of the card */ void onInvalidateTexture(int n); /** * Called when detail texture is needed for card n. This happens when the given card * becomes visible. * @param n the id of the card */ void onRequestDetailTexture(int n); /** * Called when a detail texture is no longer needed for card n. This happens when the card * goes out of view. * @param n the id of the card */ void onInvalidateDetailTexture(int n); /** * Called when geometry is needed for card n. * @param n the id of the card. */ void onRequestGeometry(int n); /** * Called when geometry is no longer needed for card n. This happens when the card goes * out of view. * @param n the id of the card */ void onInvalidateGeometry(int n); /** * Called when card animation (e.g. a fling) has started. */ void onAnimationStarted(); /** * Called when card animation has stopped. * @param carouselRotationAngle the angle of rotation, in radians, at which the animation * stopped. */ void onAnimationFinished(float carouselRotationAngle); }; private RSMessageHandler mRsMessage = new RSMessageHandler() { public void run() { if (mCallback == null) return; switch (mID) { case CMD_CARD_SELECTED: mCallback.onCardSelected(mData[0]); break; case CMD_DETAIL_SELECTED: mCallback.onDetailSelected(mData[0], mData[1], mData[2]); break; case CMD_CARD_LONGPRESS: int touchPosition[] = { mData[1], mData[2] }; Rect detailCoordinates = new Rect(mData[3], mData[4], mData[5], mData[6]); mCallback.onCardLongPress(mData[0], touchPosition, detailCoordinates); break; case CMD_REQUEST_TEXTURE: mCallback.onRequestTexture(mData[0]); break; case CMD_INVALIDATE_TEXTURE: setTexture(mData[0], null); mCallback.onInvalidateTexture(mData[0]); break; case CMD_REQUEST_DETAIL_TEXTURE: mCallback.onRequestDetailTexture(mData[0]); break; case CMD_INVALIDATE_DETAIL_TEXTURE: setDetailTexture(mData[0], 0.0f, 0.0f, 0.0f, 0.0f, null); mCallback.onInvalidateDetailTexture(mData[0]); break; case CMD_REQUEST_GEOMETRY: mCallback.onRequestGeometry(mData[0]); break; case CMD_INVALIDATE_GEOMETRY: setGeometry(mData[0], null); mCallback.onInvalidateGeometry(mData[0]); break; case CMD_ANIMATION_STARTED: mCallback.onAnimationStarted(); break; case CMD_ANIMATION_FINISHED: mCallback.onAnimationFinished(Float.intBitsToFloat(mData[0])); break; case CMD_PING: if (DBG) Log.v(TAG, "PING..."); break; default: Log.e(TAG, "Unknown RSMessage: " + mID); } } }; public CarouselRS(RenderScriptGL rs, Resources res, int resId) { mRS = rs; mRes = res; // create the script object mScript = new ScriptC_carousel(mRS, mRes, resId); mRS.setMessageHandler(mRsMessage); initProgramStore(); initFragmentProgram(); initRasterProgram(); initVertexProgram(); setSlotCount(DEFAULT_SLOT_COUNT); setVisibleSlots(DEFAULT_VISIBLE_SLOTS); setRowCount(DEFAULT_ROW_COUNT); createCards(DEFAULT_CARD_COUNT); setStartAngle(0.0f); setCarouselRotationAngle(0.0f); setRadius(1.0f); setLookAt(mEyePoint, mAtPoint, mUp); setRadius(20.0f); // Fov: 25 } public void setLookAt(float[] eye, float[] at, float[] up) { for (int i = 0; i < 3; i++) { mEyePoint[i] = eye[i]; mAtPoint[i] = at[i]; mUp[i] = up[i]; } mScript.invoke_lookAt(eye[0], eye[1], eye[2], at[0], at[1], at[2], up[0], up[1], up[2]); } public void setRadius(float radius) { mScript.invoke_setRadius(radius); } public void setCardRotation(float cardRotation) { mScript.set_cardRotation(cardRotation); } public void setCardsFaceTangent(boolean faceTangent) { mScript.set_cardsFaceTangent(faceTangent); } public void setSwaySensitivity(float swaySensitivity) { mScript.set_swaySensitivity(swaySensitivity); } public void setFrictionCoefficient(float frictionCoeff) { mScript.set_frictionCoeff(frictionCoeff); } public void setDragFactor(float dragFactor) { mScript.set_dragFactor(dragFactor); } public void setDragModel(int model) { mScript.set_dragModel(model); } public void setFillDirection(int direction) { mScript.set_fillDirection(direction); } private Matrix4f matrixFromFloat(float[] matrix) { int dimensions; if (matrix == null || matrix.length == 0) { dimensions = 0; } else if (matrix.length == 16) { dimensions = 4; } else if (matrix.length == 9) { dimensions = 3; } else { throw new IllegalArgumentException("matrix length not 0,9 or 16"); } Matrix4f rsMatrix = new Matrix4f(); // initialized as identity. for (int i = 0; i < dimensions; i++) { for (int j = 0; j < dimensions; j++) { rsMatrix.set(i, j, matrix[i*dimensions + j]); } } return rsMatrix; } public void setDefaultCardMatrix(float[] matrix) { mScript.set_defaultCardMatrix(matrixFromFloat(matrix)); } private void initVertexProgram() { ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); mVertexProgram = pvb.create(); ProgramVertexFixedFunction.Constants pva = new ProgramVertexFixedFunction.Constants(mRS); ((ProgramVertexFixedFunction)mVertexProgram).bindConstants(pva); Matrix4f proj = new Matrix4f(); proj.loadProjectionNormalized(1, 1); pva.setProjection(proj); mScript.set_vertexProgram(mVertexProgram); } private void initRasterProgram() { ProgramRaster.Builder programRasterBuilder = new ProgramRaster.Builder(mRS); mRasterProgram = programRasterBuilder.create(); //mRasterProgram.setCullMode(CullMode.NONE); mScript.set_rasterProgram(mRasterProgram); } private void initFragmentProgram() { // // Single texture program // ProgramFragment.Builder pfbSingle = new ProgramFragment.Builder(mRS); // Specify the resource that contains the shader string pfbSingle.setShader(mSingleTextureShader); // Tell the builder how many textures we have pfbSingle.addTexture(Program.TextureType.TEXTURE_2D); mSingleTextureFragmentProgram = pfbSingle.create(); // Bind the source of constant data mSingleTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0); // // Single texture program, plus blending // mFSConst = new ScriptField_FragmentShaderConstants_s(mRS, 1); mScript.bind_shaderConstants(mFSConst); ProgramFragment.Builder pfbSingleBlend = new ProgramFragment.Builder(mRS); // Specify the resource that contains the shader string pfbSingleBlend.setShader(mSingleTextureBlendingShader); // Tell the builder how many textures we have pfbSingleBlend.addTexture(Program.TextureType.TEXTURE_2D); // Define the constant input layout pfbSingleBlend.addConstant(mFSConst.getAllocation().getType()); mSingleTextureBlendingFragmentProgram = pfbSingleBlend.create(); // Bind the source of constant data mSingleTextureBlendingFragmentProgram.bindConstants(mFSConst.getAllocation(), 0); mSingleTextureBlendingFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0); // // Multi texture program // ProgramFragment.Builder pfbMulti = new ProgramFragment.Builder(mRS); // Specify the resource that contains the shader string pfbMulti.setShader(mMultiTextureShader); // Tell the builder how many textures we have pfbMulti.addTexture(Program.TextureType.TEXTURE_2D); pfbMulti.addTexture(Program.TextureType.TEXTURE_2D); // Define the constant input layout pfbMulti.addConstant(mFSConst.getAllocation().getType()); mMultiTextureFragmentProgram = pfbMulti.create(); // Bind the source of constant data mMultiTextureFragmentProgram.bindConstants(mFSConst.getAllocation(), 0); mMultiTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0); mMultiTextureFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 1); // // Multi texture program, plus blending // ProgramFragment.Builder pfbMultiBlend = new ProgramFragment.Builder(mRS); // Specify the resource that contains the shader string pfbMultiBlend.setShader(mMultiTextureBlendingShader); // Tell the builder how many textures we have pfbMultiBlend.addTexture(Program.TextureType.TEXTURE_2D); pfbMultiBlend.addTexture(Program.TextureType.TEXTURE_2D); // Define the constant input layout pfbMultiBlend.addConstant(mFSConst.getAllocation().getType()); mMultiTextureBlendingFragmentProgram = pfbMultiBlend.create(); // Bind the source of constant data mMultiTextureBlendingFragmentProgram.bindConstants(mFSConst.getAllocation(), 0); mMultiTextureBlendingFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 0); mMultiTextureBlendingFragmentProgram.bindSampler(Sampler.CLAMP_LINEAR(mRS), 1); mScript.set_linearClamp(Sampler.CLAMP_LINEAR(mRS)); mScript.set_singleTextureFragmentProgram(mSingleTextureFragmentProgram); mScript.set_singleTextureBlendingFragmentProgram(mSingleTextureBlendingFragmentProgram); mScript.set_multiTextureFragmentProgram(mMultiTextureFragmentProgram); mScript.set_multiTextureBlendingFragmentProgram(mMultiTextureBlendingFragmentProgram); } private void initProgramStore() { resizeProgramStoresCard(1); final boolean dither = true; final ProgramStore.DepthFunc depthFunc = mForceBlendCardsWithZ ? ProgramStore.DepthFunc.LESS : ProgramStore.DepthFunc.ALWAYS; // Background: Alpha disabled, depth optional mScript.set_programStoreBackground(new ProgramStore.Builder(mRS) .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ZERO) .setDitherEnabled(dither) .setDepthFunc(depthFunc) .setDepthMaskEnabled(mForceBlendCardsWithZ) .create()); // Card: Alpha enabled, depth optional setProgramStoreCard(0, new ProgramStore.Builder(mRS) .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA) .setDitherEnabled(dither) .setDepthFunc(depthFunc) .setDepthMaskEnabled(mForceBlendCardsWithZ) .create()); // Detail: Alpha enabled, depth disabled mScript.set_programStoreDetail(new ProgramStore.Builder(mRS) .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA) .setDitherEnabled(dither) .setDepthFunc(ProgramStore.DepthFunc.ALWAYS) .setDepthMaskEnabled(false) .create()); } public void createCards(int count) { // Because RenderScript can't have allocations with 0 dimensions, we always create // an allocation of at least one card. This relies on invoke_createCards() to keep // track of when the allocation is not valid. if (mCards != null && count > 0) { // resize the array int oldSize = mCards.getAllocation().getType().getX(); mCards.resize(count); mScript.invoke_createCards(oldSize, count); } else { // create array from scratch mCards = new ScriptField_Card(mRS, count > 0 ? count : 1); mScript.bind_cards(mCards); mScript.invoke_createCards(0, count); } } public void setVisibleSlots(int count) { mVisibleSlots = count; mScript.set_visibleSlotCount(count); } public void setVisibleDetails(int count) { mScript.set_visibleDetailCount(count); } public void setRowCount(int count) { mRowCount = count; mScript.set_rowCount(count); } public void setRowSpacing(float spacing) { mScript.set_rowSpacing(spacing); } public void setOverscrollSlots(float slots) { mScript.set_overscrollSlots(slots); } public void setFirstCardTop(boolean first) { mScript.set_firstCardTop(first); } public void setPrefetchCardCount(int count) { mPrefetchCardCount = count; mScript.set_prefetchCardCount(count); } public void setDetailTextureAlignment(int alignment) { mScript.set_detailTextureAlignment(alignment); } private void resizeProgramStoresCard(int count) { // enableResize works around a Renderscript bug that keeps resizes from being propagated. // TODO(jshuma): Remove enableResize once the Renderscript bug is fixed final boolean enableResize = false; if (mProgramStoresCard != null && enableResize) { int newSize = count > 0 ? count : 1; mProgramStoresCard.resize(newSize); } else { mProgramStoresCard = new ScriptField_ProgramStore_s(mRS, count > 0 ? count : 1); mScript.bind_programStoresCard(mProgramStoresCard); } } private void setProgramStoreCard(int n, ProgramStore programStore) { ScriptField_ProgramStore_s.Item item = mProgramStoresCard.get(n); if (item == null) { item = new ScriptField_ProgramStore_s.Item(); } item.programStore = programStore; mProgramStoresCard.set(item, n, false); mScript.invoke_setProgramStoresCard(n, programStore); } public void setStoreConfigs(int configs[]) { if (configs == null) { initProgramStore(); return; } final int count = configs.length; resizeProgramStoresCard(count); for (int i=0; i<count; ++i) { final int config = configs[i]; final boolean alpha = (config & CarouselController.STORE_CONFIG_ALPHA) != 0; final boolean depthReads = (config & CarouselController.STORE_CONFIG_DEPTH_READS) != 0; final boolean depthWrites = (config & CarouselController.STORE_CONFIG_DEPTH_WRITES) != 0; final boolean dither = true; final ProgramStore.BlendDstFunc dstFunc = alpha ? ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA : ProgramStore.BlendDstFunc.ZERO; final ProgramStore.DepthFunc depthFunc = depthReads ? ProgramStore.DepthFunc.LESS : ProgramStore.DepthFunc.ALWAYS; final ProgramStore ps = new ProgramStore.Builder(mRS) .setBlendFunc(ProgramStore.BlendSrcFunc.ONE, dstFunc) .setDitherEnabled(dither) .setDepthFunc(depthFunc) .setDepthMaskEnabled(depthWrites) .create(); setProgramStoreCard(i, ps); } } /** * Sets whether the background texture and default card geometry are to be drawn with respect * to the depth buffer (both reading from it and writing to it). * * This method is a specialization of functionality that can be done with greater flexibility * by setStoreConfigs. Calling setForceBlendCardsWithZ() after calling setStoreConfigs() * results in the values set in setStoreConfigs() being discarded. * * @param enabled true to read from and write to the depth buffer, false to ignore it */ public void setForceBlendCardsWithZ(boolean enabled) { mForceBlendCardsWithZ = enabled; initProgramStore(); } public void setDrawRuler(boolean drawRuler) { mScript.set_drawRuler(drawRuler); } public void setDefaultBitmap(Bitmap bitmap) { mScript.set_defaultTexture(allocationFromBitmap(bitmap, MIPMAP)); } public void setLoadingBitmap(Bitmap bitmap) { mScript.set_loadingTexture(allocationFromBitmap(bitmap, MIPMAP)); } public void setDefaultGeometry(Mesh mesh) { mScript.set_defaultGeometry(mesh); } public void setLoadingGeometry(Mesh mesh) { mScript.set_loadingGeometry(mesh); } public void setStartAngle(float theta) { mScript.set_startAngle(theta); } public void setCarouselRotationAngle(float theta) { mScript.invoke_setCarouselRotationAngle(theta); } public void setCarouselRotationAngle(float endAngle, int milliseconds, int interpolationMode, float maxAnimatedArc) { mScript.invoke_setCarouselRotationAngle2(endAngle, milliseconds, interpolationMode, maxAnimatedArc); } public void setCallback(CarouselCallback callback) { mCallback = callback; } private Allocation allocationFromBitmap(Bitmap bitmap, Allocation.MipmapControl mipmap) { if (bitmap == null) return null; Allocation allocation = Allocation.createFromBitmap(mRS, bitmap, mipmap, Allocation.USAGE_GRAPHICS_TEXTURE); return allocation; } private Allocation allocationFromPool(int n, Bitmap bitmap, Allocation.MipmapControl mipmap) { int count = (mVisibleSlots + 2*mPrefetchCardCount) * mRowCount; if (mAllocationPool == null || mAllocationPool.length != count) { Allocation[] tmp = new Allocation[count]; int oldsize = mAllocationPool == null ? 0 : mAllocationPool.length; for (int i = 0; i < Math.min(count, oldsize); i++) { tmp[i] = mAllocationPool[i]; } mAllocationPool = tmp; } Allocation allocation = mAllocationPool[n % count]; if (allocation == null) { allocation = allocationFromBitmap(bitmap, mipmap); mAllocationPool[n % count] = allocation; } else if (bitmap != null) { if (bitmap.getWidth() == allocation.getType().getX() && bitmap.getHeight() == allocation.getType().getY()) { allocation.copyFrom(bitmap); } else { Log.v(TAG, "Warning, bitmap has different size. Taking slow path"); allocation = allocationFromBitmap(bitmap, mipmap); mAllocationPool[n % count] = allocation; } } return allocation; } private ScriptField_Card.Item getCard(int n) { ScriptField_Card.Item item; try { item = mCards.get(n); } catch (ArrayIndexOutOfBoundsException e) { if (DBG) Log.v(TAG, "getCard(): no item at index " + n); item = null; } return item; } private ScriptField_Card.Item getOrCreateCard(int n) { ScriptField_Card.Item item = getCard(n); if (item == null) { if (DBG) Log.v(TAG, "getOrCreateCard(): no item at index " + n + "; creating new"); item = new ScriptField_Card.Item(); } return item; } private void setCard(int n, ScriptField_Card.Item item) { try { mCards.set(item, n, false); // This is primarily used for reference counting. } catch (ArrayIndexOutOfBoundsException e) { // The specified index didn't exist. This can happen when a stale invalidate // request outlived an array resize request. Something might be getting dropped, // but there's not much we can do about this at this point to recover. Log.w(TAG, "setCard(" + n + "): Texture " + n + " doesn't exist"); } } public void setTexture(int n, Bitmap bitmap) { if (n < 0) throw new IllegalArgumentException("Index cannot be negative"); synchronized(this) { ScriptField_Card.Item item = getOrCreateCard(n); if (bitmap != null) { item.texture = allocationFromPool(n, bitmap, MIPMAP); } else { if (item.texture != null) { if (DBG) Log.v(TAG, "unloading texture " + n); item.texture = null; } } setCard(n, item); mScript.invoke_setTexture(n, item.texture); } } void setDetailTexture(int n, float offx, float offy, float loffx, float loffy, Bitmap bitmap) { if (n < 0) throw new IllegalArgumentException("Index cannot be negative"); synchronized(this) { ScriptField_Card.Item item = getOrCreateCard(n); float width = 0.0f; float height = 0.0f; if (bitmap != null) { item.detailTexture = allocationFromBitmap(bitmap, MIPMAP); width = bitmap.getWidth(); height = bitmap.getHeight(); } else { if (item.detailTexture != null) { if (DBG) Log.v(TAG, "unloading detail texture " + n); // Don't wait for GC to free native memory. // Only works if textures are not shared. item.detailTexture.destroy(); item.detailTexture = null; } } setCard(n, item); mScript.invoke_setDetailTexture(n, offx, offy, loffx, loffy, item.detailTexture); } } void invalidateTexture(int n, boolean eraseCurrent) { if (n < 0) throw new IllegalArgumentException("Index cannot be negative"); synchronized(this) { ScriptField_Card.Item item = getCard(n); if (item == null) { // This card was never created, so there's nothing to invalidate. return; } if (eraseCurrent && item.texture != null) { if (DBG) Log.v(TAG, "unloading texture " + n); // Don't wait for GC to free native memory. // Only works if textures are not shared. item.texture.destroy(); item.texture = null; } setCard(n, item); mScript.invoke_invalidateTexture(n, eraseCurrent); } } void invalidateDetailTexture(int n, boolean eraseCurrent) { if (n < 0) throw new IllegalArgumentException("Index cannot be negative"); synchronized(this) { ScriptField_Card.Item item = getCard(n); if (item == null) { // This card was never created, so there's nothing to invalidate. return; } if (eraseCurrent && item.detailTexture != null) { if (DBG) Log.v(TAG, "unloading detail texture " + n); // Don't wait for GC to free native memory. // Only works if textures are not shared. item.detailTexture.destroy(); item.detailTexture = null; } setCard(n, item); mScript.invoke_invalidateDetailTexture(n, eraseCurrent); } } public void setGeometry(int n, Mesh geometry) { if (n < 0) throw new IllegalArgumentException("Index cannot be negative"); synchronized(this) { final boolean mipmap = false; ScriptField_Card.Item item = getOrCreateCard(n); if (geometry != null) { item.geometry = geometry; } else { if (DBG) Log.v(TAG, "unloading geometry " + n); if (item.geometry != null) { // item.geometry.destroy(); item.geometry = null; } } setCard(n, item); mScript.invoke_setGeometry(n, item.geometry); } } public void setMatrix(int n, float[] matrix) { if (n < 0) throw new IllegalArgumentException("Index cannot be negative"); synchronized(this) { final boolean mipmap = false; ScriptField_Card.Item item = getOrCreateCard(n); if (matrix != null) { item.matrix = matrixFromFloat(matrix); } else { if (DBG) Log.v(TAG, "unloading matrix " + n); item.matrix = null; } setCard(n, item); mScript.invoke_setMatrix(n, item.matrix); } } public void setBackgroundColor(Float4 color) { mScript.set_backgroundColor(color); } public void setBackgroundTexture(Bitmap bitmap) { Allocation texture = null; if (bitmap != null) { texture = Allocation.createFromBitmap(mRS, bitmap, MIPMAP, Allocation.USAGE_GRAPHICS_TEXTURE); } mScript.set_backgroundTexture(texture); } public void setDetailLineTexture(Bitmap bitmap) { Allocation texture = null; if (bitmap != null) { texture = Allocation.createFromBitmap(mRS, bitmap, MIPMAP, Allocation.USAGE_GRAPHICS_TEXTURE); } mScript.set_detailLineTexture(texture); } public void setDetailLoadingTexture(Bitmap bitmap) { Allocation texture = null; if (bitmap != null) { texture = Allocation.createFromBitmap(mRS, bitmap, MIPMAP, Allocation.USAGE_GRAPHICS_TEXTURE); } mScript.set_detailLoadingTexture(texture); } public void pauseRendering() { // Used to update multiple states at once w/o redrawing for each. mRS.bindRootScript(null); } public void resumeRendering() { mRS.bindRootScript(mScript); } public void doLongPress() { mScript.invoke_doLongPress(); } public void doMotion(float x, float y, long t) { mScript.invoke_doMotion(x, y, t); } public void doStart(float x, float y, long t) { mScript.invoke_doStart(x, y, t); } public void doStop(float x, float y, long t) { mScript.invoke_doStop(x, y, t); } public void setSlotCount(int n) { mScript.set_slotCount(n); } public void setRezInCardCount(float alpha) { mScript.set_rezInCardCount(alpha); } public void setFadeInDuration(long t) { mScript.set_fadeInDuration((int)t); // TODO: Remove cast when RS supports exporting longs } public void setCardCreationFadeDuration(long t) { mScript.set_cardCreationFadeDuration((int)t); } private Element elementForBitmap(Bitmap bitmap, Bitmap.Config defaultConfig) { Bitmap.Config config = bitmap.getConfig(); if (config == null) { config = defaultConfig; } if (config == Bitmap.Config.ALPHA_8) { return A_8(mRS); } else if (config == Bitmap.Config.RGB_565) { return RGB_565(mRS); } else if (config == Bitmap.Config.ARGB_4444) { return RGBA_4444(mRS); } else if (config == Bitmap.Config.ARGB_8888) { return RGBA_8888(mRS); } else { throw new IllegalArgumentException("Unknown configuration"); } } public Mesh loadGeometry(int resId) { if (resId == 0) { return null; } FileA3D model = FileA3D.createFromResource(mRS, mRes, resId); if (model == null) { return null; } FileA3D.IndexEntry entry = model.getIndexEntry(0); if(entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) { return null; } return (Mesh) entry.getObject(); } }