1 /* 2 * Copyright 2016 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 //#define LOG_NDEBUG 0 17 #define LOG_TAG "HdrPlusClientUtils" 18 #include <log/log.h> 19 20 #include <fstream> 21 #include <inttypes.h> 22 #include <system/graphics.h> 23 24 #include "HdrPlusClientUtils.h" 25 26 namespace android { 27 namespace hdrplus_client_utils { 28 29 // Get the RGB values of the pixel at (x, y). 30 static status_t getRgb(uint8_t *r, uint8_t *g, uint8_t* b, uint32_t x, uint32_t y, 31 const pbcamera::StreamConfiguration &streamConfig, 32 const pbcamera::StreamBuffer &buffer) { 33 switch (streamConfig.image.format) { 34 case HAL_PIXEL_FORMAT_YCrCb_420_SP: 35 { 36 // Check the stream configuration has two planes. 37 if (streamConfig.image.planes.size() != 2) { 38 ALOGE("%s: NV21 should have 2 planes but it has %zu", __FUNCTION__, 39 streamConfig.image.planes.size()); 40 return BAD_VALUE; 41 } 42 43 // Find the indices of Y, V, and U in the buffer. 44 uint32_t yIndex = y * streamConfig.image.planes[0].stride + x; 45 uint32_t vIndex = streamConfig.image.planes[0].scanline * 46 streamConfig.image.planes[0].stride + 47 (y / 2) * streamConfig.image.planes[1].stride + (x & ~0x1); 48 uint32_t uIndex = vIndex + 1; 49 50 // Convert YUV to RGB. 51 int32_t yc = ((uint8_t*)buffer.data)[yIndex]; 52 int32_t vc = ((uint8_t*)buffer.data)[vIndex] - 128; 53 int32_t uc = ((uint8_t*)buffer.data)[uIndex] - 128; 54 *r = std::min(std::max(yc + 0.003036f * uc + 1.399457f * vc, 0.0f), 255.0f); 55 *g = std::min(std::max(yc - 0.344228f * uc - 0.717202f * vc, 0.0f), 255.0f); 56 *b = std::min(std::max(yc + 1.772431f * uc - 0.006137f * vc, 0.0f), 255.0f); 57 return OK; 58 } 59 case HAL_PIXEL_FORMAT_RGB_888: 60 { 61 // Check the stream configuration has 1 plane. 62 if (streamConfig.image.planes.size() != 1) { 63 ALOGE("%s: RGB_888 should have 1 plane but it has %zu", __FUNCTION__, 64 streamConfig.image.planes.size()); 65 return BAD_VALUE; 66 } 67 68 uint32_t offset = y * streamConfig.image.planes[0].stride + x * 3; 69 *r = ((uint8_t*)buffer.data)[offset]; 70 *g = ((uint8_t*)buffer.data)[offset + 1]; 71 *b = ((uint8_t*)buffer.data)[offset + 2]; 72 return OK; 73 } 74 default: 75 ALOGE("%s: Format %d is not supported.", __FUNCTION__, streamConfig.image.format); 76 return BAD_VALUE; 77 } 78 } 79 80 status_t writePpm(const std::string &filename, const pbcamera::StreamConfiguration &streamConfig, 81 const pbcamera::StreamBuffer &buffer) { 82 if (streamConfig.image.format != HAL_PIXEL_FORMAT_YCrCb_420_SP && 83 streamConfig.image.format != HAL_PIXEL_FORMAT_RGB_888) { 84 ALOGE("%s: format 0x%x is not supported.", __FUNCTION__, streamConfig.image.format); 85 return BAD_VALUE; 86 } 87 88 std::ofstream outfile(filename, std::ios::binary); 89 if (!outfile.is_open()) { 90 ALOGE("%s: Opening file (%s) failed.", __FUNCTION__, filename.data()); 91 return NO_INIT; 92 } 93 94 uint32_t width = streamConfig.image.width; 95 uint32_t height = streamConfig.image.height; 96 97 // Write headers of the ppm file. 98 outfile << "P6"; 99 outfile << " " << std::to_string(width) << " " << std::to_string(height) << " 255 "; 100 101 // Write RGB values of the image. 102 uint8_t r, g, b; 103 for (uint32_t y = 0; y < height; y++) { 104 for (uint32_t x = 0; x < width; x++) { 105 status_t res = getRgb(&r, &g, &b, x, y, streamConfig, buffer); 106 if (res != OK) { 107 ALOGE("%s: Getting RGB failed: %s (%d).", __FUNCTION__, strerror(-res), res); 108 return res; 109 } 110 outfile << r << g << b; 111 } 112 } 113 114 ALOGD("%s: Saved file: %s", __FUNCTION__, filename.data()); 115 116 outfile.close(); 117 return OK; 118 } 119 120 status_t comparePpm(const std::string &filename, const pbcamera::StreamConfiguration &streamConfig, 121 const pbcamera::StreamBuffer &buffer, float *diffRatio) { 122 if (streamConfig.image.format != HAL_PIXEL_FORMAT_YCrCb_420_SP) { 123 ALOGE("%s: format 0x%x is not supported.", __FUNCTION__, streamConfig.image.format); 124 return BAD_VALUE; 125 } 126 127 std::ifstream ifile(filename, std::ios::binary); 128 if (!ifile.is_open()) { 129 ALOGE("%s: Opening file (%s) failed.", __FUNCTION__, filename.data()); 130 return NO_INIT; 131 } 132 133 std::string s; 134 135 // Read headers of the ppm file. 136 ifile >> s; 137 if (s != "P6") { 138 ALOGE("%s: Invalid PPM file header: %s", __FUNCTION__, s.c_str()); 139 return BAD_VALUE; 140 } 141 142 // Read width and height. 143 ifile >> s; 144 uint32_t width = std::stoul(s); 145 146 ifile >> s; 147 uint32_t height = std::stoul(s); 148 149 if (width != streamConfig.image.width || height != streamConfig.image.height) { 150 ALOGE("%s: Image resolution doesn't match. image %dx%d ppm %dx%d", 151 __FUNCTION__, streamConfig.image.width, streamConfig.image.height, 152 width, height); 153 return BAD_VALUE; 154 } 155 156 ifile >> s; 157 if (s != "255") { 158 ALOGE("%s: Expecting 255 but got %s", __FUNCTION__, s.c_str()); 159 return BAD_VALUE; 160 } 161 162 char c; 163 164 // Get a space 165 ifile.get(c); 166 167 // Now the RGB values start. 168 uint8_t r, g, b; 169 uint64_t diff = 0; 170 171 for (uint32_t y = 0; y < height; y++) { 172 for (uint32_t x = 0; x < width; x++) { 173 status_t res = getRgb(&r, &g, &b, x, y, streamConfig, buffer); 174 if (res != OK) { 175 ALOGE("%s: Getting RGB failed: %s (%d).", __FUNCTION__, strerror(-res), res); 176 return res; 177 } 178 179 // Get r, g, b from golden image and accumulate the differences. 180 ifile.get(c); 181 diff += abs(static_cast<int32_t>(c) - r); 182 ifile.get(c); 183 diff += abs(static_cast<int32_t>(c) - g); 184 ifile.get(c); 185 diff += abs(static_cast<int32_t>(c) - b); 186 } 187 } 188 189 if (diffRatio != nullptr) { 190 *diffRatio = diff / (static_cast<float>(width) * height * 3 * 256); 191 } 192 193 return OK; 194 } 195 196 } // hdrplus_client_utils 197 } // namespace android 198