blob: 124c4621f5b974cdea54209c2406e60794c6d605 [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.h"
#include <zircon/assert.h>
#include <unordered_set>
namespace camera::raw {
RawFormatInstance CreateFormatInstance(const RawFormat& format, uint32_t width, uint32_t height,
std::optional<uint32_t> row_stride) {
ZX_ASSERT_MSG(format.packing_block.repeat().type == ChunkRepeatType::FILL_IMAGE,
"Top level PackingBlock repeat type must be FILL_IMAGE.");
std::function<PackingBlock(const PackingBlock&)> create_instance_packing;
create_instance_packing = [=, &create_instance_packing](const PackingBlock& block) {
uint32_t pixel_sum = 0;
uint64_t num_bits = 0;
std::unordered_set<uint32_t> seen_pixels;
PointerList<Chunk> instance_chunks(block.chunks().size());
for (uint64_t i = 0; i < block.chunks().size(); ++i) {
const Chunk* 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());
}
num_bits += piece.num_bits();
instance_chunks.emplace_back<PixelPiece>(piece);
} else if (chunk->type() == ChunkType::PADDING) {
const Padding& pad = Chunk::get<Padding>(chunk);
if (pad.repeat().type == ChunkRepeatType::FINITE) {
num_bits += pad.num_bits();
instance_chunks.emplace_back<Padding>(pad);
} else if (pad.repeat().type == ChunkRepeatType::FILL_STRIDE) {
ZX_ASSERT_MSG(row_stride, "Stride needed but not provided.");
ZX_ASSERT_MSG(
num_bits % 8 == 0,
"There must be an even number of bytes in a PixelBlock before FILL_STRIDE padding.");
// If we're looking at stride padding, the current num_bits must be the total for the full
// width. The stride should be strictly bigger then, or the format is ill specified.
size_t bytes = num_bits / 8;
ZX_ASSERT_MSG(
*row_stride >= bytes,
"Row stride must be greater than or equal to the number of bytes in 'width' pixels.");
if (*row_stride > bytes) {
size_t pad_size = *row_stride - bytes;
num_bits += pad_size * 8;
instance_chunks.emplace_back<Padding>(pad_size * 8, ChunkRepeat::finite(1));
}
} else {
// It doesn't make any sense to fill an image or the width of an image with padding.
ZX_ASSERT_MSG(false, "Padding only supports FINITE and FILL_STRIDE repeat types.");
}
} else if (chunk->type() == ChunkType::PACKING_BLOCK) {
const PackingBlock& subblock = Chunk::get<PackingBlock>(chunk);
PackingBlock subblock_instance = create_instance_packing(subblock);
// The PackingBlock returned by any call to this function must have a FINITE repeat
// value, as that is the point of this function.
ZX_ASSERT(subblock_instance.repeat().type == ChunkRepeatType::FINITE);
pixel_sum += subblock_instance.repeat().times * subblock_instance.num_pixels();
num_bits += subblock_instance.repeat().times * subblock_instance.num_bits();
instance_chunks.emplace_back<PackingBlock>(std::move(subblock_instance));
}
}
ZX_ASSERT_MSG(num_bits % 8 == 0, "A PackingBlock must contain a whole number of bytes.");
if (block.repeat().type == ChunkRepeatType::FINITE) {
return PackingBlock(std::move(instance_chunks), block.repeat());
} else if (block.repeat().type == ChunkRepeatType::FILL_WIDTH) {
ZX_ASSERT_MSG(
width % pixel_sum == 0,
"A FILL_WIDTH PackingBlock must contain a number of pixels that evenly divides the "
"width.");
uint32_t num_blocks = width / pixel_sum;
return PackingBlock(std::move(instance_chunks), ChunkRepeat::finite(num_blocks));
}
// If we've gotten here the repeat type should be FILL_IMAGE.
ZX_ASSERT_MSG(block.repeat().type == ChunkRepeatType::FILL_IMAGE,
"PackingBlocks only support FINITE, FILL_WIDTH, and FILL_IMAGE repeat types.");
uint32_t total_pixels = width * height;
uint32_t num_blocks = total_pixels / pixel_sum;
if (total_pixels % pixel_sum != 0) {
++num_blocks;
}
return PackingBlock(std::move(instance_chunks), ChunkRepeat::finite(num_blocks));
};
PackingBlock instance_block = create_instance_packing(format.packing_block);
return RawFormatInstance(format.id, width, height, row_stride, std::move(instance_block),
format.color_filter, format.depth_map);
}
} // namespace camera::raw