1 /*
2  * Copyright (C) 2020 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 
17 package org.robolectric.shadows.testing.bitmapverifiers;
18 
19 import android.graphics.Color;
20 
21 public class BlurPixelVerifier extends BitmapVerifier {
22 
23   private final int dstColor;
24   private final int srcColor;
25 
26   /**
27    * Create a BitmapVerifier that compares pixel values relative to the provided source and
28    * destination colors. Pixels closer to the center of the test bitmap are expected to match closer
29    * to the source color, while pixels on the exterior of the test bitmap are expected to match the
30    * destination color more closely
31    */
BlurPixelVerifier(int srcColor, int dstColor)32   public BlurPixelVerifier(int srcColor, int dstColor) {
33     this.srcColor = srcColor;
34     this.dstColor = dstColor;
35   }
36 
37   @Override
verify(int[] bitmap, int offset, int stride, int width, int height)38   public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
39 
40     float dstRedChannel = Color.red(dstColor);
41     float dstGreenChannel = Color.green(dstColor);
42     float dstBlueChannel = Color.blue(dstColor);
43 
44     float srcRedChannel = Color.red(srcColor);
45     float srcGreenChannel = Color.green(srcColor);
46     float srcBlueChannel = Color.blue(srcColor);
47 
48     // Calculate the largest rgb color difference between the source and destination
49     // colors
50     double maxDifference =
51         Math.pow(srcRedChannel - dstRedChannel, 2.0f)
52             + Math.pow(srcGreenChannel - dstGreenChannel, 2.0f)
53             + Math.pow(srcBlueChannel - dstBlueChannel, 2.0f);
54 
55     // Calculate the maximum distance between pixels to the center of the test image
56     double maxPixelDistance = Math.sqrt(Math.pow(width / 2.0, 2.0) + Math.pow(height / 2.0, 2.0));
57 
58     // Additional tolerance applied to comparisons
59     float threshold = .05f;
60     for (int x = 0; x < width; x++) {
61       for (int y = 0; y < height; y++) {
62         double pixelDistance =
63             Math.sqrt(Math.pow(x - width / 2.0, 2.0) + Math.pow(y - height / 2.0, 2.0));
64         // Calculate the threshold of the destination color expected based on the
65         // pixels position relative to the center
66         double dstPercentage = pixelDistance / maxPixelDistance + threshold;
67 
68         int pixelColor = bitmap[indexFromXAndY(x, y, stride, offset)];
69         double pixelRedChannel = Color.red(pixelColor);
70         double pixelGreenChannel = Color.green(pixelColor);
71         double pixelBlueChannel = Color.blue(pixelColor);
72         // Compare the RGB color distance between the current pixel and the destination
73         // color
74         double dstDistance =
75             Math.sqrt(
76                 Math.pow(pixelRedChannel - dstRedChannel, 2.0)
77                     + Math.pow(pixelGreenChannel - dstGreenChannel, 2.0)
78                     + Math.pow(pixelBlueChannel - dstBlueChannel, 2.0));
79 
80         // Compare the RGB color distance between the current pixel and the source
81         // color
82         double srcDistance =
83             Math.sqrt(
84                 Math.pow(pixelRedChannel - srcRedChannel, 2.0)
85                     + Math.pow(pixelGreenChannel - srcGreenChannel, 2.0)
86                     + Math.pow(pixelBlueChannel - srcBlueChannel, 2.0));
87 
88         // calculate the ratio between the destination color to the current pixel
89         // color relative to the maximum distance between source and destination colors
90         // If this value exceeds the threshold expected for the pixel distance from
91         // center then we are rendering an unexpected color
92         double dstFraction = dstDistance / maxDifference;
93         if (dstFraction > dstPercentage) {
94           return false;
95         }
96 
97         // similarly compute the ratio between the source color to the current pixel
98         // color relative to the maximum distance between source and destination colors
99         // If this value exceeds the threshold expected for the pixel distance from
100         // center then we are rendering an unexpected source color
101         double srcFraction = srcDistance / maxDifference;
102         if (srcFraction > dstPercentage) {
103           return false;
104         }
105       }
106     }
107     return true;
108   }
109 }
110