gpr-tools/source/lib/vc5_encoder/component.c

404 wiersze
13 KiB
C
Executable File

/*! @file component.c
*
* @brief Implementation of the inverse component transform and inverse component permutation.
*
* (C) Copyright 2018 GoPro Inc (http://gopro.com/).
*
* Licensed under either:
* - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0
* - MIT license, http://opensource.org/licenses/MIT
* at your option.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "headers.h"
/*!
@brief Initialize a component transform
*/
CODEC_ERROR InitComponentTransform(COMPONENT_TRANSFORM *transform)
{
if (transform != NULL)
{
transform->component_count = 0;
transform->transform_matrix = NULL;
transform->transform_offset = NULL;
transform->transform_scale = NULL;
return CODEC_ERROR_OKAY;
}
return CODEC_ERROR_UNEXPECTED;
}
/*!
@brief Initialize a component permutation
*/
CODEC_ERROR InitComponentPermutation(COMPONENT_PERMUTATION *permutation)
{
if (permutation != NULL)
{
permutation->component_count = 0;
permutation->permutation_array = NULL;
return CODEC_ERROR_OKAY;
}
return CODEC_ERROR_UNEXPECTED;
}
/*!
@brief Allocate the arrays in a component transform
The allocated arrays in the component transform are initialized to all zeros.
*/
CODEC_ERROR AllocateComponentTransform(gpr_allocator *allocator, COMPONENT_TRANSFORM *transform, int component_count)
{
size_t transform_matrix_size = component_count * component_count * sizeof(uint16_t);
size_t transform_offset_size = component_count * sizeof(uint16_t);
size_t transform_scale_size = component_count * sizeof(uint16_t);
transform->transform_matrix = (uint16_t *)allocator->Alloc(transform_matrix_size);
transform->transform_offset = (uint16_t *)allocator->Alloc(transform_offset_size);
transform->transform_scale = (uint16_t *)allocator->Alloc(transform_scale_size);
if (transform->transform_matrix == NULL ||
transform->transform_offset == NULL ||
transform->transform_scale == NULL) {
//TODO: Should clean up the partially allocated transform arrays
return CODEC_ERROR_OUTOFMEMORY;
}
transform->component_count = component_count;
memset(transform->transform_matrix, 0, transform_matrix_size);
memset(transform->transform_offset, 0, transform_offset_size);
memset(transform->transform_scale, 0, transform_scale_size);
return CODEC_ERROR_OKAY;
}
/*!
@brief Allocate the arrays in a component permutation
The allocated arrays in the component permutation are initialized to all zeros.
*/
CODEC_ERROR AllocateComponentPermutation(gpr_allocator *allocator, COMPONENT_PERMUTATION *permutation, int component_count)
{
size_t permutation_array_size = component_count * sizeof(uint16_t);
permutation->permutation_array = (uint16_t *)allocator->Alloc(permutation_array_size);
if (permutation->permutation_array == NULL) {
return CODEC_ERROR_OUTOFMEMORY;
}
permutation->component_count = component_count;
memset(permutation->permutation_array, 0, permutation_array_size);
return CODEC_ERROR_OKAY;
}
/*!
@brief Release the arrays in a component transform
*/
CODEC_ERROR ReleaseComponentTransform(gpr_allocator *allocator, COMPONENT_TRANSFORM *transform)
{
if (transform != NULL)
{
allocator->Free(transform->transform_matrix);
allocator->Free(transform->transform_offset);
allocator->Free(transform->transform_scale);
memset(transform, 0, sizeof(COMPONENT_TRANSFORM));
}
return CODEC_ERROR_OKAY;
}
/*!
@brief Release the arrays in a component permutation
*/
CODEC_ERROR ReleaseComponentPermutation(gpr_allocator *allocator, COMPONENT_PERMUTATION *permutation)
{
if (permutation != NULL)
{
allocator->Free(permutation->permutation_array);
memset(permutation, 0, sizeof(COMPONENT_PERMUTATION));
}
return CODEC_ERROR_OKAY;
}
/*!
@brief Initialize a component transform to the identity transform
*/
CODEC_ERROR InitComponentTransformIdentity(COMPONENT_TRANSFORM *transform, int component_count, gpr_allocator *allocator)
{
CODEC_ERROR error = CODEC_ERROR_OKAY;
int component_index;
InitComponentTransform(transform);
error = AllocateComponentTransform(allocator, transform, component_count);
if (error != CODEC_ERROR_OKAY) {
return error;
}
for (component_index = 0; component_index < component_count; component_index++)
{
// Compute the index to the diagonal element in the matrix
int array_index = component_index * component_count + component_index;
transform->transform_matrix[array_index] = 1;
}
return CODEC_ERROR_OKAY;
}
/*!
@brief Initialize a component transform to the identity permutation
*/
CODEC_ERROR InitComponentPermutationIdentity(COMPONENT_PERMUTATION *permutation, int component_count, gpr_allocator *allocator)
{
CODEC_ERROR error = CODEC_ERROR_OKAY;
int component_index;
InitComponentPermutation(permutation);
error = AllocateComponentPermutation(allocator, permutation, component_count);
if (error != CODEC_ERROR_OKAY) {
return error;
}
for (component_index = 0; component_index < component_count; component_index++)
{
permutation->permutation_array[component_index] = component_index;
}
return CODEC_ERROR_OKAY;
}
/*!
@brief Initialize a component transform to known values for testing
*/
CODEC_ERROR InitComponentTransformTesting(COMPONENT_TRANSFORM *transform, int component_count, gpr_allocator *allocator)
{
CODEC_ERROR error = CODEC_ERROR_OKAY;
int row;
int column;
InitComponentTransform(transform);
error = AllocateComponentTransform(allocator, transform, component_count);
if (error != CODEC_ERROR_OKAY) {
return error;
}
for (row = 0; row < component_count; row++)
{
for (column = 0; column < component_count; column++)
{
// Compute the index to the element in the matrix
int array_index = row * component_count + column;
transform->transform_matrix[array_index] = array_index;
}
transform->transform_offset[row] = (component_count - row);
transform->transform_scale[row] = row + 1;
}
return CODEC_ERROR_OKAY;
}
/*!
@brief Initialize a component transform to known values for testing
*/
CODEC_ERROR InitComponentPermutationTesting(COMPONENT_PERMUTATION *permutation, int component_count, gpr_allocator *allocator)
{
CODEC_ERROR error = CODEC_ERROR_OKAY;
int component_index;
InitComponentPermutation(permutation);
error = AllocateComponentPermutation(allocator, permutation, component_count);
if (error != CODEC_ERROR_OKAY) {
return error;
}
for (component_index = 0; component_index < component_count; component_index++)
{
permutation->permutation_array[component_index] = component_count - component_index - 1;
}
return CODEC_ERROR_OKAY;
}
/*!
@brief Return true if the component transform is the identity transform
*/
bool IsComponentTransformIdentity(COMPONENT_TRANSFORM *transform)
{
int component_count;
int component_row;
int component_column;
// A null transform is equivalent to the identity transform
if (transform == NULL) return true;
component_count = transform->component_count;
for (component_row = 0; component_row < component_count; component_row++)
{
// Is the transform matrix the identity matrix?
for (component_column = 0; component_column < component_count; component_column++)
{
// Compute the index to the component element in the transform matrix
int array_index = component_row * component_count + component_column;
if (component_row == component_column)
{
if (transform->transform_matrix[array_index] != 1) return false;
}
else
{
if (transform->transform_matrix[array_index] != 0) return false;
}
}
// Is the transform offset zero?
if (transform->transform_offset != 0) return false;
// Is the scale factor zero?
if (transform->transform_scale != 0) return false;
}
// The component transform is the identity transform
return true;
}
/*!
@brief Allocate the arrays in a component permutation
*/
bool IsComponentPermutationIdentity(COMPONENT_PERMUTATION *permutation)
{
int component_count;
int component_index;
// A null permutation is equivalent to the identity permutation
if (permutation == NULL) {
return true;
}
component_count = permutation->component_count;
for (component_index = 0; component_index < component_count; component_index++)
{
if (permutation->permutation_array[component_index] != component_index) {
return false;
}
}
// The component permutation is the identity permutation
return true;
}
/*!
@brief Write the component transform to the bitstream
@todo Use the InverseTransform16 syntax element if any values are larger than a single byte
*/
CODEC_ERROR WriteComponentTransform(COMPONENT_TRANSFORM *transform, BITSTREAM *stream)
{
if (transform != NULL)
{
const int component_count = transform->component_count;
const size_t chunk_payload_size = (component_count * component_count + 2 * component_count) * sizeof(uint8_t);
const size_t chunk_payload_padding = sizeof(SEGMENT) - (chunk_payload_size % sizeof(SEGMENT));
const int chunk_payload_length = (int)((chunk_payload_size + sizeof(SEGMENT) - 1) / sizeof(SEGMENT));
int i;
// Write the tag value pair for the small chunk element for the component transform
PutTagPair(stream, CODEC_TAG_InverseTransform, chunk_payload_length);
for (i = 0; i < component_count; i++)
{
int offset_value;
int scale_value;
// Write the row at this index in the transform matrix
int j;
for (j = 0; j < component_count; j++)
{
int array_index = i * component_count + j;
int array_value = transform->transform_matrix[array_index];
assert(INT8_MIN <= array_value && array_value <= INT8_MAX);
PutBits(stream, array_value, 8);
}
// Write the offset
offset_value = transform->transform_offset[i];
assert(INT8_MIN <= offset_value && offset_value <= INT8_MAX);
PutBits(stream, offset_value, 8);
// Write the scale
scale_value = transform->transform_scale[i];
assert(0 <= scale_value && scale_value <= UINT8_MAX);
PutBits(stream, scale_value, 8);
}
// Pad the remainer of the chunk payload with zeros
for (i = 0; i < (int)chunk_payload_padding; i++) {
PutBits(stream, 0, 8);
}
// Check that the bitstream is aligned on a segment boundary
assert(IsAlignedSegment(stream));
if (! (IsAlignedSegment(stream))) {
return CODEC_ERROR_UNEXPECTED;
}
return CODEC_ERROR_OKAY;
}
return CODEC_ERROR_UNEXPECTED;
}
/*!
@brief Write the component permutation to the bitstream
*/
CODEC_ERROR WriteComponentPermutation(COMPONENT_PERMUTATION *permutation, BITSTREAM *stream)
{
if (permutation != NULL)
{
const int component_count = permutation->component_count;
const size_t chunk_payload_size = component_count * sizeof(uint8_t);
const size_t chunk_payload_padding = sizeof(SEGMENT) - (chunk_payload_size % sizeof(SEGMENT));
const int chunk_payload_length = (int)((chunk_payload_size + sizeof(SEGMENT) - 1) / sizeof(SEGMENT));
int i;
// Write the tag value pair for the small chunk element for the component transform
PutTagPair(stream, CODEC_TAG_InversePermutation, chunk_payload_length);
for (i = 0; i < component_count; i++)
{
uint8_t value = (uint8_t)permutation->permutation_array[i];
PutBits(stream, value, 8);
}
// Pad the remainer of the chunk payload with zeros
for (i = 0; i < (int)chunk_payload_padding; i++) {
PutBits(stream, 0, 8);
}
// Check that the bitstream is aligned on a segment boundary
assert(IsAlignedSegment(stream));
if (! (IsAlignedSegment(stream))) {
return CODEC_ERROR_UNEXPECTED;
}
return CODEC_ERROR_OKAY;
}
return CODEC_ERROR_UNEXPECTED;
}