blob: b76c926ec51b2c943cb0a59d811c95ff530ffc79 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/camera/lib/raw_formats/raw_lookups.h"
#include <zircon/assert.h>
#include <unordered_set>
namespace camera::raw {
namespace {
// This recursive function goes and finds all the PixelPieces for the given pixel index as well as
// the offsets into the buffer of the bytes they describe bits within.
using PixelPieceOffsetList = std::vector<std::tuple<uint64_t, const PixelPiece&>>;
void find_offsets(const PackingBlock& block, uint32_t pixel_index, uint64_t initial_offset,
PixelPieceOffsetList& out) {
int32_t pixel_sum = 0;
uint64_t bits_offset = 0;
std::unordered_set<uint32_t> seen_pixels;
for (uint64_t i = 0; i < block.chunks().size(); ++i) {
const auto& chunk = block.chunks()[i];
if (chunk->type() == ChunkType::PIXEL_PIECE) {
const PixelPiece& piece = Chunk::get<PixelPiece>(chunk);
if (!seen_pixels.contains(piece.pixel_index())) {
++pixel_sum;
seen_pixels.insert(piece.pixel_index());
}
// Is this a piece of our pixel?
if (piece.pixel_index() == pixel_index) {
// The offset of this pixel into the buffer is going to be the offset in (whole) bytes
// into this block, plus the offset in bytes of the block itself (initial_offset).
uint64_t offset = initial_offset + (bits_offset / 8);
out.emplace_back(offset, piece);
}
bits_offset += piece.num_bits();
} else if (chunk->type() == ChunkType::PADDING) {
// Just some padding, move along.
const Padding& pad = Chunk::get<Padding>(chunk);
ZX_ASSERT_MSG(pad.repeat().type == ChunkRepeatType::FINITE,
"Non FINITE repeat chunk type found in PackingBlock.");
bits_offset += pad.num_bits();
} else if (chunk->type() == ChunkType::PACKING_BLOCK) {
const PackingBlock& sub_block = Chunk::get<PackingBlock>(chunk);
ZX_ASSERT_MSG(sub_block.repeat().type == ChunkRepeatType::FINITE,
"Non FINITE repeat chunk type found in PackingBlock.");
ZX_ASSERT_MSG(bits_offset % 8 == 0, "Saw sub block that isn't byte aligned.");
// First check if the pixel we're looking for is anywhere within this block/its repeats.
// If not, increment the pixel and bit sums and move on.
uint32_t num_pixels = sub_block.repeat().times * sub_block.num_pixels();
if (pixel_index >= num_pixels) {
pixel_sum += num_pixels;
bits_offset += sub_block.repeat().times * sub_block.num_bits();
continue;
}
// Ok, the pixel is somewhere within the repeats of this block. Figure out which one and
// prepare to recurse. The block index is also equal to the number of blocks which come
/// before the block we're about to recurse on.
uint32_t block_index = pixel_index / sub_block.num_pixels();
// We add the number of pixels in all the blocks before the block we will recurse on to the
// pixel_sum before we use it to calculate the pixel index in the sub-block.
pixel_sum += block_index * sub_block.num_pixels();
// The pixel index in the sub block will be the index in this block minus the number of
// pixels that came before it in this block.
uint32_t sub_pixel_index = pixel_index - pixel_sum;
// Update bits_offset with the size of all the blocks before the block we will recurse on.
bits_offset += block_index * sub_block.num_bits();
// We pass the offset to the sub block as the initial offset in the recursive call.
uint64_t sub_block_offset = initial_offset + (bits_offset / 8);
// Make the recursive call and return.
find_offsets(sub_block, sub_pixel_index, sub_block_offset, out);
return;
}
}
}
} // namespace
uint64_t GetPixel(const RawFormatInstance& format_instance, uint32_t pixel_index,
const uint8_t* buffer, size_t buffer_size) {
ZX_ASSERT_MSG(buffer, "GetPixel given null buffer.");
ZX_ASSERT_MSG(pixel_index < (format_instance.width * format_instance.height),
"pixel_index out of bounds.");
const PackingBlock& packing = format_instance.packing_block;
// Figure out which of the top level block repetitions our pixel must be in and call find_offsets.
uint32_t block_index = pixel_index / packing.num_pixels();
uint32_t sub_pixel_index = pixel_index - (block_index * packing.num_pixels());
uint64_t sub_block_offset = (block_index * packing.num_bits()) / 8;
PixelPieceOffsetList offsets;
find_offsets(packing, sub_pixel_index, sub_block_offset, offsets);
uint64_t out = 0;
for (const auto& [offset, piece] : offsets) {
ZX_ASSERT_MSG(offset < buffer_size, "Pixel offset calculated to be greater than buffer size.");
uint64_t piece_data;
int8_t shift = piece.shift();
if (shift >= 0) {
piece_data = (buffer[offset] & piece.mask()) << shift;
} else {
piece_data = (buffer[offset] & piece.mask()) >> -shift;
}
out |= piece_data;
}
return out;
}
void SetPixel(const RawFormatInstance& format_instance, uint32_t pixel_index, uint64_t pixel_value,
uint8_t* buffer, size_t buffer_size) {
ZX_ASSERT_MSG(buffer, "SetPixel given null buffer.");
ZX_ASSERT_MSG(pixel_index < (format_instance.width * format_instance.height),
"pixel_index out of bounds.");
const PackingBlock& packing = format_instance.packing_block;
// Figure out which of the top level block repetitions our pixel must be in and call find_offsets.
uint32_t block_index = pixel_index / packing.num_pixels();
uint32_t sub_pixel_index = pixel_index - (block_index * packing.num_pixels());
uint64_t sub_block_offset = (block_index * packing.num_bits()) / 8;
PixelPieceOffsetList offsets;
find_offsets(packing, sub_pixel_index, sub_block_offset, offsets);
for (const auto& [offset, piece] : offsets) {
ZX_ASSERT_MSG(offset < buffer_size, "Pixel offset calculated to be greater than buffer size.");
uint8_t byte_value = buffer[offset];
uint64_t mask = piece.mask();
int8_t shift = piece.shift();
// Clear the part of the byte we're about to overwrite.
byte_value &= ~mask;
// Get the part of the new pixel value we're going to OR in.
uint64_t new_byte_segment;
if (shift >= 0) {
mask <<= shift;
new_byte_segment = (pixel_value & mask) >> shift;
} else {
shift = -shift;
mask >>= shift;
new_byte_segment = (pixel_value & mask) << shift;
}
byte_value |= static_cast<uint8_t>(new_byte_segment);
buffer[offset] = byte_value;
}
}
} // namespace camera::raw