blob: 24b71d68d57d6697114c23330723a04cbc0c9c73 [file] [edit]
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "ColorBarFrameSource"
#define ATRACE_TAG ATRACE_TAG_CAMERA
#include "ColorBarFrameSource.h"
#include <android/hardware/graphics/common/1.2/types.h>
#include <log/log.h>
#include <utils/Trace.h>
#include <algorithm>
#include <cmath>
#include "utils/HWLUtils.h"
namespace android {
namespace framesource {
ColorBarFrameSource::ColorBarFrameSource() {
InitializeColors();
}
void ColorBarFrameSource::InitializeColors() {
// Standard 100% Color Bars
// Order: White, Yellow, Cyan, Green, Magenta, Red, Blue, Black
struct {
uint8_t r, g, b;
} rgb_colors[kNumBars] = {
{255, 255, 255}, // White
{255, 255, 0}, // Yellow
{0, 255, 255}, // Cyan
{0, 255, 0}, // Green
{255, 0, 255}, // Magenta
{255, 0, 0}, // Red
{0, 0, 255}, // Blue
{0, 0, 0} // Black
};
for (size_t i = 0; i < kNumBars; ++i) {
colors_[i].r = rgb_colors[i].r;
colors_[i].g = rgb_colors[i].g;
colors_[i].b = rgb_colors[i].b;
colors_[i].y = static_cast<uint8_t>(
(0.299 * colors_[i].r + 0.587 * colors_[i].g + 0.114 * colors_[i].b));
colors_[i].cb = static_cast<uint8_t>(
128 +
(-0.1687 * colors_[i].r - 0.3313 * colors_[i].g + 0.5 * colors_[i].b));
colors_[i].cr =
static_cast<uint8_t>(128 + (0.5 * colors_[i].r - 0.4187 * colors_[i].g -
0.0813 * colors_[i].b));
}
}
status_t ColorBarFrameSource::ProduceFrame(uint32_t /*camera_id*/,
nsecs_t /*timestamp*/,
const SensorSettings& /*settings*/,
SensorBuffer* buffer,
const SensorBuffer* /*input_buffer*/) {
ATRACE_CALL();
if (buffer == nullptr) {
return BAD_VALUE;
}
switch (buffer->format) {
case PixelFormat::RGB_888:
DrawRGB(buffer->plane.img.img, buffer->width, buffer->height,
buffer->plane.img.stride_in_bytes, 3, false);
break;
case PixelFormat::RGBA_8888:
DrawRGB(buffer->plane.img.img, buffer->width, buffer->height,
buffer->plane.img.stride_in_bytes, 4, true);
break;
case PixelFormat::YCRCB_420_SP:
case PixelFormat::YCBCR_420_888: {
YUV420Frame output_frame;
output_frame.width = buffer->width;
output_frame.height = buffer->height;
output_frame.planes = buffer->plane.img_y_crcb;
DrawYUV420(output_frame);
break;
}
default:
ALOGE("%s: Unknown format %x", __FUNCTION__, buffer->format);
return BAD_VALUE;
}
return OK;
}
void ColorBarFrameSource::DrawYUV420(const YUV420Frame& frame) {
uint32_t width = frame.width;
uint32_t height = frame.height;
auto& planes = frame.planes;
for (uint32_t y = 0; y < height; ++y) {
uint8_t* row_y = planes.img_y + y * planes.y_stride;
uint8_t* row_cb = planes.img_cb + (y / 2) * planes.cbcr_stride;
uint8_t* row_cr = planes.img_cr + (y / 2) * planes.cbcr_stride;
for (uint32_t b = 0; b < kNumBars; ++b) {
uint32_t start_x = (b * width) / kNumBars;
uint32_t end_x = ((b + 1) * width) / kNumBars;
start_x &= ~1;
end_x &= ~1;
if (b == kNumBars - 1) end_x = width;
const auto& color = colors_[b];
uint16_t y_val = color.y;
uint16_t cb_val = color.cb;
uint16_t cr_val = color.cr;
for (uint32_t x = start_x; x < end_x; x += 2) {
row_y[x] = static_cast<uint8_t>(y_val);
if (x + 1 < width) {
row_y[x + 1] = static_cast<uint8_t>(y_val);
}
if ((y & 1) == 0) {
uint8_t* cb_ptr = row_cb + (x / 2) * planes.cbcr_step;
uint8_t* cr_ptr = row_cr + (x / 2) * planes.cbcr_step;
*cb_ptr = static_cast<uint8_t>(cb_val);
*cr_ptr = static_cast<uint8_t>(cr_val);
}
}
}
}
}
void ColorBarFrameSource::DrawRGB(uint8_t* img, uint32_t width, uint32_t height,
uint32_t stride, int bytes_per_pixel,
bool is_rgba) {
for (uint32_t y = 0; y < height; ++y) {
uint8_t* row = img + y * stride;
for (uint32_t bar_idx = 0; bar_idx < kNumBars; ++bar_idx) {
uint32_t start_x = (bar_idx * width) / kNumBars;
uint32_t end_x = ((bar_idx + 1) * width) / kNumBars;
if (bar_idx == kNumBars - 1) end_x = width;
const auto& c = colors_[bar_idx];
uint8_t r = c.r;
uint8_t g = c.g;
uint8_t b = c.b;
for (uint32_t x = start_x; x < end_x; ++x) {
uint8_t* px = row + x * bytes_per_pixel;
// Layout is typically R, G, B, [A]
px[0] = r;
px[1] = g;
px[2] = b;
if (is_rgba) {
px[3] = 0xFF; // Alpha opaque
}
}
}
}
}
void ColorBarFrameSource::CalculateAndAppendNoiseProfile(
float /*gain*/, float /*max_raw_value*/, HalCameraMetadata* result) {
// Use small epsilon to avoid divide-by-zero in downstream calculations
double noise_profile[8];
std::fill(std::begin(noise_profile), std::end(noise_profile), 1e-9);
result->Set(ANDROID_SENSOR_NOISE_PROFILE, noise_profile, 8);
}
} // namespace framesource
} // namespace android