1/*
2 * Copyright (C) 2012 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.rs.sgtest;
18
19import android.renderscript.Allocation;
20import android.renderscript.Element;
21import android.renderscript.RenderScript;
22import android.renderscript.Sampler;
23import android.renderscript.Script;
24import android.renderscript.Type;
25import android.renderscript.Matrix3f;
26import android.renderscript.Matrix4f;
27import android.renderscript.ScriptGroup;
28import android.util.Log;
29
30import java.lang.reflect.Constructor;
31import java.lang.Math;
32import java.util.HashMap;
33
34public class Filters extends TestBase {
35
36  interface FilterInterface {
37    public void init();
38    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b);
39    public Script.KernelID getKernelID();
40      public ScriptGroup.Closure asyncLaunch(ScriptGroup.Builder2 builder,
41                                              Object in, Type outputType);
42    public void forEach(Allocation in, Allocation out);
43  }
44
45    abstract class FilterBase implements FilterInterface {
46        public ScriptGroup.Closure asyncLaunch(ScriptGroup.Builder2 builder,
47                                                Object in, Type outputType) {
48            return builder.addKernel(getKernelID(), outputType, in);
49        }
50    }
51
52  /*
53
54    Template for a subclass that implements Filter.
55
56  class Filter implements Filter {
57    Filter(RenderScript RS) { s = new ScriptC_(RS); }
58
59    void init() {}
60
61    Script.KernelID getKernelID() { return s.getKernelID_(); }
62
63    void forEach(Allocation in, Allocation out) { s.forEach_(in, out); }
64
65    private ScriptC_ s;
66  }
67  */
68
69  class ColorMatrixFilter extends FilterBase {
70    public ColorMatrixFilter(RenderScript RS) { s_mat = new ScriptC_colormatrix_f(RS); }
71
72    public void init() { }
73
74    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) { /* TODO */ return null; }
75
76    public Script.KernelID getKernelID() { return s_mat.getKernelID_colormatrix(); }
77
78    public void forEach(Allocation in, Allocation out) { s_mat.forEach_colormatrix(in, out); }
79
80    private ScriptC_colormatrix_f s_mat;
81  }
82
83  class ContrastFilter extends FilterBase {
84    public ContrastFilter(RenderScript RS) { s = new ScriptC_contrast_f(RS); }
85
86    public void init() {}
87
88    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) { return null; }
89
90    public Script.KernelID getKernelID() { return s.getKernelID_contrast(); }
91
92    public void forEach(Allocation in, Allocation out) { s.forEach_contrast(in, out); }
93
94    private ScriptC_contrast_f s;
95  }
96
97  class ExposureFilter extends FilterBase {
98    public ExposureFilter(RenderScript RS) { s = new ScriptC_exposure_f(RS); }
99
100    public void init() {}
101
102    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) { return null; }
103
104    public Script.KernelID getKernelID() { return s.getKernelID_exposure(); }
105
106    public void forEach(Allocation in, Allocation out) { s.forEach_exposure(in, out); }
107
108    private ScriptC_exposure_f s;
109  }
110
111  class FisheyeFilter extends FilterBase {
112    public FisheyeFilter(RenderScript RS) {
113        mRS = RS;
114        s = new ScriptC_fisheye_approx_relaxed_f(RS);
115    }
116
117    public void init() {
118    }
119
120    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) {
121        return b.addInvoke(s.getInvokeID_init_filter(),
122                           dimX, dimY, 0.5f, 0.5f, 0.5f, Sampler.CLAMP_LINEAR(mRS));
123    }
124
125    public Script.KernelID getKernelID() { return s.getKernelID_fisheye(); }
126
127    public ScriptGroup.Closure asyncLaunch(ScriptGroup.Builder2 builder,
128                                            Object in, Type outputType) {
129        return builder.addKernel(getKernelID(), outputType,
130                                 new ScriptGroup.Binding(s.getFieldID_in_alloc(), in));
131    }
132
133    public void forEach(Allocation in, Allocation out) {
134        s.set_in_alloc(in);
135        s.forEach_fisheye(out);
136    }
137
138      private RenderScript mRS;
139      private ScriptC_fisheye_approx_relaxed_f s;
140      private final int dimX=1067, dimY=1600;
141  }
142
143  class GreyFilter extends FilterBase {
144    public GreyFilter(RenderScript RS) { s = new ScriptC_greyscale_f(RS); }
145
146    public void init() {}
147
148    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) { return null; }
149
150    public Script.KernelID getKernelID() { return s.getKernelID_greyscale(); }
151
152    public void forEach(Allocation in, Allocation out) { s.forEach_greyscale(in, out); }
153
154    private ScriptC_greyscale_f s;
155  }
156
157  class LevelsFilter extends FilterBase {
158    public LevelsFilter(RenderScript RS) { s = new ScriptC_levels_relaxed_f(RS); }
159    private final float mSaturation = 1.0f;
160    private final float mInBlack = 0.0f; // 0-255
161    private final float mOutBlack = 0.0f; // 0-255
162    private final float mInWhite = 255.0f; // 0-255
163    private final float mOutWhite = 255.0f; // 0-255
164    private final float mInWMinInB = mInWhite - mInBlack;
165    private final float mOutWMinOutB = mOutWhite - mOutBlack;
166    private final float mOverInWMinInB = 1.f / mInWMinInB;
167    private Matrix3f mSatMatrix;
168
169    private void setLevels() {
170      s.set_inBlack(mInBlack);
171      s.set_outBlack(mOutBlack);
172      s.set_inWMinInB(mInWMinInB);
173      s.set_outWMinOutB(mOutWMinOutB);
174      s.set_overInWMinInB(mOverInWMinInB);
175    }
176
177    private void setSaturation() {
178      Matrix3f satMatrix = new Matrix3f();
179      float rWeight = 0.299f;
180      float gWeight = 0.587f;
181      float bWeight = 0.114f;
182      float oneMinusS = 1.0f - mSaturation;
183
184      satMatrix.set(0, 0, oneMinusS * rWeight + mSaturation);
185      satMatrix.set(0, 1, oneMinusS * rWeight);
186      satMatrix.set(0, 2, oneMinusS * rWeight);
187      satMatrix.set(1, 0, oneMinusS * gWeight);
188      satMatrix.set(1, 1, oneMinusS * gWeight + mSaturation);
189      satMatrix.set(1, 2, oneMinusS * gWeight);
190      satMatrix.set(2, 0, oneMinusS * bWeight);
191      satMatrix.set(2, 1, oneMinusS * bWeight);
192      satMatrix.set(2, 2, oneMinusS * bWeight + mSaturation);
193      s.set_colorMat(satMatrix);
194
195      mSatMatrix = satMatrix;
196    }
197
198    public void init() {
199      setSaturation();
200      setLevels();
201    }
202
203    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) {
204        return b.addInvoke(s.getInvokeID_initialize(),
205                           mInBlack, mOutBlack, mInWMinInB, mOutWMinOutB,
206                           mOverInWMinInB, mSatMatrix);
207    }
208
209    public Script.KernelID getKernelID() { return s.getKernelID_levels_v4(); }
210
211    public void forEach(Allocation in, Allocation out) { s.forEach_levels_v4(in, out); }
212
213    private ScriptC_levels_relaxed_f s;
214  }
215
216  class ShadowsFilter extends FilterBase {
217    public ShadowsFilter(RenderScript RS) { s = new ScriptC_shadows_f(RS); }
218
219    public void init() { s.invoke_prepareShadows(50.f); }
220
221    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) {
222      cInit = b.addInvoke(s.getInvokeID_prepareShadows(), 50.f);
223      return cInit;
224    }
225
226    public Script.KernelID getKernelID() { return s.getKernelID_shadowsKernel(); }
227
228    public void forEach(Allocation in, Allocation out) { s.forEach_shadowsKernel(in, out); }
229
230    private ScriptC_shadows_f s;
231    private ScriptGroup.Closure cInit;
232  }
233
234  class VibranceFilter extends FilterBase {
235    public VibranceFilter(RenderScript RS) { s = new ScriptC_vibrance_f(RS); }
236
237    public void init() {}
238
239    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) { return null; }
240
241    public Script.KernelID getKernelID() { return s.getKernelID_vibranceKernel(); }
242
243    public void forEach(Allocation in, Allocation out) { s.forEach_vibranceKernel(in, out); }
244
245    private ScriptC_vibrance_f s;
246  }
247
248  class VignetteFilter extends FilterBase {
249    public VignetteFilter(RenderScript RS) { s = new ScriptC_vignette_approx_relaxed_f(RS); }
250    private final float center_x = 0.5f;
251    private final float center_y = 0.5f;
252    private final float scale = 0.5f;
253    private final float shade = 0.5f;
254    private final float slope = 20.0f;
255    private ScriptGroup.Closure cInit;
256
257    public void init() {
258      s.invoke_init_vignette(
259          mInPixelsAllocation.getType().getX(),
260          mInPixelsAllocation.getType().getY(), center_x,
261          center_y, scale, shade, slope);
262    }
263
264    public ScriptGroup.Closure prepInit(ScriptGroup.Builder2 b) {
265      cInit = b.addInvoke(s.getInvokeID_init_vignette(),
266            mInPixelsAllocation.getType().getX(),
267            mInPixelsAllocation.getType().getY(),
268            center_x,
269            center_y,
270            scale, shade, slope);
271      return cInit;
272    }
273
274    public Script.KernelID getKernelID() { return s.getKernelID_vignette(); }
275
276    public void forEach(Allocation in, Allocation out) { s.forEach_vignette(in, out); }
277
278    private ScriptC_vignette_approx_relaxed_f s;
279  }
280
281  public final static Class[] mFilterClasses = {
282    ColorMatrixFilter.class,
283    ContrastFilter.class,
284    ExposureFilter.class,
285    /* The fisheye filter uses rsSample, which does not work for float4 element
286       type.
287    FisheyeFilter.class,
288    */
289    GreyFilter.class,
290    LevelsFilter.class,
291    ShadowsFilter.class,
292    VibranceFilter.class,
293    VignetteFilter.class
294  };
295  private FilterInterface[] mFilters;
296  private int[] mIndices;
297
298  ScriptC_uc4tof4 s_uc2f;
299  ScriptC_f4touc4 s_f2uc;
300
301  private Allocation[] mScratchPixelsAllocation = new Allocation[2];
302  private ScriptGroup mGroup;
303  private ScriptGroup mGroup2;
304
305  private int mWidth;
306  private int mHeight;
307  private int mMode;
308
309  public static final int EMULATED = 0;
310  public static final int NATIVE2 = 1;
311  public static final int NATIVE1 = 2;
312  public static final int MANUAL = 3;
313
314  public Filters(int mode, int[] filter) {
315    mMode = mode;
316    mIndices = new int[filter.length];
317    System.arraycopy(filter, 0, mIndices, 0, filter.length);
318    mFilters = new FilterInterface[filter.length+2];
319  }
320
321  public void createTest(android.content.res.Resources res) {
322    s_uc2f = new ScriptC_uc4tof4(mRS);
323    s_f2uc = new ScriptC_f4touc4(mRS);
324    for (int i = 0; i < mIndices.length; i++) {
325      try {
326        /*
327        Constructor[] constructors = mFilterClasses[mIndices[i]].getConstructors();
328        for (Constructor ctr : constructors) {
329          Log.i("Filters", "constructor " + ctr);
330        }
331        */
332        Constructor constructor =
333            // mFilterClasses[i].getConstructor(new Class[]{ RenderScript.class });
334            //mFilterClasses[i].getConstructor(RenderScript.class);
335            mFilterClasses[mIndices[i]].getConstructors()[0];
336        try {
337          mFilters[i] = (FilterInterface)constructor.newInstance(this, mRS);
338        } catch (Exception e) {
339          Log.e("Filters", "newInstance caught " + e);
340          System.exit(-2);
341        }
342        mFilters[i].init();
343      } catch (Exception e) {
344        Log.e("Filters", "getConstructor caught " + e + " for " +
345              mFilterClasses[mIndices[i]].getName());
346        System.exit(-1);
347      }
348
349    }
350
351    mWidth = mInPixelsAllocation.getType().getX();
352    mHeight = mInPixelsAllocation.getType().getY();
353
354    Type.Builder tb = new Type.Builder(mRS, Element.F32_4(mRS));
355    tb.setX(mWidth);
356    tb.setY(mHeight);
357    Type connect = tb.create();
358
359    switch (mMode) {
360      case NATIVE1:
361          ScriptGroup.Builder b = new ScriptGroup.Builder(mRS);
362          b.addKernel(s_uc2f.getKernelID_uc4tof4());
363          b.addKernel(mFilters[0].getKernelID());
364          b.addConnection(connect, s_uc2f.getKernelID_uc4tof4(),
365              mFilters[0].getKernelID());
366
367          for (int i = 0; i < mIndices.length; i++) {
368            b.addKernel(mFilters[i].getKernelID());
369            b.addConnection(connect, mFilters[i-1].getKernelID(),
370                mFilters[i].getKernelID());
371          }
372
373          b.addKernel(s_f2uc.getKernelID_f4touc4());
374          b.addConnection(mOutPixelsAllocation.getType(),
375              mFilters[0].getKernelID(), s_f2uc.getKernelID_f4touc4());
376
377          mGroup = b.create();
378          break;
379      case NATIVE2: {
380        ScriptGroup.Builder2 b2 = new ScriptGroup.Builder2(mRS);
381
382        for (int i = 0; i < mIndices.length; i++) {
383          mFilters[i].prepInit(b2);
384        }
385
386        ScriptGroup.Input in = b2.addInput();
387
388        ScriptGroup.Closure c = b2.addKernel(s_uc2f.getKernelID_uc4tof4(),
389            connect, in);
390
391        for (int i = 0; i < mIndices.length; i++) {
392            c = mFilters[i].asyncLaunch(b2, c.getReturn(), connect);
393        }
394
395        c = b2.addKernel(s_f2uc.getKernelID_f4touc4(),
396            mOutPixelsAllocation.getType(),
397            c.getReturn());
398
399        final String name = mFilters[0].getClass().getSimpleName() + "-" +
400                mFilters[1].getClass().getSimpleName();
401        mGroup2 = b2.create(name, c.getReturn());
402      }
403        break;
404      case EMULATED:
405        mScratchPixelsAllocation[0] = Allocation.createTyped(mRS, connect);
406        mScratchPixelsAllocation[1] = Allocation.createTyped(mRS, connect);
407        break;
408    }
409  }
410
411    public void runTest() {
412        switch (mMode) {
413          case NATIVE1:
414            // mGroup.setInput(mFilters[0].getKernelID(), mInPixelsAllocation);
415            mGroup.setInput(s_uc2f.getKernelID_uc4tof4(), mInPixelsAllocation);
416            // mGroup.setOutput(mFilters[mIndices.length - 1].getKernelID(), mOutPixelsAllocation);
417            mGroup.setOutput(s_f2uc.getKernelID_f4touc4(), mOutPixelsAllocation);
418            mGroup.execute();
419            break;
420          case NATIVE2:
421            mOutPixelsAllocation = (Allocation)mGroup2.execute(mInPixelsAllocation)[0];
422            break;
423          case EMULATED:
424            s_uc2f.forEach_uc4tof4(mInPixelsAllocation, mScratchPixelsAllocation[0]);
425            for (int i = 0; i < mIndices.length; i++) {
426              mFilters[i].forEach(mScratchPixelsAllocation[i % 2],
427                  mScratchPixelsAllocation[(i+1) % 2]);
428            }
429            s_f2uc.forEach_f4touc4(mScratchPixelsAllocation[mIndices.length % 2],
430                mOutPixelsAllocation);
431            break;
432        }
433    }
434
435}
436