1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.wallpaper.nexus;
18
19import static android.renderscript.Element.RGBA_8888;
20import static android.renderscript.Element.RGB_565;
21import static android.renderscript.ProgramStore.DepthFunc.ALWAYS;
22import static android.renderscript.Sampler.Value.LINEAR;
23import static android.renderscript.Sampler.Value.CLAMP;
24import static android.renderscript.Sampler.Value.WRAP;
25
26import com.android.wallpaper.R;
27import com.android.wallpaper.RenderScriptScene;
28
29import android.app.WallpaperManager;
30import android.content.res.Resources;
31import android.graphics.Bitmap;
32import android.graphics.BitmapFactory;
33import android.graphics.Rect;
34import android.os.Bundle;
35import android.renderscript.*;
36import android.renderscript.ProgramStore.BlendDstFunc;
37import android.renderscript.ProgramStore.BlendSrcFunc;
38import android.view.SurfaceHolder;
39
40import java.util.TimeZone;
41
42class NexusRS extends RenderScriptScene {
43    private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
44
45    private ProgramVertexFixedFunction.Constants mPvOrthoAlloc;
46
47    private int mInitialWidth;
48    private int mInitialHeight;
49    private float mWorldScaleX;
50    private float mWorldScaleY;
51    private float mXOffset;
52    private ScriptC_nexus mScript;
53
54    public NexusRS(int width, int height) {
55        super(width, height);
56
57        mInitialWidth = width;
58        mInitialHeight = height;
59        mWorldScaleX = 1.0f;
60        mWorldScaleY = 1.0f;
61
62        mOptionsARGB.inScaled = false;
63        mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888;
64    }
65
66    @Override
67    public void setOffset(float xOffset, float yOffset, int xPixels, int yPixels) {
68        mXOffset = xOffset;
69        mScript.set_gXOffset(xOffset);
70    }
71
72    @Override
73    public void start() {
74        super.start();
75    }
76
77    @Override
78    public void resize(int width, int height) {
79        super.resize(width, height); // updates mWidth, mHeight
80
81        // android.util.Log.d("NexusRS", String.format("resize(%d, %d)", width, height));
82
83        mWorldScaleX = (float)mInitialWidth / width;
84        mWorldScaleY = (float)mInitialHeight / height;
85        mScript.set_gWorldScaleX(mWorldScaleX);
86        mScript.set_gWorldScaleY(mWorldScaleY);
87    }
88
89    @Override
90    protected ScriptC createScript() {
91        mScript = new ScriptC_nexus(mRS, mResources, R.raw.nexus);
92
93        createProgramFragmentStore();
94        createProgramFragment();
95        createProgramVertex();
96        createState();
97
98        mScript.set_gTBackground(loadTexture(R.drawable.pyramid_background));
99        mScript.set_gTPulse(loadTextureARGB(R.drawable.pulse));
100        mScript.set_gTGlow(loadTextureARGB(R.drawable.glow));
101        mScript.setTimeZone(TimeZone.getDefault().getID());
102        mScript.invoke_initPulses();
103        return mScript;
104    }
105
106    private void createState() {
107        int mode;
108        try {
109            mode = mResources.getInteger(R.integer.nexus_mode);
110        } catch (Resources.NotFoundException exc) {
111            mode = 0; // standard nexus mode
112        }
113
114        mScript.set_gIsPreview(isPreview() ? 1 : 0);
115        mScript.set_gMode(mode);
116        mScript.set_gXOffset(0.f);
117        mScript.set_gWorldScaleX(mWorldScaleX);
118        mScript.set_gWorldScaleY(mWorldScaleY);
119    }
120
121    private Allocation loadTexture(int id) {
122        return Allocation.createFromBitmapResource(mRS, mResources, id,
123                                           Allocation.MipmapControl.MIPMAP_NONE,
124                                           Allocation.USAGE_GRAPHICS_TEXTURE);
125    }
126
127    private Allocation loadTextureARGB(int id) {
128        Bitmap b = BitmapFactory.decodeResource(mResources, id, mOptionsARGB);
129        return Allocation.createFromBitmap(mRS, b,
130                                           Allocation.MipmapControl.MIPMAP_NONE,
131                                           Allocation.USAGE_GRAPHICS_TEXTURE);
132    }
133
134
135    private void createProgramFragment() {
136        // sampler and program fragment for pulses
137        ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS);
138        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
139                           ProgramFragmentFixedFunction.Builder.Format.RGBA, 0);
140        ProgramFragment pft = builder.create();
141        pft.bindSampler(Sampler.WRAP_LINEAR(mRS), 0);
142        mScript.set_gPFTexture(pft);
143
144        // sampler and program fragment for background image
145        builder = new ProgramFragmentFixedFunction.Builder(mRS);
146        builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE,
147                           ProgramFragmentFixedFunction.Builder.Format.RGB, 0);
148        ProgramFragment pft565 = builder.create();
149        pft565.bindSampler(Sampler.CLAMP_NEAREST(mRS), 0);
150        mScript.set_gPFTexture565(pft565);
151    }
152
153    private void createProgramFragmentStore() {
154        ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
155        builder.setDepthFunc(ALWAYS);
156        builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE);
157        builder.setDitherEnabled(false);
158        ProgramStore solid = builder.create();
159        mRS.bindProgramStore(solid);
160
161        builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE);
162        mScript.set_gPSBlend(builder.create());
163    }
164
165    private void createProgramVertex() {
166        mPvOrthoAlloc = new ProgramVertexFixedFunction.Constants(mRS);
167        Matrix4f proj = new Matrix4f();
168        proj.loadOrthoWindow(mWidth, mHeight);
169        mPvOrthoAlloc.setProjection(proj);
170
171        ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS);
172        pvb.setTextureMatrixEnable(true);
173        ProgramVertex pv = pvb.create();
174        ((ProgramVertexFixedFunction)pv).bindConstants(mPvOrthoAlloc);
175        mRS.bindProgramVertex(pv);
176    }
177
178    @Override
179    public Bundle onCommand(String action, int x, int y, int z, Bundle extras,
180            boolean resultRequested) {
181
182        if (mWidth < mHeight) {
183            // nexus.rs ignores the xOffset when rotated; we shall endeavor to do so as well
184            x = (int) (x + mXOffset * mWidth / mWorldScaleX);
185        }
186
187        // android.util.Log.d("NexusRS", String.format(
188        //     "dw=%d, bw=%d, xOffset=%g, x=%d",
189        //     dw, bw, mWorldState.xOffset, x));
190
191        if (WallpaperManager.COMMAND_TAP.equals(action)
192                || WallpaperManager.COMMAND_SECONDARY_TAP.equals(action)
193                || WallpaperManager.COMMAND_DROP.equals(action)) {
194            mScript.invoke_addTap(x, y);
195        }
196        return null;
197    }
198}
199