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