blob: 53842f47f28391407262c9f2a3930fcf71de11d5 [file] [log] [blame]
// Copyright 2016 The Cobalt Authors. All Rights Reserved.
// 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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "base/basictypes.h"
#include "cobalt/loader/mesh/projection_codec/constants.h"
#include "cobalt/loader/mesh/projection_codec/indexed_vert.h"
namespace cobalt {
namespace loader {
namespace mesh {
namespace projection_codec {
class ProjectionDecoder {
// We need limits to keep us from having crazy large meshes that eat up all of
// our RAM and take down services. The values below are chosen to be large
// enough for everything we see on the horizon, but if future valid meshes
// need bigger numbers they can certainly be adjusted.
static const uint32_t kMaxCoordinateCount = 10 * 1000;
static const uint32_t kMaxVertexCount = 32 * 1000;
static const uint32_t kMaxMeshCount = 32;
static const uint32_t kMaxTriangleIndexCount = 128 * 1000;
// This is a callback handler interface for a ProjecionDecoder.
class Sink {
virtual ~Sink();
// These are the callbacks, with calling pattern like:
// BeginMeshCollection
// AddCoordinate
// ...
// AddVertex
// ...
// BeginMeshInstance
// SetMeshGeometryType
// AddVertexIndex
// ...
// EndMeshInstance
// EndMeshCollection
// Give the Sink an opportunity to check the CRC and decide whether it
// is known already and skip the decoding. This helps with memory, cpu, and
// streaming cost.
virtual bool IsCached(uint32_t crc) = 0;
virtual void BeginMeshCollection() = 0;
virtual void AddCoordinate(float value) = 0;
virtual void AddVertex(const IndexedVert& v) = 0;
virtual void BeginMeshInstance() = 0;
virtual void SetTextureId(uint8_t texture_id) = 0;
virtual void SetMeshGeometryType(MeshGeometryType type) = 0;
virtual void AddVertexIndex(size_t v_index) = 0;
virtual void EndMeshInstance() = 0;
virtual void EndMeshCollection() = 0;
// Decodes a full projection box that includes the full box header.
static bool DecodeToSink(const uint8_t* data, size_t data_size, Sink* sink);
// Decodes the contents of a projection box without the full box header.
// |version| and |flags| are the values from the full box header provided by
// an external parser. |data| contains all box contents after the full box
// header.
static bool DecodeBoxContentsToSink(uint8_t version, uint32_t flags,
const uint8_t* data, size_t data_size,
Sink* sink);
// Decodes the contents of a projection box without the full box header or
// CRC-32 or compression FourCC. |version|, |flags|, |crc|, and |compression|
// are the values the partially parsed MeshProjectionBox. |data| contains all
// the remaining box contents after compression FourCC.
static bool DecodeMeshesToSink(uint8_t version, uint32_t flags, uint32_t crc,
const std::string& compression,
const uint8_t* data, size_t data_size,
Sink* sink);
class BitReader {
BitReader(const uint8_t* data, size_t data_size);
// Reads the requested number of big-endian bits. Sets ok() to false if an
// error ocurred.
uint32_t GetBits(int32_t num_bits);
// Aligns the reader to an 8-bit boundary, potentially discarding up to 7
// bits.
void Align();
// Discards the requested number of bits and advances the reader forward.
void SkipBits(int64_t num_bits);
// Gets a pointer to the underlying raw buffer of aligned bytes. Exactly
// bits_remaining() / 8 bytes are available and may be accessed.
const uint8_t* aligned_raw_bytes() const;
// Gets the number of bits available in the buffer. If ok() is false, this
// will be zero.
int64_t bits_remaining() const;
// Returns true if the reader is aligned on an 8-bit boundary.
bool is_aligned() const;
// Returns true if the reader is in a good state and everything is okay.
bool ok() const;
const uint8_t* data_begin_; // Unowned.
const uint8_t* data_end_; // Unowned.
// Cached bits, stored as big-endian. 7 or fewer bits may be cached between
// method calls.
uint64_t cached_bits_;
// The number of cached bits in cached_bits_. Only fewer than 8 bits should
// ever be cached between method calls.
int32_t cached_bit_count_;
bool ok_;
bool error_;
Sink* sink_; // Unowned.
BitReader reader_;
uint8_t version_;
uint32_t flags_;
uint32_t crc_;
std::string compression_;
std::vector<uint8_t> decompress_buffer_;
ProjectionDecoder(const uint8_t* data, size_t data_size, Sink* sink);
bool VerifyGreater(uint32_t a, uint32_t b);
bool VerifyEqual(uint32_t a, uint32_t b);
bool VerifyEqual(const std::string& a, const std::string& b);
bool VerifyAligned();
uint32_t GetBits(int32_t num_bits);
uint8_t GetUInt8();
uint32_t GetUInt32();
float GetFloat();
std::string GetFourCC();
void SkipBits(int64_t num_bits);
void Align();
void CheckOK();
void DecodeProjectionBox();
void DecodeProjectionBoxContents(uint8_t version, uint32_t flags);
void DecodeCompressedProjectionBoxContents(uint8_t version, uint32_t flags,
uint32_t crc,
const std::string& compression);
void DecodeMeshData();
void DeflateDecompress();
} // namespace projection_codec
} // namespace mesh
} // namespace loader
} // namespace cobalt