• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 #include "host/libs/graphics_detector/img.h"
18 
19 #include <fstream>
20 #include <ostream>
21 
22 #include <android-base/logging.h>
23 
24 namespace cuttlefish {
25 
26 // Loads:
27 //   rgba_pixels[0] = R for x:0 y:0
28 //   rgba_pixels[1] = G for x:0 y:0
29 //   rgba_pixels[2] = B for x:0 y:0
30 //   rgba_pixels[3] = A for x:0 y:0
LoadRGBAFromBitmapFile(const std::string & filename,uint32_t * out_w,uint32_t * out_h,std::vector<uint8_t> * out_pixels)31 void LoadRGBAFromBitmapFile(const std::string& filename, uint32_t* out_w,
32                             uint32_t* out_h, std::vector<uint8_t>* out_pixels) {
33   *out_w = 0;
34   *out_h = 0;
35   out_pixels->clear();
36 
37   std::ifstream bitmap(filename, std::ofstream::in | std::ofstream::binary);
38   if (!bitmap.is_open()) {
39     LOG(ERROR) << "Failed to open " << filename;
40     return;
41   }
42 
43   std::vector<char> bitmap_bytes((std::istreambuf_iterator<char>(bitmap)),
44                                  std::istreambuf_iterator<char>());
45 
46   if (bitmap_bytes[0] != 0x42) {
47     LOG(ERROR) << "Invalid bitmap file?";
48     return;
49   }
50   if (bitmap_bytes[1] != 0x4D) {
51     LOG(ERROR) << "Invalid bitmap file?";
52     return;
53   }
54 
55   auto ReadUint16AtByte = [&](const uint32_t offset) {
56     return *reinterpret_cast<uint16_t*>(&bitmap_bytes[offset]);
57   };
58   auto ReadUint32AtByte = [&](const uint32_t offset) {
59     return *reinterpret_cast<uint32_t*>(&bitmap_bytes[offset]);
60   };
61 
62   uint32_t w = ReadUint32AtByte(18);
63   uint32_t h = ReadUint32AtByte(22);
64   LOG(ERROR) << "Loading " << filename << " w:" << w << " h:" << h;
65 
66   uint32_t planes = ReadUint16AtByte(26);
67   if (planes != 1) {
68     LOG(ERROR) << "Unhandled number of planes: " << planes;
69     return;
70   }
71   uint32_t bits_per_pixel = ReadUint16AtByte(28);
72   if (bits_per_pixel != 32) {
73     LOG(ERROR) << "Unhandled number of bpp: " << bits_per_pixel;
74     return;
75   }
76 
77   uint32_t r_channel_mask = ReadUint32AtByte(54);
78   uint32_t g_channel_mask = ReadUint32AtByte(58);
79   uint32_t b_channel_mask = ReadUint32AtByte(62);
80   uint32_t a_channel_mask = ReadUint32AtByte(66);
81 
82   /*
83   LOG(ERROR) << " r_channel_mask:" << r_channel_mask
84              << " g_channel_mask:" << g_channel_mask
85              << " b_channel_mask:" << b_channel_mask
86              << " a_channel_mask:" << a_channel_mask;
87   */
88 
89   *out_w = w;
90   *out_h = h;
91   out_pixels->clear();
92   out_pixels->reserve(w * h * 4);
93 
94   uint32_t bitmap_headers_size = ReadUint32AtByte(10);
95   uint32_t bitmap_pixels_offset = bitmap_headers_size;
96 
97   auto GetChannel = [](uint32_t pixel, uint32_t channel_mask) {
98     if (channel_mask == 0) {
99       return static_cast<uint8_t>(0xFF);
100     } else if (channel_mask == 0x000000FF) {
101       return static_cast<uint8_t>((pixel & channel_mask) >> 0);
102     } else if (channel_mask == 0x0000FF00) {
103       return static_cast<uint8_t>((pixel & channel_mask) >> 8);
104     } else if (channel_mask == 0x00FF0000) {
105       return static_cast<uint8_t>((pixel & channel_mask) >> 16);
106     } else if (channel_mask == 0xFF000000) {
107       return static_cast<uint8_t>((pixel & channel_mask) >> 24);
108     } else {
109       LOG(FATAL) << "Unhandled channel mask: " << channel_mask;
110       return static_cast<uint8_t>(0);
111     }
112   };
113 
114   for (uint32_t y = 0; y < h; y++) {
115     uint32_t flipped_y = h - y - 1;
116     for (uint32_t x = 0; x < w; x++) {
117       uint32_t pixel_offset = (flipped_y * w * 4) + (x * 4);
118       uint32_t pixel = ReadUint32AtByte(bitmap_pixels_offset + pixel_offset);
119 
120       uint8_t r = GetChannel(pixel, r_channel_mask);
121       uint8_t g = GetChannel(pixel, g_channel_mask);
122       uint8_t b = GetChannel(pixel, b_channel_mask);
123       uint8_t a = GetChannel(pixel, a_channel_mask);
124 
125       out_pixels->push_back(r);
126       out_pixels->push_back(g);
127       out_pixels->push_back(b);
128       out_pixels->push_back(a);
129 
130 #if 0
131       LOG(ERROR) << " r_channel_mask:" << r_channel_mask
132                  << " g_channel_mask:" << g_channel_mask
133                  << " b_channel_mask:" << b_channel_mask
134                  << " a_channel_mask:" << a_channel_mask
135                  << " pixel:" << pixel;
136 #endif
137 #if 0
138       LOG(ERROR) << " x:" << x
139                  << " y:" << y
140                  << " r:" << (int)r
141                  << " g:" << (int)g
142                  << " b:" << (int)b
143                  << " a:" << (int)a;
144 #endif
145     }
146   }
147 }
148 
149 // Assumes:
150 //   rgba_pixels[0] = R for x:0 y:0
151 //   rgba_pixels[1] = G for x:0 y:0
152 //   rgba_pixels[2] = B for x:0 y:0
153 //   rgba_pixels[3] = A for x:0 y:0
SaveRGBAToBitmapFile(uint32_t w,uint32_t h,const uint8_t * rgba_pixels,const std::string & filename)154 void SaveRGBAToBitmapFile(uint32_t w, uint32_t h, const uint8_t* rgba_pixels,
155                           const std::string& filename) {
156   std::ofstream bitmap(filename, std::ofstream::out | std::ofstream::binary);
157   if (!bitmap.is_open()) {
158     LOG(ERROR) << "Failed to open " << filename;
159     return;
160   }
161 
162   static constexpr const uint32_t kBytesPerPixel = 4;
163   uint32_t bitmap_pixels_size = w * h * kBytesPerPixel;
164   uint32_t bitmap_header_size = 14;
165   uint32_t bitmap_dib_header_size = 108;
166   uint32_t bitmap_headers_size = bitmap_header_size + bitmap_dib_header_size;
167   uint32_t bitmap_file_size = bitmap_headers_size + bitmap_pixels_size;
168 
169   auto WriteAsBytes = [&](const auto& value) {
170     bitmap.write(reinterpret_cast<const char*>(&value), sizeof(value));
171   };
172   auto WriteCharAsBytes = [&](const char value) { WriteAsBytes(value); };
173   auto WriteUint16AsBytes = [&](const uint16_t value) { WriteAsBytes(value); };
174   auto WriteUint32AsBytes = [&](const uint32_t value) { WriteAsBytes(value); };
175 
176   WriteCharAsBytes(0x42);  // "B"
177   WriteCharAsBytes(0x4D);  // "M"
178   WriteUint32AsBytes(bitmap_file_size);
179   WriteCharAsBytes(0);                      // reserved 1
180   WriteCharAsBytes(0);                      // reserved 1
181   WriteCharAsBytes(0);                      // reserved 2
182   WriteCharAsBytes(0);                      // reserved 2
183   WriteUint32AsBytes(bitmap_headers_size);  // offset to actual pixel data
184   WriteUint32AsBytes(bitmap_dib_header_size);
185   WriteUint32AsBytes(w);
186   WriteUint32AsBytes(h);
187   WriteUint16AsBytes(1);                   // number of planes
188   WriteUint16AsBytes(32);                  // bits per pixel
189   WriteUint32AsBytes(0x03);                // compression/format
190   WriteUint32AsBytes(bitmap_pixels_size);  // image size
191   WriteUint32AsBytes(0);                   // horizontal print reset
192   WriteUint32AsBytes(0);                   // vertical print reset
193   WriteUint32AsBytes(0);                   // num_palette_colors
194   WriteUint32AsBytes(0);                   // num_important_colors
195   WriteUint32AsBytes(0x000000FF);          // red channel mask
196   WriteUint32AsBytes(0x0000FF00);          // green channel mask
197   WriteUint32AsBytes(0x00FF0000);          // blue channel mask
198   WriteUint32AsBytes(0xFF000000);          // alpha channel mask
199   WriteUint32AsBytes(0x206e6957);          // "win"
200   for (uint32_t i = 0; i < 36; i++) {
201     WriteCharAsBytes(0);
202   }                       // cie color space
203   WriteUint32AsBytes(0);  // "win"
204   WriteUint32AsBytes(0);  // "win"
205   WriteUint32AsBytes(0);  // "win"
206 
207   uint32_t stride_bytes = w * 4;
208   for (uint32_t current_y = 0; current_y < h; current_y++) {
209     uint32_t flipped_y = h - current_y - 1;
210 
211     const uint8_t* current_pixel = rgba_pixels + (stride_bytes * flipped_y);
212     for (uint32_t current_x = 0; current_x < w; current_x++) {
213       WriteAsBytes(*current_pixel);
214       ++current_pixel;
215       WriteAsBytes(*current_pixel);
216       ++current_pixel;
217       WriteAsBytes(*current_pixel);
218       ++current_pixel;
219       WriteAsBytes(*current_pixel);
220       ++current_pixel;
221     }
222   }
223 
224   bitmap.close();
225   LOG(INFO) << "Saved bitmap to " << filename;
226 }
227 
LoadYUV420FromBitmapFile(const std::string & filename,uint32_t * out_w,uint32_t * out_h,std::vector<uint8_t> * out_y,std::vector<uint8_t> * out_u,std::vector<uint8_t> * out_v)228 void LoadYUV420FromBitmapFile(const std::string& filename, uint32_t* out_w,
229                               uint32_t* out_h, std::vector<uint8_t>* out_y,
230                               std::vector<uint8_t>* out_u,
231                               std::vector<uint8_t>* out_v) {
232   std::vector<uint8_t> rgba;
233 
234   LoadRGBAFromBitmapFile(filename, out_w, out_h, &rgba);
235 
236   if (rgba.empty()) return;
237 
238   ConvertRGBA8888ToYUV420(*out_w, *out_h, rgba, out_y, out_u, out_v);
239 }
240 
FillWithColor(uint32_t width,uint32_t height,uint8_t red,uint8_t green,uint8_t blue,uint8_t alpha,std::vector<uint8_t> * out_pixels)241 void FillWithColor(uint32_t width, uint32_t height, uint8_t red, uint8_t green,
242                    uint8_t blue, uint8_t alpha,
243                    std::vector<uint8_t>* out_pixels) {
244   out_pixels->clear();
245   out_pixels->reserve(width * height * 4);
246   for (uint32_t y = 0; y < height; y++) {
247     for (uint32_t x = 0; x < width; x++) {
248       out_pixels->push_back(red);
249       out_pixels->push_back(green);
250       out_pixels->push_back(blue);
251       out_pixels->push_back(alpha);
252     }
253   }
254 }
255 
256 namespace {
257 
Clamp(int x)258 uint8_t Clamp(int x) {
259   if (x > 255) {
260     return 255;
261   }
262   if (x < 0) {
263     return 0;
264   }
265   return static_cast<uint8_t>(x);
266 }
267 
268 // BT.601 with "Studio Swing" / narrow range.
ConvertRGBA8888PixelToYUV(const uint8_t r,const uint8_t g,const uint8_t b,uint8_t * out_y,uint8_t * out_u,uint8_t * out_v)269 void ConvertRGBA8888PixelToYUV(const uint8_t r, const uint8_t g,
270                                const uint8_t b, uint8_t* out_y, uint8_t* out_u,
271                                uint8_t* out_v) {
272   const int r_int = static_cast<int>(r);
273   const int g_int = static_cast<int>(g);
274   const int b_int = static_cast<int>(b);
275   *out_y =
276       Clamp((((66 * r_int) + (129 * g_int) + (25 * b_int) + 128) >> 8) + 16);
277   *out_u =
278       Clamp((((-38 * r_int) - (74 * g_int) + (112 * b_int) + 128) >> 8) + 128);
279   *out_v =
280       Clamp((((112 * r_int) - (94 * g_int) - (18 * b_int) + 128) >> 8) + 128);
281 }
282 
283 }  // namespace
284 
ConvertRGBA8888ToYUV420(uint32_t w,uint32_t h,const std::vector<uint8_t> & rgba_pixels,std::vector<uint8_t> * y_pixels,std::vector<uint8_t> * u_pixels,std::vector<uint8_t> * v_pixels)285 void ConvertRGBA8888ToYUV420(uint32_t w, uint32_t h,
286                              const std::vector<uint8_t>& rgba_pixels,
287                              std::vector<uint8_t>* y_pixels,
288                              std::vector<uint8_t>* u_pixels,
289                              std::vector<uint8_t>* v_pixels) {
290   y_pixels->reserve(w * h);
291   u_pixels->reserve((w / 2) * (h / 2));
292   v_pixels->reserve((w / 2) * (h / 2));
293 
294   const auto* input = rgba_pixels.data();
295   for (uint32_t y = 0; y < h; y++) {
296     for (uint32_t x = 0; x < w; x++) {
297       const uint8_t r = *input;
298       ++input;
299       const uint8_t g = *input;
300       ++input;
301       const uint8_t b = *input;
302       ++input;
303       // const uint8_t a = *input;
304       ++input;
305 
306       uint8_t pixel_y;
307       uint8_t pixel_u;
308       uint8_t pixel_v;
309       ConvertRGBA8888PixelToYUV(r, g, b, &pixel_y, &pixel_u, &pixel_v);
310 
311       y_pixels->push_back(pixel_y);
312       if ((x % 2 == 0) && (y % 2 == 0)) {
313         u_pixels->push_back(pixel_u);
314         v_pixels->push_back(pixel_v);
315       }
316     }
317   }
318 }
319 
320 namespace {
321 
PixelsAreSimilar(uint32_t pixel1,uint32_t pixel2)322 bool PixelsAreSimilar(uint32_t pixel1, uint32_t pixel2) {
323   const uint8_t* pixel1_rgba = reinterpret_cast<const uint8_t*>(&pixel1);
324   const uint8_t* pixel2_rgba = reinterpret_cast<const uint8_t*>(&pixel2);
325 
326   constexpr const uint32_t kDefaultTolerance = 2;
327   for (uint32_t channel = 0; channel < 4; channel++) {
328     const uint8_t pixel1_channel = pixel1_rgba[channel];
329     const uint8_t pixel2_channel = pixel2_rgba[channel];
330     if ((std::max(pixel1_channel, pixel2_channel) -
331          std::min(pixel1_channel, pixel2_channel)) > kDefaultTolerance) {
332       return false;
333     }
334   }
335   return true;
336 }
337 
338 }  // namespace
339 
ImagesAreSimilar(uint32_t width,uint32_t height,const std::vector<uint8_t> & image1_rgba8888,const std::vector<uint8_t> & image2_rgba8888)340 bool ImagesAreSimilar(uint32_t width, uint32_t height,
341                       const std::vector<uint8_t>& image1_rgba8888,
342                       const std::vector<uint8_t>& image2_rgba8888) {
343   bool images_are_similar = true;
344 
345   uint32_t reported_incorrect_pixels = 0;
346   constexpr const uint32_t kMaxReportedIncorrectPixels = 10;
347 
348   const uint32_t* image1_pixels =
349       reinterpret_cast<const uint32_t*>(image1_rgba8888.data());
350   const uint32_t* image2_pixels =
351       reinterpret_cast<const uint32_t*>(image2_rgba8888.data());
352 
353   for (uint32_t y = 0; y < width; y++) {
354     for (uint32_t x = 0; x < height; x++) {
355       const uint32_t image1_pixel = image1_pixels[y * height + x];
356       const uint32_t image2_pixel = image2_pixels[y * height + x];
357       if (!PixelsAreSimilar(image1_pixel, image2_pixel)) {
358         images_are_similar = false;
359         if (reported_incorrect_pixels < kMaxReportedIncorrectPixels) {
360           reported_incorrect_pixels++;
361           const uint8_t* image1_pixel_rgba =
362               reinterpret_cast<const uint8_t*>(&image1_pixel);
363           const uint8_t* image2_pixel_rgba =
364               reinterpret_cast<const uint8_t*>(&image2_pixel);
365           LOG(ERROR) << "Pixel comparison failed at (" << x << ", " << y << ") "
366                      << " with "
367                      << " r:" << static_cast<int>(image1_pixel_rgba[0])
368                      << " g:" << static_cast<int>(image1_pixel_rgba[1])
369                      << " b:" << static_cast<int>(image1_pixel_rgba[2])
370                      << " a:" << static_cast<int>(image1_pixel_rgba[3])
371                      << " versus "
372                      << " r:" << static_cast<int>(image2_pixel_rgba[0])
373                      << " g:" << static_cast<int>(image2_pixel_rgba[1])
374                      << " b:" << static_cast<int>(image2_pixel_rgba[2])
375                      << " a:" << static_cast<int>(image2_pixel_rgba[3]);
376         }
377       }
378     }
379   }
380 
381   return images_are_similar;
382 }
383 
384 }  // namespace cuttlefish