blob: bb1469295507b8363bab7a649bce1b1c210b670a [file] [log] [blame] [edit]
// Copyright 2024 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libec/fingerprint/fp_frame_utils.h"
#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <type_traits>
#include <vector>
#include <absl/strings/str_format.h>
#include <base/logging.h>
namespace ec {
namespace {
/**
* Performs ceiling division of two unsigned integers.
*
* This function calculates the ceiling division (rounded-up integer division)
* of two unsigned integer-like values.
*
* @tparam T The unsigned integer-like type (e.g., unsigned int, unsigned long).
* @param numerator The division numerator.
* @param denominator The division denominator.
* @return The ceiling numerator/denominator.
*/
template <typename T>
constexpr T DivideCeiling(T numerator, T denominator) {
static_assert(std::is_unsigned_v<T>,
"DivideCeiling requires unsigned integer-like types.");
return (numerator + denominator - 1) / denominator;
}
} // namespace
std::optional<std::string> FpFrameBufferToPGM(
const std::vector<std::uint8_t>& buffer, FpFrameBufferToPGMBufferDim dims) {
if (dims.width == 0 || dims.height == 0) {
LOG(ERROR) << "The width and/or height are 0.";
return std::nullopt;
}
// The max supported PGM pixel depth is 16 bit.
if (dims.bits_per_pixel < 1 || dims.bits_per_pixel > 16) {
LOG(ERROR) << "Invalid bits per pixel " << dims.bits_per_pixel;
return std::nullopt;
}
std::size_t bytes_per_pixel =
DivideCeiling(dims.bits_per_pixel, static_cast<std::size_t>(8u));
std::size_t expected_buffer_size = dims.width * dims.height * bytes_per_pixel;
if (buffer.size() != expected_buffer_size) {
LOG(ERROR) << "Buffer size mismatch. Expected: " << expected_buffer_size
<< ", Actual: " << buffer.size();
return std::nullopt;
}
std::string pgm;
auto it = buffer.begin();
// Write the graymap PGM ASCII header.
pgm += "P2\n";
pgm += absl::StrFormat("# Sensor is %zux%zx %zubpp\n", dims.width,
dims.height, dims.bits_per_pixel);
pgm += absl::StrFormat("%zu %zx\n", dims.width, dims.height);
pgm += "# Max Value:\n";
std::size_t pixel_max_value = (1u << dims.bits_per_pixel) - 1;
assert(0 < pixel_max_value && pixel_max_value < 65536);
pgm += absl::StrFormat("%zu\n", pixel_max_value);
pgm += "# Pixels:\n";
for (std::size_t y = 0; y < dims.height; ++y) {
for (std::size_t x = 0; x < dims.width; ++x) {
uint16_t pixel = *it++;
if (bytes_per_pixel == 2) {
uint16_t pixel_msb = *it++;
pixel |= pixel_msb << 8;
}
const char* space_sep = x > 0 ? " " : "";
pgm += absl::StrFormat("%s%u", space_sep, pixel);
}
pgm += "\n";
}
pgm += "# END OF FILE\n";
return pgm;
}
} // namespace ec