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.magicsmoke;
18
19import static android.renderscript.Sampler.Value.LINEAR;
20import static android.renderscript.Sampler.Value.WRAP;
21
22import com.android.magicsmoke.R;
23
24import android.content.Context;
25import android.content.SharedPreferences;
26import android.content.SharedPreferences.Editor;
27import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
28import android.graphics.Bitmap;
29import android.graphics.BitmapFactory;
30import android.media.MediaPlayer;
31import android.os.Handler;
32import android.renderscript.*;
33import android.renderscript.Element.Builder;
34import android.renderscript.ProgramStore.BlendDstFunc;
35import android.renderscript.ProgramStore.BlendSrcFunc;
36import android.util.Log;
37import android.view.MotionEvent;
38import android.os.Bundle;
39
40import java.util.TimeZone;
41
42class MagicSmokeRS extends RenderScriptScene implements OnSharedPreferenceChangeListener {
43
44    static class WorldState {
45        public float mXOffset;
46        public float mYOffset;
47        public int   mPreset;
48        public int   mTextureMask;
49        public int   mRotate;
50        public int   mTextureSwap;
51        public int   mProcessTextureMode;
52        public int   mBackCol;
53        public int   mLowCol;
54        public int   mHighCol;
55        public float mAlphaMul;
56        public int   mPreMul;
57    }
58    WorldState mWorldState = new WorldState();
59    //private Type mStateType;
60    //private Allocation mState;
61
62    private ProgramStore mPStore;
63    private ProgramFragment mPF5tex;
64    private ProgramFragment mPF4tex;
65    private Sampler[] mSampler;
66    private Allocation[] mSourceTextures;
67    private Allocation[] mRealTextures;
68
69    private ScriptC_clouds mScript;
70
71    private ScriptField_VertexShaderConstants_s mVSConst;
72    private ScriptField_FragmentShaderConstants_s mFSConst;
73
74    private ProgramVertex mPV5tex;
75    private ProgramVertex mPV4tex;
76    private ProgramVertexFixedFunction.Constants mPVAlloc;
77
78    private static final int RSID_STATE = 0;
79    //private static final int RSID_PROGRAMVERTEX = 3;
80    private static final int RSID_NOISESRC1 = 1;
81    private static final int RSID_NOISESRC2 = 2;
82    private static final int RSID_NOISESRC3 = 3;
83    private static final int RSID_NOISESRC4 = 4;
84    private static final int RSID_NOISESRC5 = 5;
85    private static final int RSID_NOISEDST1 = 6;
86    private static final int RSID_NOISEDST2 = 7;
87    private static final int RSID_NOISEDST3 = 8;
88    private static final int RSID_NOISEDST4 = 9;
89    private static final int RSID_NOISEDST5 = 10;
90
91    private Context mContext;
92    private SharedPreferences mSharedPref;
93
94    static class Preset {
95        Preset(int processmode, int backcol, int locol, int hicol, float mul, int mask,
96                 boolean rot, boolean texswap, boolean premul) {
97            mProcessTextureMode = processmode;
98            mBackColor = backcol;
99            mLowColor = locol;
100            mHighColor = hicol;
101            mAlphaMul = mul;
102            mTextureMask = mask;
103            mRotate = rot;
104            mTextureSwap = texswap;
105            mPreMul = premul;
106        }
107        public int mProcessTextureMode;
108        public int mBackColor;
109        public int mLowColor;
110        public int mHighColor;
111        public float mAlphaMul;
112        public int mTextureMask;
113        public boolean mRotate;
114        public boolean mTextureSwap;
115        public boolean mPreMul;
116    }
117
118    public static final int DEFAULT_PRESET = 16;
119    public static final Preset [] mPreset = new Preset[] {
120        //       proc    back     low       high     alph  mask  rot    swap   premul
121        new Preset(1,  0x000000, 0x000000, 0xffffff, 2.0f, 0x0f, true,  false, false),
122        new Preset(1,  0x0000ff, 0x000000, 0xffffff, 2.0f, 0x0f, true,  false, false),
123        new Preset(1,  0x00ff00, 0x000000, 0xffffff, 2.0f, 0x0f, true,  false, false),
124        new Preset(1,  0x00ff00, 0x000000, 0xffffff, 2.0f, 0x0f, true,  false, true),
125        new Preset(1,  0x00ff00, 0x00ff00, 0xffffff, 2.5f, 0x1f, true,  true,  true),
126        new Preset(1,  0x800000, 0xff0000, 0xffffff, 2.5f, 0x1f, true,  true,  false),
127        new Preset(0,  0x000000, 0x000000, 0xffffff, 0.0f, 0x1f, true,  false, false),
128        new Preset(1,  0x0000ff, 0x00ff00, 0xffff00, 2.0f, 0x1f, true,  true,  false),
129        new Preset(1,  0x008000, 0x00ff00, 0xffffff, 2.5f, 0x1f, true,  true,  false),
130        new Preset(1,  0x800000, 0xff0000, 0xffffff, 2.5f, 0x1f, true,  true,  true),
131        new Preset(1,  0x808080, 0x000000, 0xffffff, 2.0f, 0x0f, true,  false, true),
132        new Preset(1,  0x0000ff, 0x000000, 0xffffff, 2.0f, 0x0f, true,  false, true),
133        new Preset(1,  0x0000ff, 0x00ff00, 0xffff00, 1.5f, 0x1f, false, false, true),
134        new Preset(1,  0x0000ff, 0x00ff00, 0xffff00, 2.0f, 0x1f, true,  true,  true),
135        new Preset(1,  0x0000ff, 0x00ff00, 0xffff00, 1.5f, 0x1f, true,  true,  true),
136        new Preset(1,  0x808080, 0x000000, 0xffffff, 2.0f, 0x0f, true,  false, false),
137        new Preset(1,  0x000000, 0x000000, 0xffffff, 2.0f, 0x0f, true,  true,  false),
138        new Preset(2,  0x000000, 0x000070, 0xff2020, 2.5f, 0x1f, true,  false, false),
139        new Preset(2,  0x6060ff, 0x000070, 0xffffff, 2.5f, 0x1f, true,  false, false),
140        new Preset(3,  0x0000f0, 0x000000, 0xffffff, 2.0f, 0x0f, true,  true,  false),
141    };
142
143    private float mTouchY;
144
145    MagicSmokeRS(Context context, int width, int height) {
146        super(width, height);
147        mWidth = width;
148        mHeight = height;
149        mContext = context;
150        mSharedPref = mContext.getSharedPreferences("magicsmoke", Context.MODE_PRIVATE);
151        mSharedPref.registerOnSharedPreferenceChangeListener(this);
152        makeNewState();
153    }
154
155    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
156
157        if (!mIsStarted) {
158            start();
159            mRS.finish();
160            stop(false);
161        } else {
162            makeNewState();
163        }
164    }
165
166    void makeNewState() {
167        int p = mSharedPref.getInt("preset", DEFAULT_PRESET);
168        if (p >= mPreset.length) {
169            p = 0;
170        }
171        mWorldState.mPreset = p;
172        mWorldState.mTextureMask = mPreset[p].mTextureMask;
173        mWorldState.mRotate = mPreset[p].mRotate ? 1 : 0;
174        mWorldState.mTextureSwap = mPreset[p].mTextureSwap ? 1 : 0;
175        mWorldState.mProcessTextureMode = mPreset[p].mProcessTextureMode;
176        mWorldState.mBackCol = mPreset[p].mBackColor;
177        mWorldState.mLowCol = mPreset[p].mLowColor;
178        mWorldState.mHighCol = mPreset[p].mHighColor;
179        mWorldState.mAlphaMul = mPreset[p].mAlphaMul;
180        mWorldState.mPreMul = mPreset[p].mPreMul ? 1 : 0;
181
182        if(mScript != null) {
183            mScript.set_gPreset(mWorldState.mPreset);
184            mScript.set_gTextureMask(mWorldState.mTextureMask);
185            mScript.set_gRotate(mWorldState.mRotate);
186            mScript.set_gTextureSwap(mWorldState.mTextureSwap);
187            mScript.set_gProcessTextureMode(mWorldState.mProcessTextureMode);
188            mScript.set_gBackCol(mWorldState.mBackCol);
189            mScript.set_gLowCol(mWorldState.mLowCol);
190            mScript.set_gHighCol(mWorldState.mHighCol);
191            mScript.set_gAlphaMul(mWorldState.mAlphaMul);
192            mScript.set_gPreMul(mWorldState.mPreMul);
193        }
194    }
195
196    @Override
197    public void resize(int width, int height) {
198        super.resize(width, height);
199        if (mPVAlloc != null) {
200            Matrix4f proj = new Matrix4f();
201            proj.loadProjectionNormalized(width, height);
202            mPVAlloc.setProjection(proj);
203        }
204    }
205
206    @Override
207    public Bundle onCommand(String action, int x, int y, int z, Bundle extras,
208            boolean resultRequested) {
209
210        if ("android.wallpaper.tap".equals(action)) {
211            mTouchY = y;
212        }
213        return null;
214    }
215
216    /*@Override
217    public void onTouchEvent(MotionEvent event) {
218        switch(event.getAction()) {
219            case MotionEvent.ACTION_DOWN:
220                mTouchY = event.getY();
221                break;
222            case MotionEvent.ACTION_MOVE:
223                float dy = event.getY() - mTouchY;
224                mTouchY += dy;
225                dy /= 20;
226                if (dy > 4) {
227                    dy = 4;
228                } else if (dy < -4) {
229                    dy = -4;
230                }
231                //mState.data(mWorldState);
232        }
233    }*/
234
235    @Override
236    public void setOffset(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) {
237        // update our state, then push it to the renderscript
238        mWorldState.mXOffset = xOffset;
239        mWorldState.mYOffset = yOffset;
240        mScript.set_gXOffset(mWorldState.mXOffset);
241        mScript.set_gYOffset(mWorldState.mYOffset);
242    }
243
244    @Override
245    public void stop(boolean forReal) {
246        if (forReal) {
247            mSharedPref.unregisterOnSharedPreferenceChangeListener(this);
248        }
249        super.stop(forReal);
250    }
251
252    @Override
253    public void start() {
254        makeNewState();
255        super.start();
256    }
257
258    float alphafactor;
259    Type mTextureType;
260
261    void loadBitmap(int id, int index, String name, float alphamul, int lowcol, int highcol) {
262        BitmapFactory.Options opts = new BitmapFactory.Options();
263        opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
264        Bitmap in = BitmapFactory.decodeResource(mResources, id, opts);
265
266        // Bitmaps are stored in memory in premultiplied form. We want non-premultiplied,
267        // which is what getPixels gives us.
268        int pixels[] = new int[65536];
269        in.getPixels(pixels, 0, 256, 0, 0, 256, 256);
270        mRealTextures[index] = Allocation.createTyped(mRS, mTextureType,
271                                                      Allocation.MipmapControl.MIPMAP_NONE,
272                                                      Allocation.USAGE_SCRIPT |
273                                                      Allocation.USAGE_GRAPHICS_TEXTURE);
274        mSourceTextures[index] = Allocation.createTyped(mRS, mTextureType,
275                                                      Allocation.MipmapControl.MIPMAP_NONE,
276                                                      Allocation.USAGE_SCRIPT);
277
278        // copyFrom needs a byte[], not an int[], so we need to copy the data first
279        byte bpixels[] = new byte[65536*4];
280        for (int i = 0; i < 65536; i++) {
281            bpixels[i * 4 + 0] = (byte)(pixels[i] & 0xff);
282            bpixels[i * 4 + 1] = (byte)((pixels[i] >> 8) & 0xff);
283            bpixels[i * 4 + 2] = (byte)((pixels[i] >>16) & 0xff);
284            bpixels[i * 4 + 3] = (byte)((pixels[i] >> 24) & 0xff);
285        }
286        mSourceTextures[index].copyFrom(bpixels);
287        in.recycle();
288    }
289
290    void loadBitmaps() {
291        alphafactor = 1f;
292        float alphamul = mPreset[mWorldState.mPreset].mAlphaMul;
293        int lowcol = mPreset[mWorldState.mPreset].mLowColor;
294        int highcol = mPreset[mWorldState.mPreset].mHighColor;
295        //Log.i("@@@@", "preset " + mWorldState.mPreset + ", mul: " + alphamul +
296        //        ", colors: " + Integer.toHexString(lowcol) + "/" + Integer.toHexString(highcol));
297
298        // TODO: using different high and low colors for each layer offers some cool effects too
299        loadBitmap(R.drawable.noise1, 0, "Tnoise1", alphamul, lowcol, highcol);
300        loadBitmap(R.drawable.noise2, 1, "Tnoise2", alphamul, lowcol, highcol);
301        loadBitmap(R.drawable.noise3, 2, "Tnoise3", alphamul, lowcol, highcol);
302        loadBitmap(R.drawable.noise4, 3, "Tnoise4", alphamul, lowcol, highcol);
303        loadBitmap(R.drawable.noise5, 4, "Tnoise5", alphamul, lowcol, highcol);
304
305        mScript.set_gTnoise1(mRealTextures[0]);
306        mScript.set_gTnoise2(mRealTextures[1]);
307        mScript.set_gTnoise3(mRealTextures[2]);
308        mScript.set_gTnoise4(mRealTextures[3]);
309        mScript.set_gTnoise5(mRealTextures[4]);
310
311        mScript.bind_gNoisesrc1(mSourceTextures[0]);
312        mScript.bind_gNoisesrc2(mSourceTextures[1]);
313        mScript.bind_gNoisesrc3(mSourceTextures[2]);
314        mScript.bind_gNoisesrc4(mSourceTextures[3]);
315        mScript.bind_gNoisesrc5(mSourceTextures[4]);
316
317        mScript.bind_gNoisedst1(mRealTextures[0]);
318        mScript.bind_gNoisedst2(mRealTextures[1]);
319        mScript.bind_gNoisedst3(mRealTextures[2]);
320        mScript.bind_gNoisedst4(mRealTextures[3]);
321        mScript.bind_gNoisedst5(mRealTextures[4]);
322    }
323
324    @Override
325    protected ScriptC createScript() {
326
327        mScript = new ScriptC_clouds(mRS, mResources, R.raw.clouds);
328
329        mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1);
330        mScript.bind_gVSConstants(mVSConst);
331
332        {
333            ProgramVertex.Builder builder = new ProgramVertex.Builder(mRS);
334            builder.setShader(mResources, R.raw.pv5tex);
335            builder.addConstant(mVSConst.getAllocation().getType());
336            builder.addInput(ScriptField_VertexInputs_s.createElement(mRS));
337
338            mPV5tex = builder.create();
339            mPV5tex.bindConstants(mVSConst.getAllocation(), 0);
340
341            builder.setShader(mResources, R.raw.pv4tex);
342            mPV4tex = builder.create();
343            mPV4tex.bindConstants(mVSConst.getAllocation(), 0);
344        }
345        mScript.set_gPV5tex(mPV5tex);
346        mScript.set_gPV4tex(mPV4tex);
347
348        mSourceTextures = new Allocation[5];
349        mRealTextures = new Allocation[5];
350
351        Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
352        tb.setX(256);
353        tb.setY(256);
354        mTextureType = tb.create();
355        loadBitmaps();
356
357        Sampler.Builder samplerBuilder = new Sampler.Builder(mRS);
358        samplerBuilder.setMinification(LINEAR);
359        samplerBuilder.setMagnification(LINEAR);
360        samplerBuilder.setWrapS(WRAP);
361        samplerBuilder.setWrapT(WRAP);
362        mSampler = new Sampler[5];
363        for (int i = 0; i < 5; i++)
364            mSampler[i] = samplerBuilder.create();
365
366        {
367            mFSConst = new ScriptField_FragmentShaderConstants_s(mRS, 1);
368            mScript.bind_gFSConstants(mFSConst);
369
370            ProgramFragment.Builder builder = new ProgramFragment.Builder(mRS);
371            builder.setShader(mResources, R.raw.pf5tex);
372            for (int texCount = 0; texCount < 5; texCount ++) {
373                builder.addTexture(Program.TextureType.TEXTURE_2D);
374            }
375            builder.addConstant(mFSConst.getAllocation().getType());
376
377            mPF5tex = builder.create();
378            for (int i = 0; i < 5; i++)
379                mPF5tex.bindSampler(mSampler[i], i);
380            mPF5tex.bindConstants(mFSConst.getAllocation(), 0);
381
382            builder = new ProgramFragment.Builder(mRS);
383            builder.setShader(mResources, R.raw.pf4tex);
384            for (int texCount = 0; texCount < 4; texCount ++) {
385                builder.addTexture(Program.TextureType.TEXTURE_2D);
386            }
387            builder.addConstant(mFSConst.getAllocation().getType());
388            mPF4tex = builder.create();
389            for (int i = 0; i < 4; i++)
390                mPF4tex.bindSampler(mSampler[i], i);
391            mPF4tex.bindConstants(mFSConst.getAllocation(), 0);
392        }
393
394        mScript.set_gPF5tex(mPF5tex);
395        mScript.set_gPF4tex(mPF4tex);
396
397
398        {
399            ProgramStore.Builder builder = new ProgramStore.Builder(mRS);
400            builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS);
401            builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO);
402            builder.setDitherEnabled(true); // without dithering there is severe banding
403            builder.setDepthMaskEnabled(false);
404            mPStore = builder.create();
405        }
406
407        mScript.set_gPStore(mPStore);
408
409        mScript.set_gPreset(mWorldState.mPreset);
410        mScript.set_gTextureMask(mWorldState.mTextureMask);
411        mScript.set_gRotate(mWorldState.mRotate);
412        mScript.set_gTextureSwap(mWorldState.mTextureSwap);
413        mScript.set_gProcessTextureMode(mWorldState.mProcessTextureMode);
414        mScript.set_gBackCol(mWorldState.mBackCol);
415        mScript.set_gLowCol(mWorldState.mLowCol);
416        mScript.set_gHighCol(mWorldState.mHighCol);
417        mScript.set_gAlphaMul(mWorldState.mAlphaMul);
418        mScript.set_gPreMul(mWorldState.mPreMul);
419        mScript.set_gXOffset(mWorldState.mXOffset);
420
421        return mScript;
422    }
423}
424