blob: 37a448b0fb6ce331cc4438fda6aab5297adf5d00 [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.
#ifndef SRC_CAMERA_LIB_RAW_FORMATS_RAW_PACKING_H_
#define SRC_CAMERA_LIB_RAW_FORMATS_RAW_PACKING_H_
#include <bit>
#include <concepts>
#include <functional>
#include <memory>
#include <optional>
#include <vector>
#include "src/camera/lib/raw_formats/pointer_list.h"
#include "src/camera/lib/raw_formats/raw_hash.h"
namespace camera::raw {
// A chunk may be:
// - some number of bits of padding.
// - some number of bits (a "piece") of a pixel.
// - a packing block containing a list of chunks.
enum class ChunkType {
PADDING,
PIXEL_PIECE,
PACKING_BLOCK,
};
// A chunk can be tagged to be repeated in certain ways within the context of it's parent.
enum class ChunkRepeatType {
FINITE,
FILL_WIDTH,
FILL_STRIDE,
FILL_IMAGE,
};
// All the information needed to describe chunk repetition.
struct ChunkRepeat {
ChunkRepeatType type;
uint32_t times; // used if type is FINITE.
static constexpr ChunkRepeat finite(uint32_t times) { return {ChunkRepeatType::FINITE, times}; }
static constexpr ChunkRepeat fill_width() { return {ChunkRepeatType::FILL_WIDTH, 0}; }
static constexpr ChunkRepeat fill_stride() { return {ChunkRepeatType::FILL_STRIDE, 0}; }
static constexpr ChunkRepeat fill_image() { return {ChunkRepeatType::FILL_IMAGE, 0}; }
};
// Interface for a "Chunk". All chunks have a size in bits, some repetition specifier, and a type
// (used for casting to the actual implementation). They should also be cloneable (performs dynamic
// allocation).
class Chunk {
public:
constexpr virtual ~Chunk() = default;
constexpr virtual uint64_t num_bits() const = 0;
constexpr virtual ChunkType type() const = 0;
constexpr virtual ChunkRepeat repeat() const = 0;
constexpr virtual Chunk* clone() const = 0;
// A helper for grabbing a const reference to the underlying implementation.
template <typename T>
requires std::derived_from<T, Chunk>
static constexpr const T& get(const Chunk* chunk) {
return *(static_cast<const T*>(chunk));
}
};
class Padding : public Chunk {
public:
constexpr Padding(uint64_t num_bits, ChunkRepeat repeat) : num_bits_(num_bits), repeat_(repeat) {}
constexpr Padding(const Padding& o) : num_bits_(o.num_bits_), repeat_(o.repeat_) {}
constexpr uint64_t num_bits() const override { return num_bits_; }
constexpr ChunkType type() const override { return type_; }
constexpr ChunkRepeat repeat() const override { return repeat_; }
constexpr Chunk* clone() const override { return new Padding(num_bits_, repeat_); }
private:
const uint64_t num_bits_;
const ChunkType type_ = ChunkType::PADDING;
const ChunkRepeat repeat_;
};
class PixelPiece : public Chunk {
public:
constexpr PixelPiece(uint32_t pixel_index, uint8_t mask, int8_t shift)
: pixel_index_(pixel_index), num_bits_(std::popcount(mask)), mask_(mask), shift_(shift) {}
constexpr PixelPiece(const PixelPiece& o)
: pixel_index_(o.pixel_index_), num_bits_(o.num_bits_), mask_(o.mask_), shift_(o.shift_) {}
constexpr uint32_t pixel_index() const { return pixel_index_; }
constexpr uint8_t mask() const { return mask_; }
constexpr int8_t shift() const { return shift_; }
constexpr uint64_t num_bits() const override { return num_bits_; }
constexpr ChunkType type() const override { return ChunkType::PIXEL_PIECE; }
constexpr ChunkRepeat repeat() const override { return {ChunkRepeatType::FINITE, 1}; }
constexpr Chunk* clone() const override { return new PixelPiece(pixel_index_, mask_, shift_); }
private:
const uint32_t pixel_index_;
const uint64_t num_bits_;
const uint8_t mask_;
const int8_t shift_;
};
// Sum all of the bits within a list of chunks, provided all of the chunks have a finite repeat
// specification. Returns zero if any are not finite.
constexpr uint64_t SumBits(const PointerList<Chunk>& chunks) {
uint64_t sum = 0;
for (uint64_t i = 0; i < chunks.size(); ++i) {
const Chunk& chunk = *(chunks[i]);
if (chunk.repeat().type != ChunkRepeatType::FINITE)
return 0;
sum += chunk.repeat().times * chunk.num_bits();
}
return sum;
}
// Sum all of the pixels within a list of chunks, provided all of the chunks have a finite repeat
// specification. Returns zero if any are not finite.
constexpr uint32_t SumPixels(const PointerList<Chunk>& chunks);
class PackingBlock : public Chunk {
public:
template <typename Cpl>
constexpr PackingBlock(Cpl&& chunks, ChunkRepeat repeat)
: size_(SumBits(chunks) / 8),
num_pixels_(SumPixels(chunks)),
repeat_(repeat),
chunks_(std::forward<Cpl>(chunks)) {}
constexpr PackingBlock(const PackingBlock& o)
: size_(o.size_), num_pixels_(o.num_pixels_), repeat_(o.repeat_), chunks_(o.chunks_) {}
PackingBlock(PackingBlock&& o) noexcept
: size_(o.size_),
num_pixels_(o.num_pixels_),
repeat_(o.repeat_),
chunks_(std::move(o.chunks_)) {}
PackingBlock(PackingBlock&& o, ChunkRepeat repeat)
: size_(o.size_),
num_pixels_(o.num_pixels_),
repeat_(repeat),
chunks_(std::move(o.chunks_)) {}
// A PackingBlock must contain a whole number of pixels.
constexpr uint32_t num_pixels() const { return num_pixels_; }
// A PackingBlock must be a whole number of bytes.
constexpr size_t size() const { return size_; }
// Get the child chunks.
constexpr const PointerList<Chunk>& chunks() const { return chunks_; }
// The size() in bytes multiplied by 8.
constexpr uint64_t num_bits() const override { return size_ * 8; }
// The type of chunk this is.
constexpr ChunkType type() const override { return ChunkType::PACKING_BLOCK; }
// How should this chunk be repeated.
constexpr ChunkRepeat repeat() const override { return repeat_; }
constexpr Chunk* clone() const override { return new PackingBlock(chunks_, repeat_); }
private:
size_t size_;
uint32_t num_pixels_;
ChunkRepeat repeat_;
PointerList<Chunk> chunks_;
};
constexpr uint32_t SumPixels(const PointerList<Chunk>& chunks) {
// The things we do for the sake of making it work in constexpr. In the absence of constexpr
// support for std::unordered_set, this uses an array of bools to keep track of which pixel
// indices we've seen. Pixel indices in a block always sequential starting at 0, and there can't
// be more pixels than there are chunks (so this array may be slightly overprovisioned but never
// underprovisioned).
bool* seen_pixels = new bool[chunks.size()];
for (uint64_t i = 0; i < chunks.size(); ++i) {
seen_pixels[i] = false;
}
uint32_t pixel_sum = 0;
for (uint64_t i = 0; i < chunks.size(); ++i) {
const Chunk& chunk = *(chunks[i]);
if (chunk.repeat().type != ChunkRepeatType::FINITE) {
delete[] seen_pixels;
return 0;
}
if (chunk.type() == ChunkType::PACKING_BLOCK) {
const PackingBlock& block = Chunk::get<PackingBlock>(chunks[i]);
pixel_sum += block.repeat().times * block.num_pixels();
} else if (chunk.type() == ChunkType::PIXEL_PIECE) {
const PixelPiece& piece = Chunk::get<PixelPiece>(chunks[i]);
if (!seen_pixels[piece.pixel_index()]) {
++pixel_sum;
seen_pixels[piece.pixel_index()] = true;
}
}
}
delete[] seen_pixels;
return pixel_sum;
}
namespace internal {
template <>
struct hash<ChunkRepeat> {
constexpr size_t operator()(ChunkRepeat const& cr) const noexcept {
size_t seed = 0;
internal::hash_combine(seed, cr.type);
internal::hash_combine(seed, cr.times);
return seed;
}
};
template <>
struct hash<Padding> {
constexpr size_t operator()(Padding const& c) const noexcept {
size_t seed = 0;
internal::hash_combine(seed, c.num_bits());
internal::hash_combine(seed, c.type());
internal::hash_combine(seed, c.repeat());
return seed;
}
};
template <>
struct hash<PixelPiece> {
constexpr size_t operator()(PixelPiece const& p) const noexcept {
size_t seed = 0;
internal::hash_combine(seed, p.num_bits());
internal::hash_combine(seed, p.type());
internal::hash_combine(seed, p.repeat());
internal::hash_combine(seed, p.pixel_index());
internal::hash_combine(seed, p.mask());
internal::hash_combine(seed, p.shift());
return seed;
}
};
template <>
struct hash<PackingBlock> {
constexpr size_t operator()(PackingBlock const& pb) const noexcept {
size_t seed = 0;
internal::hash_combine(seed, pb.num_bits());
internal::hash_combine(seed, pb.type());
internal::hash_combine(seed, pb.repeat());
internal::hash_combine(seed, pb.num_pixels());
internal::hash_combine(seed, pb.chunks().size());
for (uint64_t i = 0; i < pb.chunks().size(); ++i) {
const auto& chunk = pb.chunks()[i];
if (chunk->type() == ChunkType::PADDING) {
const Padding& pad = Chunk::get<Padding>(chunk);
internal::hash_combine(seed, pad);
} else if (chunk->type() == ChunkType::PIXEL_PIECE) {
const PixelPiece& piece = Chunk::get<PixelPiece>(chunk);
internal::hash_combine(seed, piece);
} else if (chunk->type() == ChunkType::PACKING_BLOCK) {
const PackingBlock& block = Chunk::get<PackingBlock>(chunk);
internal::hash_combine(seed, block);
}
}
return seed;
}
};
} // namespace internal
} // namespace camera::raw
#endif // SRC_CAMERA_LIB_RAW_FORMATS_RAW_PACKING_H_