summaryrefslogtreecommitdiff
path: root/gpr/source/lib/vc5_encoder/component.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpr/source/lib/vc5_encoder/component.c')
-rwxr-xr-xgpr/source/lib/vc5_encoder/component.c403
1 files changed, 403 insertions, 0 deletions
diff --git a/gpr/source/lib/vc5_encoder/component.c b/gpr/source/lib/vc5_encoder/component.c
new file mode 100755
index 0000000..72c33ed
--- /dev/null
+++ b/gpr/source/lib/vc5_encoder/component.c
@@ -0,0 +1,403 @@
+/*! @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;
+}