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.test.hwuicompare;
18
19import com.android.test.hwuicompare.R;
20import com.android.test.hwuicompare.ScriptC_errorCalculator;
21
22import android.content.Context;
23import android.content.res.Resources;
24import android.graphics.Bitmap;
25import android.graphics.Color;
26import android.renderscript.Allocation;
27import android.renderscript.Element;
28import android.renderscript.RenderScript;
29import android.util.Log;
30
31public class ErrorCalculator {
32    private static final String LOG_TAG = "ErrorCalculator";
33    private static final int REGION_SIZE = 8;
34
35    private static final boolean LOG_TIMING = false;
36    private static final boolean LOG_CALC = false;
37
38    private final RenderScript mRS;
39    private Allocation mIdealPixelsAllocation;
40    private Allocation mGivenPixelsAllocation;
41    private Allocation mOutputPixelsAllocation;
42
43    private final Allocation mInputRowsAllocation;
44    private final Allocation mOutputRegionsAllocation;
45
46    private final ScriptC_errorCalculator mScript;
47
48    private final int[] mOutputRowRegions;
49
50    public ErrorCalculator(Context c, Resources resources) {
51        int width = resources.getDimensionPixelSize(R.dimen.layer_width);
52        int height = resources.getDimensionPixelSize(R.dimen.layer_height);
53        mOutputRowRegions = new int[height / REGION_SIZE];
54
55        mRS = RenderScript.create(c);
56        int[] rowIndices = new int[height / REGION_SIZE];
57        for (int i = 0; i < rowIndices.length; i++)
58            rowIndices[i] = i * REGION_SIZE;
59
60        mScript = new ScriptC_errorCalculator(mRS, resources, R.raw.errorcalculator);
61        mScript.set_HEIGHT(height);
62        mScript.set_WIDTH(width);
63        mScript.set_REGION_SIZE(REGION_SIZE);
64
65        mInputRowsAllocation = Allocation.createSized(mRS, Element.I32(mRS), rowIndices.length,
66                Allocation.USAGE_SCRIPT);
67        mInputRowsAllocation.copyFrom(rowIndices);
68        mOutputRegionsAllocation = Allocation.createSized(mRS, Element.I32(mRS),
69                mOutputRowRegions.length, Allocation.USAGE_SCRIPT);
70    }
71
72
73    private static long startMillis, middleMillis;
74
75    public float calcErrorRS(Bitmap ideal, Bitmap given) {
76        if (LOG_TIMING) {
77            startMillis = System.currentTimeMillis();
78        }
79
80        mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
81                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
82        mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
83                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
84
85        mScript.bind_ideal(mIdealPixelsAllocation);
86        mScript.bind_given(mGivenPixelsAllocation);
87
88        mScript.forEach_countInterestingRegions(mInputRowsAllocation, mOutputRegionsAllocation);
89        mOutputRegionsAllocation.copyTo(mOutputRowRegions);
90
91        int regionCount = 0;
92        for (int region : mOutputRowRegions) {
93            regionCount += region;
94        }
95        int interestingPixels = Math.max(1, regionCount) * REGION_SIZE * REGION_SIZE;
96
97        if (LOG_TIMING) {
98            long startMillis2 = System.currentTimeMillis();
99        }
100
101        mScript.forEach_accumulateError(mInputRowsAllocation, mOutputRegionsAllocation);
102        mOutputRegionsAllocation.copyTo(mOutputRowRegions);
103        float totalError = 0;
104        for (int row : mOutputRowRegions) {
105            totalError += row;
106        }
107        totalError /= 1024.0f;
108
109        if (LOG_TIMING) {
110            long finalMillis = System.currentTimeMillis();
111            Log.d(LOG_TAG, "rs: first part took " + (middleMillis - startMillis) + "ms");
112            Log.d(LOG_TAG, "rs: last part took " + (finalMillis - middleMillis) + "ms");
113        }
114        if (LOG_CALC) {
115            Log.d(LOG_TAG, "rs: error " + totalError + ", pixels " + interestingPixels);
116        }
117        return totalError / interestingPixels;
118    }
119
120    public void calcErrorHeatmapRS(Bitmap ideal, Bitmap given, Bitmap output) {
121        mIdealPixelsAllocation = Allocation.createFromBitmap(mRS, ideal,
122                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
123        mGivenPixelsAllocation = Allocation.createFromBitmap(mRS, given,
124                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
125
126        mScript.bind_ideal(mIdealPixelsAllocation);
127        mScript.bind_given(mGivenPixelsAllocation);
128
129        mOutputPixelsAllocation = Allocation.createFromBitmap(mRS, output,
130                Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
131        mScript.forEach_displayDifference(mOutputPixelsAllocation, mOutputPixelsAllocation);
132        mOutputPixelsAllocation.copyTo(output);
133    }
134
135    public static float calcError(Bitmap ideal, Bitmap given) {
136        if (LOG_TIMING) {
137            startMillis = System.currentTimeMillis();
138        }
139
140        int interestingRegions = 0;
141        for (int x = 0; x < ideal.getWidth(); x += REGION_SIZE) {
142            for (int y = 0; y < ideal.getWidth(); y += REGION_SIZE) {
143                if (inspectRegion(ideal, x, y)) {
144                    interestingRegions++;
145                }
146            }
147        }
148
149        int interestingPixels = Math.max(1, interestingRegions) * REGION_SIZE * REGION_SIZE;
150
151        if (LOG_TIMING) {
152            long startMillis2 = System.currentTimeMillis();
153        }
154
155        float totalError = 0;
156        for (int x = 0; x < ideal.getWidth(); x++) {
157            for (int y = 0; y < ideal.getHeight(); y++) {
158                int idealColor = ideal.getPixel(x, y);
159                int givenColor = given.getPixel(x, y);
160                if (idealColor == givenColor)
161                    continue;
162                totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor));
163                totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor));
164                totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor));
165                totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor));
166            }
167        }
168        totalError /= 1024.0f;
169        if (LOG_TIMING) {
170            long finalMillis = System.currentTimeMillis();
171            Log.d(LOG_TAG, "dvk: first part took " + (middleMillis - startMillis) + "ms");
172            Log.d(LOG_TAG, "dvk: last part took " + (finalMillis - middleMillis) + "ms");
173        }
174        if (LOG_CALC) {
175            Log.d(LOG_TAG, "dvk: error " + totalError + ", pixels " + interestingPixels);
176        }
177        return totalError / interestingPixels;
178    }
179
180    private static boolean inspectRegion(Bitmap ideal, int x, int y) {
181        int regionColor = ideal.getPixel(x, y);
182        for (int i = 0; i < REGION_SIZE; i++) {
183            for (int j = 0; j < REGION_SIZE; j++) {
184                if (ideal.getPixel(x + i, y + j) != regionColor)
185                    return true;
186            }
187        }
188        return false;
189    }
190}
191