summaryrefslogtreecommitdiff
path: root/gpr/source/lib/vc5_encoder/encoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'gpr/source/lib/vc5_encoder/encoder.c')
-rwxr-xr-xgpr/source/lib/vc5_encoder/encoder.c2555
1 files changed, 2555 insertions, 0 deletions
diff --git a/gpr/source/lib/vc5_encoder/encoder.c b/gpr/source/lib/vc5_encoder/encoder.c
new file mode 100755
index 0000000..0701a4d
--- /dev/null
+++ b/gpr/source/lib/vc5_encoder/encoder.c
@@ -0,0 +1,2555 @@
+/*! @file encoder.c
+ *
+ * @brief Implementation of functions for encoding samples.
+ *
+ * Encoded samples must be aligned on a four byte boundary.
+ * Any constraints on the alignment of data within the sample
+ * are handled by padding the sample to the correct alignment.
+ *
+ * Note that the encoded dimensions are the actual dimensions of each channel
+ * (or the first channel in the case of 4:2:2 sampling) in the encoded sample.
+ * The display offsets and dimensions specify the portion of the encoded frame
+ * that should be displayed, but in the case of a Bayer image the display
+ * dimensions are doubled to account for the effects of the demosaic filter.
+ * If a Bayer image is encoded to Bayer format (no demosaic filter applied),
+ * then the encoded dimensions will be the same as grid of Bayer quads, less
+ * any padding required during encoding, but the display dimensions and
+ * offset will be reported as if a demosiac filter were applied to scale the
+ * encoded frame to the display dimensions (doubling the width and height).
+ *
+ * (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"
+
+#if ENABLED(NEON)
+#include <arm_neon.h>
+#endif
+
+/*!
+ @brief Align the bitstream to a byte boundary
+
+ Enough bits are written to the bitstream to align the
+ bitstream to the next byte.
+ */
+static CODEC_ERROR AlignBitsByte(BITSTREAM *bitstream)
+{
+ if (bitstream->count > 0 && (bitstream->count % 8) != 0)
+ {
+ // Compute the number of bits of padding
+ BITCOUNT count = (8 - (bitstream->count % 8));
+ PutBits(bitstream, 0, count);
+ }
+ assert((bitstream->count % 8) == 0);
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Align the bitstream to the next segment
+
+ The corresponding function in the existing codec flushes the bitstream.
+
+ @todo Is it necessary to flush the bitstream (and the associated byte stream)
+ after aligning the bitstream to a segment boundary?
+ */
+static CODEC_ERROR AlignBitsSegment(BITSTREAM *bitstream)
+{
+ STREAM *stream = bitstream->stream;
+ size_t byte_count;
+
+ // Byte align the bitstream
+ AlignBitsByte(bitstream);
+ assert((bitstream->count % 8) == 0);
+
+ // Compute the number of bytes in the bit buffer
+ byte_count = bitstream->count / 8;
+
+ // Add the number of bytes written to the stream
+ byte_count += stream->byte_count;
+
+ while ((byte_count % sizeof(TAGVALUE)) != 0)
+ {
+ PutBits(bitstream, 0, 8);
+ byte_count++;
+ }
+
+ // The bitstream should be aligned to the next segment
+ assert((bitstream->count == 0) || (bitstream->count == bit_word_count));
+ assert((byte_count % sizeof(TAGVALUE)) == 0);
+
+ return CODEC_ERROR_OKAY;
+}
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+/*!
+ @brief Set default values for the pattern element structure
+
+ Some image formats imply specific parameters for the dimensions of the
+ pattern elements and the number of components per sample. If the pattern
+ element structure has not been fully specified by the command-line
+ arguments, then missing values can be filled in from the default values
+ for the image format.
+ */
+bool SetImageFormatDefaults(ENCODER *encoder)
+{
+ switch (encoder->image_format)
+ {
+#if VC5_ENABLED_PART(VC5_PART_COLOR_SAMPLING)
+ if (IsPartEnabled(encoder->enabled_parts, VC5_PART_COLOR_SAMPLING))
+ {
+ // The components per sample parameter is not applicable to VC-5 Part 4 bitstreams
+ assert(encoder->components_per_sample == 0);
+ encoder->components_per_sample = 0;
+ }
+ else
+ {
+ // Set the default components per sample assuming no alpha channel
+ if (encoder->components_per_sample == 0) {
+ encoder->components_per_sample = 3;
+ }
+ }
+#else
+ // Set the default components per sample assuming no alpha channel
+ if (encoder->components_per_sample == 0) {
+ encoder->components_per_sample = 3;
+ }
+#endif
+ return true;
+
+ case IMAGE_FORMAT_RAW:
+ if (encoder->pattern_width == 0) {
+ encoder->pattern_width = 2;
+ }
+
+ if (encoder->pattern_height == 0) {
+ encoder->pattern_height = 2;
+ }
+
+ if (encoder->components_per_sample == 0) {
+ encoder->components_per_sample = 1;
+ }
+
+ return true;
+
+ default:
+ // Unable to set default values for the pattern elements
+ return false;
+ }
+}
+#endif
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+/*!
+ @brief Check for inconsistent values for the parameters specified on the command-line
+
+ This routine looks for inconsistencies between the image format, the dimensions of the
+ pattern elements, and the number of components per sample.
+ */
+bool CheckImageFormatParameters(ENCODER *encoder)
+{
+ switch (encoder->image_format)
+ {
+ case IMAGE_FORMAT_RAW:
+
+ if (encoder->pattern_width != 2) {
+ return false;
+ }
+
+ if (encoder->pattern_height != 2) {
+ return false;
+ }
+
+ if (encoder->components_per_sample != 1) {
+ return false;
+ }
+
+ // The parameters for the Bayer image format are correct
+ return true;
+
+ default:
+ // Cannot verify the parameters for an unknown image format
+ return false;
+
+ }
+}
+#endif
+
+/*!
+ @brief Prepare the encoder state
+*/
+CODEC_ERROR PrepareEncoderState(ENCODER *encoder,
+ const UNPACKED_IMAGE *image,
+ const ENCODER_PARAMETERS *parameters)
+{
+ CODEC_STATE *codec = &encoder->codec;
+ int channel_count = image->component_count;
+ int channel_number;
+
+ // Set the default value for the number of bits per lowpass coefficient
+ PRECISION lowpass_precision = 16;
+
+ if (parameters->encoded.lowpass_precision > 0) {
+ lowpass_precision = parameters->encoded.lowpass_precision;
+ }
+
+ for (channel_number = 0; channel_number < channel_count; channel_number++)
+ {
+ DIMENSION width = image->component_array_list[channel_number].width;
+ DIMENSION height = image->component_array_list[channel_number].height;
+ PRECISION bits_per_component = image->component_array_list[channel_number].bits_per_component;
+
+ // Copy the component array parameters into the encoder state
+ encoder->channel[channel_number].width = width;
+ encoder->channel[channel_number].height = height;
+ encoder->channel[channel_number].bits_per_component = bits_per_component;
+
+ // The lowpass bands in all channels are encoded with the same precision
+ encoder->channel[channel_number].lowpass_precision = lowpass_precision;
+ }
+
+ // Record the number of channels in the encoder state
+ encoder->channel_count = channel_count;
+
+ // The encoder uses three wavelet transform levels for each channel
+ encoder->wavelet_count = 3;
+
+ // Set the channel encoding order
+ if (parameters->channel_order_count > 0)
+ {
+ // Use the channel order specified by the encoding parameters
+ encoder->channel_order_count = parameters->channel_order_count;
+ memcpy(encoder->channel_order_table, parameters->channel_order_table, sizeof(encoder->channel_order_table));
+ }
+ else
+ {
+ // Use the default channel encoding order
+ for (channel_number = 0; channel_number < channel_count; channel_number++)
+ {
+ encoder->channel_order_table[channel_number] = channel_number;
+ }
+ encoder->channel_order_count = channel_count;
+ }
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ // The actual image dimensions are reported in the bitstream header (VC-5 Part 3)
+ encoder->image_width = parameters->input.width;
+ encoder->image_height = parameters->input.height;
+ encoder->pattern_width = parameters->pattern_width;
+ encoder->pattern_height = parameters->pattern_height;
+ encoder->components_per_sample = parameters->components_per_sample;
+ encoder->image_format = parameters->encoded.format;
+ encoder->max_bits_per_component = MaxBitsPerComponent(image);
+
+ // Set default parameters for the image format
+ SetImageFormatDefaults(encoder);
+
+ if (!CheckImageFormatParameters(encoder)) {
+ return CODEC_ERROR_BAD_IMAGE_FORMAT;
+ }
+#else
+ // The dimensions of the image is the maximum of the channel dimensions (VC-5 Part 1)
+ GetMaximumChannelDimensions(image, &encoder->image_width, &encoder->image_height);
+#endif
+
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+ // Interlaced images are encoded as separate layers
+ encoder->progressive = parameters->progressive;
+ encoder->top_field_first = TRUE;
+ encoder->frame_inverted = FALSE;
+ encoder->progressive = 1;
+
+ // Set the number of layers (sub-samples) in the encoded sample
+ encoder->layer_count = parameters->layer_count;
+#endif
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ // by default, all sections are enabled
+ encoder->enabled_sections = parameters->enabled_sections;
+#endif
+
+ // Initialize the codec state with the default parameters used by the decoding process
+ return PrepareCodecState(codec);
+}
+
+/*!
+ @brief Initialize the encoder data structure
+
+ This routine performs the same function as a C++ constructor.
+ The encoder is initialized with default values that are replaced
+ by the parameters used to prepare the encoder (see @ref PrepareEncoder).
+
+ This routine does not perform all of the initializations required
+ to prepare the encoder data structure for decoding a sample.
+*/
+CODEC_ERROR InitEncoder(ENCODER *encoder, const gpr_allocator *allocator, const VERSION *version)
+{
+ assert(encoder != NULL);
+ if (! (encoder != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ memset(encoder, 0, sizeof(ENCODER));
+
+ // Assign a memory allocator to the encoder
+ encoder->allocator = (gpr_allocator *)allocator;
+
+ // Write debugging information to standard output
+ encoder->logfile = stdout;
+
+ if (version)
+ {
+ // Store the version number in the encoder
+ memcpy(&encoder->version, version, sizeof(encoder->version));
+ }
+ else
+ {
+ // Clear the version number in the encoder
+ memset(&encoder->version, 0, sizeof(encoder->version));
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Encode the image into the output stream
+
+ This is a convenience routine for applications that use a byte stream to
+ represent a memory buffer or binary file that will store the encoded image.
+
+ The image is unpacked into a set of component arrays by the image unpacking
+ process invoked by calling the routine @ref ImageUnpackingProcess. The image
+ unpacking process is informative and is not part of the VC-5 standard.
+
+ The main entry point for encoding the component arrays output by the image
+ unpacking process is @ref EncodingProcess.
+*/
+CODEC_ERROR EncodeImage(IMAGE *image, STREAM *stream, RGB_IMAGE *rgb_image, ENCODER_PARAMETERS *parameters)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+
+ // Allocate data structures for the encoder state and the bitstream
+ ENCODER encoder;
+ BITSTREAM bitstream;
+
+ SetupEncoderLogCurve();
+
+ UNPACKED_IMAGE unpacked_image;
+
+ // Unpack the image into a set of component arrays
+ error = ImageUnpackingProcess(image, &unpacked_image, parameters, &parameters->allocator);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Initialize the bitstream data structure
+ InitBitstream(&bitstream);
+
+ // Bind the bitstream to the byte stream
+ error = AttachBitstream(&bitstream, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Encode the component arrays into the bitstream
+ error = EncodingProcess(&encoder, &unpacked_image, &bitstream, parameters);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ if( rgb_image != NULL && parameters->rgb_resolution == GPR_RGB_RESOLUTION_SIXTEENTH )
+ { // Thumbnail
+ SetupDecoderLogCurve();
+
+ WaveletToRGB(parameters->allocator,
+ encoder.transform[0].wavelet[2]->data[LL_BAND], encoder.transform[1].wavelet[2]->data[LL_BAND], encoder.transform[2].wavelet[2]->data[LL_BAND],
+ encoder.transform[0].wavelet[2]->width, encoder.transform[0].wavelet[2]->height, encoder.transform[0].wavelet[2]->width,
+ rgb_image, 14, 8, &parameters->rgb_gain );
+ }
+
+ error = ReleaseComponentArrays( &parameters->allocator, &unpacked_image, unpacked_image.component_count );
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Release any resources allocated by the bitstream
+ ReleaseBitstream(&bitstream);
+
+ // Release any resources allocated by the encoder
+ ReleaseEncoder(&encoder);
+
+ return error;
+}
+
+/*!
+ @brief Reference implementation of the VC-5 encoding process.
+
+ The encoder takes input image in the form of a list of component arrays
+ produced by the image unpacking process and encodes the image into the
+ bitstream.
+
+ External parameters are used to initialize the encoder state.
+
+ The encoder state determines how the image is encoded int the bitstream.
+*/
+CODEC_ERROR EncodingProcess(ENCODER *encoder,
+ const UNPACKED_IMAGE *image,
+ BITSTREAM *bitstream,
+ const ENCODER_PARAMETERS *parameters)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+
+ // Initialize the encoder using the parameters provided by the application
+ error = PrepareEncoder(encoder, image, parameters);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ if (encoder->image_format == IMAGE_FORMAT_UNKNOWN) {
+ return CODEC_ERROR_BAD_IMAGE_FORMAT;
+ }
+ if ( parameters->verbose_flag )
+ {
+ LogPrint("Pattern width: %d\n", encoder->pattern_width);
+ LogPrint("Pattern height: %d\n", encoder->pattern_height);
+
+ if (!IsPartEnabled(encoder->enabled_parts, VC5_PART_COLOR_SAMPLING)) {
+ LogPrint("Components per sample: %d\n", encoder->components_per_sample);
+ }
+ LogPrint("Internal precision: %d\n", encoder->internal_precision);
+
+ LogPrint("\n");
+ }
+#endif
+
+ // Write the bitstream start marker
+ PutBitstreamStartMarker(bitstream);
+
+ // Allocate six pairs of lowpass and highpass buffers for each channel
+ AllocateEncoderHorizontalBuffers(encoder);
+
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+
+ if (!IsPartEnabled(encoder.enabled_parts, VC5_PART_LAYERS) || encoder.layer_count == 1)
+ {
+ // Encode the image as a single layer in the sample
+ error = EncodeSingleImage(encoder, ImageData(image), image->pitch, &bitstream);
+ }
+ else
+ {
+ // Each layer encodes a separate frame
+ IMAGE *image_array[MAX_LAYER_COUNT];
+ memset(image_array, 0, sizeof(image_array));
+
+ // The encoding parameters must include a decompositor
+ assert (parameters->decompositor != NULL);
+
+ // Decompose the frame into individual frames for each layer
+ error = parameters->decompositor(image, image_array, encoder.layer_count);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Encode each frame as a separate layer in the sample
+ error = EncodeMultipleImages(encoder, image_array, encoder.layer_count, &bitstream);
+ }
+#else
+
+ // Encode one image into the bitstream
+ error = EncodeSingleImage(encoder, image, bitstream);
+
+#endif
+
+ DeallocateEncoderHorizontalBuffers(encoder);
+
+ return error;
+}
+
+/*!
+ @brief Initialize the encoder using the specified parameters
+
+ It is important to use the correct encoded image dimensions (including padding)
+ and the correct encoded format to initialize the encoder. The decoded image
+ dimensions must be adjusted to account for a lower decoded resolution if applicable.
+
+ It is expected that the parameters data structure may change over time
+ with additional or different fields, depending on the codec profile or
+ changes made to the codec during further development. The parameters
+ data structure may have a version number or may evolve into a dictionary
+ of key-value pairs with missing keys indicating that a default value
+ should be used.
+
+ @todo Add more error checking to this top-level routine
+*/
+CODEC_ERROR PrepareEncoder(ENCODER *encoder,
+ const UNPACKED_IMAGE *image,
+ const ENCODER_PARAMETERS *parameters)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+ VERSION version = VERSION_INITIALIZER(VC5_VERSION_MAJOR, VC5_VERSION_MINOR, VC5_VERSION_REVISION, 0);
+ PRECISION max_bits_per_component = MaxBitsPerComponent(image);
+
+ // Initialize the encoder data structure
+ InitEncoder(encoder, &parameters->allocator, &version);
+
+ // Set the mask that specifies which parts of the VC-5 standard are supported
+ encoder->enabled_parts = parameters->enabled_parts;
+
+ // Verify that the enabled parts are correct
+ error = VerifyEnabledParts(encoder->enabled_parts);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Remember the internal precision used by the image unpacking process
+ encoder->internal_precision = minimum(max_bits_per_component, default_internal_precision);
+
+ // Initialize the encoding parameters and the codec state
+ PrepareEncoderState(encoder, image, parameters);
+
+ // Allocate the wavelet transforms
+ AllocEncoderTransforms(encoder);
+
+ // Initialize the quantizer
+ SetEncoderQuantization(encoder, parameters);
+
+ // Initialize the wavelet transforms
+ PrepareEncoderTransforms(encoder);
+
+ // Allocate the scratch buffers used for encoding
+ AllocEncoderBuffers(encoder);
+
+ // Initialize the encoding tables for magnitudes and runs of zeros
+ error = PrepareCodebooks(&parameters->allocator, &encoder_codeset_17 );
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Select the codebook for encoding
+ encoder->codeset = &encoder_codeset_17;
+
+ // The encoder is ready to decode a sample
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Free all resources allocated by the encoder
+*/
+CODEC_ERROR ReleaseEncoder(ENCODER *encoder)
+{
+ if (encoder != NULL)
+ {
+ gpr_allocator *allocator = encoder->allocator;
+ int channel;
+
+ // Free the encoding tables
+ ReleaseCodebooks(allocator, encoder->codeset);
+
+ // Free the wavelet tree for each channel
+ for (channel = 0; channel < MAX_CHANNEL_COUNT; channel++)
+ {
+ ReleaseTransform(allocator, &encoder->transform[channel]);
+ }
+
+ //TODO: Free the encoding buffers
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Encode a single image into the bitstream
+
+ This is the main entry point for encoding a single image into the bitstream.
+ The encoder must have been initialized by a call to @ref PrepareEncoder.
+
+ The unpacked image is the set of component arrays output by the image unpacking
+ process. The bitstream must be initialized and bound to a byte stream before
+ calling this routine.
+*/
+CODEC_ERROR EncodeSingleImage(ENCODER *encoder, const UNPACKED_IMAGE *image, BITSTREAM *stream)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+
+ // Write the sample header that is common to all layers
+ error = EncodeBitstreamHeader(encoder, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Write the sample extension header to the bitstream
+ error = EncodeExtensionHeader(encoder, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Encode each component array as a separate channel in the bitstream
+ error = EncodeMultipleChannels(encoder, image, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Finish the encoded sample after the last layer
+ error = EncodeBitstreamTrailer(encoder, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Force any data remaining in the bitstream to be written into the sample
+ FlushBitstream(stream);
+
+ return error;
+}
+
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+/*!
+ @brief Encode multiple frames as separate layers in a sample
+
+ The encoder must have been initialized by a call to @ref PrepareEncoder.
+
+ The bitstream must be initialized and bound to a byte stream before
+ calling this routine.
+*/
+CODEC_ERROR EncodeMultipleFrames(ENCODER *encoder, IMAGE *image_array[], int frame_count, BITSTREAM *stream)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+ //CODEC_STATE *codec = &encoder->codec;
+
+ int layer_index;
+
+ // The number of frames must match the number of layers in the sample
+ assert(frame_count == encoder->layer_count);
+
+ // Initialize the codec state
+ PrepareEncoderState(encoder);
+
+ // Write the bitstream start marker
+ error = PutBitstreamStartMarker(stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Write the bitstream header that is common to all layers
+ error = EncodeBitstreamHeader(encoder, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Write the extension header to the bitstream
+ error = EncodeExtensionHeader(encoder, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Encode each frame in a separate layer in the sample
+ for (layer_index = 0; layer_index < frame_count; layer_index++)
+ {
+ error = EncodeLayer(encoder, image_array[layer_index]->buffer, image_array[layer_index]->pitch, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+ }
+
+ error = EncodeSampleExtensionTrailer(encoder, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Finish the encoded sample after the last layer
+ error = EncodeSampleTrailer(encoder, stream);
+ assert(error == CODEC_ERROR_OKAY);
+ if (! (error == CODEC_ERROR_OKAY)) {
+ return error;
+ }
+
+ // Force any data remaining in the bitstream to be written into the sample
+ FlushBitstream(stream);
+
+ // Check that the sample offset stack has been emptied
+ assert(stream->sample_offset_count == 0);
+
+ //TODO: Any resources need to be released?
+
+ // Done encoding all layers in the sample
+ return error;
+}
+#endif
+
+/*!
+ @brief Initialize the wavelet transforms for encoding
+*/
+CODEC_ERROR PrepareEncoderTransforms(ENCODER *encoder)
+{
+ //int channel_count = encoder->channel_count;
+ int channel_number;
+
+ // Set the prescale and quantization in each wavelet transform
+ for (channel_number = 0; channel_number < encoder->channel_count; channel_number++)
+ {
+ TRANSFORM *transform = &encoder->transform[channel_number];
+
+ // Set the prescaling (may be used in setting the quantization)
+ int bits_per_component = encoder->channel[channel_number].bits_per_component;
+ SetTransformPrescale(transform, bits_per_component);
+
+ //TODO: Are the wavelet scale factors still used?
+
+ // Must set the transform scale if not calling SetTransformQuantization
+ SetTransformScale(transform);
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Unpack the image into component arrays for encoding
+*/
+CODEC_ERROR ImageUnpackingProcess(const PACKED_IMAGE *input,
+ UNPACKED_IMAGE *output,
+ const ENCODER_PARAMETERS *parameters,
+ gpr_allocator *allocator)
+{
+ ENABLED_PARTS enabled_parts = parameters->enabled_parts;
+ int channel_count;
+ DIMENSION max_channel_width;
+ DIMENSION max_channel_height;
+ int bits_per_component;
+
+ // The configuration of component arrays is determined by the image format
+ switch (input->format)
+ {
+ case PIXEL_FORMAT_RAW_RGGB_12:
+ case PIXEL_FORMAT_RAW_RGGB_12P:
+ case PIXEL_FORMAT_RAW_RGGB_14:
+ case PIXEL_FORMAT_RAW_GBRG_12:
+ case PIXEL_FORMAT_RAW_GBRG_12P:
+ case PIXEL_FORMAT_RAW_RGGB_16:
+ channel_count = 4;
+ max_channel_width = input->width / 2;
+ max_channel_height = input->height / 2;
+ bits_per_component = 12;
+ break;
+
+ default:
+ assert(0);
+ return CODEC_ERROR_PIXEL_FORMAT;
+ break;
+ }
+
+ // Allocate space for the component arrays
+ AllocateComponentArrays(allocator, output, channel_count, max_channel_width, max_channel_height,
+ input->format, bits_per_component);
+
+
+ // The configuration of component arrays is determined by the image format
+ switch (input->format)
+ {
+ case PIXEL_FORMAT_RAW_RGGB_14:
+ UnpackImage_14(input, output, enabled_parts, true );
+ break;
+
+ case PIXEL_FORMAT_RAW_RGGB_12:
+ UnpackImage_12(input, output, enabled_parts, true );
+ break;
+
+ case PIXEL_FORMAT_RAW_GBRG_12:
+ UnpackImage_12(input, output, enabled_parts, false );
+ break;
+
+ case PIXEL_FORMAT_RAW_RGGB_12P:
+ UnpackImage_12P(input, output, enabled_parts, true );
+ break;
+
+ case PIXEL_FORMAT_RAW_GBRG_12P:
+ UnpackImage_12P(input, output, enabled_parts, false );
+ break;
+
+ default:
+ assert(0);
+ return CODEC_ERROR_PIXEL_FORMAT;
+ break;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+
+
+/*!
+ @brief Insert the header segments that are common to all samples
+
+ This code was derived from PutVideoIntraFrameHeader in the current codec.
+
+ @todo Need to output the channel size table.
+*/
+CODEC_ERROR EncodeBitstreamHeader(ENCODER *encoder, BITSTREAM *stream)
+{
+ CODEC_STATE *codec = &encoder->codec;
+
+ //TAGWORD subband_count = 10;
+ TAGWORD image_width = encoder->image_width;
+ TAGWORD image_height = encoder->image_height;
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ TAGWORD image_format = encoder->image_format;
+ TAGWORD pattern_width = encoder->pattern_width;
+ TAGWORD pattern_height = encoder->pattern_height;
+ TAGWORD components_per_sample = encoder->components_per_sample;
+ TAGWORD max_bits_per_component = encoder->max_bits_per_component;
+ TAGWORD default_bits_per_component = max_bits_per_component;
+#else
+ TAGWORD default_bits_per_component = encoder->internal_precision;
+#endif
+
+ // Align the start of the header on a segment boundary
+ AlignBitsSegment(stream);
+
+ // The bitstream should be aligned to a segment boundary
+ assert(IsAlignedSegment(stream));
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_HEADER))
+ {
+ // Write the section header for the bitstream header into the bitstream
+ BeginHeaderSection(encoder, stream);
+ }
+#endif
+
+ // Output the number of channels
+ if (encoder->channel_count != codec->channel_count) {
+ PutTagPair(stream, CODEC_TAG_ChannelCount, encoder->channel_count);
+ codec->channel_count = encoder->channel_count;
+ }
+
+ // Inform the decoder of the maximum component array dimensions
+ PutTagPair(stream, CODEC_TAG_ImageWidth, image_width);
+ PutTagPair(stream, CODEC_TAG_ImageHeight, image_height);
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ if (IsPartEnabled(encoder->enabled_parts, VC5_PART_IMAGE_FORMATS))
+ {
+ PutTagPair(stream, CODEC_TAG_ImageFormat, image_format);
+ PutTagPair(stream, CODEC_TAG_PatternWidth, pattern_width);
+ PutTagPair(stream, CODEC_TAG_PatternHeight, pattern_height);
+ PutTagPair(stream, CODEC_TAG_ComponentsPerSample, components_per_sample);
+ PutTagPair(stream, CODEC_TAG_MaxBitsPerComponent, max_bits_per_component);
+ }
+#endif
+
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+ if (IsPartEnabled(encoder->enabled_parts, VC5_PART_LAYERS))
+ {
+ // Output the number of layers in the sample (optional for backward compatibility)
+ //PutTagPairOptional(stream, CODEC_TAG_LAYER_COUNT, layer_count);
+ }
+#endif
+
+ // Record the image dimensions in the codec state
+ codec->image_width = image_width;
+ codec->image_height = image_height;
+
+ // The image dimensions determine the default channel dimensions
+ codec->channel_width = image_width;
+ codec->channel_height = image_height;
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ if (IsPartEnabled(encoder->enabled_parts, VC5_PART_IMAGE_FORMATS))
+ {
+ // Record the pattern element parameters in the codec state
+ codec->image_format = image_format;
+ codec->pattern_width = pattern_width;
+ codec->pattern_height = pattern_height;
+ codec->components_per_sample = components_per_sample;
+ codec->max_bits_per_component = (PRECISION)max_bits_per_component;
+ }
+#endif
+
+ // This parameter is the default precision for each channel
+ codec->bits_per_component = default_bits_per_component;
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_HEADER))
+ {
+ // Make sure that the bitstream is aligned to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Update the section header with the actual size of the bitstream header section
+ EndSection(stream);
+ }
+#endif
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the trailer at the end of the encoded sample
+
+ This routine updates the sample size segment in the sample extension header
+ with the actual size of the encoded sample. The size of the encoded sample
+ does not include the size of the sample header or trailer.
+
+ Note that the trailer may not be necessary as the decoder may stop
+ reading from the sample after it has decoded all of the information
+ required to reconstruct the frame.
+
+ This code was derived from PutVideoIntraFrameTrailer in the current codec.
+*/
+CODEC_ERROR EncodeBitstreamTrailer(ENCODER *encoder, BITSTREAM *stream)
+{
+ AlignBitsSegment(stream);
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the unique image identifier
+
+ @todo Should the UMID instance number be a parameter to this routine?
+ */
+static CODEC_ERROR WriteUniqueImageIdentifier(ENCODER *encoder, BITSTREAM *stream)
+{
+ const int UMID_length_byte = 0x13;
+ const int UMID_instance_number = 0;
+
+ // Total length of the unique image identifier chunk payload (in segments)
+ const int identifier_chunk_payload_length = UMID_length + sequence_number_length;
+
+ // Write the tag value pair for the small chunk element for the unique image identifier
+ PutTagPairOptional(stream, CODEC_TAG_UniqueImageIdentifier, identifier_chunk_payload_length);
+
+ // Write the UMID label
+ PutByteArray(stream, UMID_label, sizeof(UMID_label));
+
+ // Write the UMID length byte
+ PutBits(stream, UMID_length_byte, 8);
+
+ // Write the UMID instance number
+ PutBits(stream, UMID_instance_number, 24);
+
+ // Write the image sequence identifier
+ PutByteArray(stream, encoder->image_sequence_identifier, sizeof(encoder->image_sequence_identifier));
+
+ // Write the image sequence number
+ PutLong(stream, encoder->image_sequence_number);
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write extra information that follows the sample header into the bitstream
+
+ This routine writes metadata into the sample header extension.
+
+ Metadata includes the unique GUID for each video clip, the number of each video frame,
+ and the timecode (if available). The GUID and frame number pair uniquely identify each
+ frame in the encoded clip.
+
+ This routine also outputs additional information that describes the characterstics of
+ the encoded video in the GOP extension and sample flags.
+
+ The size of the sample extension header is provided by the sample size segment.
+*/
+CODEC_ERROR EncodeExtensionHeader(ENCODER *encoder, BITSTREAM *stream)
+{
+ ENABLED_PARTS enabled_parts = encoder->enabled_parts;
+
+ // Encode the transform prescale for the first channel (assume all channels are the same)
+ TAGWORD prescale_shift = PackTransformPrescale(&encoder->transform[0]);
+
+ // The tag-value pair is required if the encoder is not using the default values
+ //if (IsTransformPrescaleDefault(&encoder->transform[0], TRANSFORM_TYPE_SPATIAL, encoder->encoded.precision))
+ if (IsTransformPrescaleDefault(&encoder->transform[0], encoder->internal_precision))
+ {
+ PutTagPairOptional(stream, CODEC_TAG_PrescaleShift, prescale_shift);
+ }
+ else
+ {
+ PutTagPair(stream, CODEC_TAG_PrescaleShift, prescale_shift);
+ }
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS))
+ {
+ WriteUniqueImageIdentifier(encoder, stream);
+ }
+#endif
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS) &&
+ !IsComponentTransformIdentity(encoder->component_transform))
+ {
+ WriteComponentTransform(encoder->component_transform, stream);
+ }
+#endif
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS) &&
+ !IsComponentPermutationIdentity(encoder->component_permutation))
+ {
+ WriteComponentPermutation(encoder->component_permutation, stream);
+ }
+#endif
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the sample extension trailer into the bitstream
+
+ This routine must be called after encoding the sample and before writing the
+ sample trailer, but must only be called if the sample extension header was
+ written into the bitstream.
+*/
+CODEC_ERROR EncodeExtensionTrailer(ENCODER *encoder, BITSTREAM *stream)
+{
+ return CODEC_ERROR_OKAY;
+}
+
+static int32_t GetMultiplier(QUANT divisor)
+{
+ switch (divisor)
+ {
+ case 1:
+ return (uint32_t)(1 << 16);
+
+ case 12:
+ return (1 << 16) / 12;
+
+ case 24:
+ return (1 << 16) / 24;
+
+ case 32:
+ return (1 << 16) / 32;
+
+ case 48:
+ return (1 << 16) / 48;
+
+ case 96:
+ return (1 << 16) / 96;
+
+ case 144:
+ return (1 << 16) / 144;
+
+ default:
+ return (uint32_t)(1 << 16) / divisor;
+ };
+}
+
+/*!
+ @brief Compute the rounding value for quantization
+ */
+static QUANT QuantizerMidpoint(QUANT correction, QUANT divisor)
+{
+ int32_t midpoint = 0;
+
+ if (correction == 2)
+ {
+ midpoint = divisor >> 1;
+
+ // CFEncode_Premphasis_Original
+ if (midpoint) {
+ midpoint--;
+ }
+ }
+ else if (correction > 2 && correction < 9)
+ {
+ midpoint = divisor / correction;
+ }
+
+ return midpoint;
+}
+
+static void GetQuantizationParameters(int32_t midpoint_prequant, QUANT quant[], int32_t* midpoints, int32_t* multipliers)
+{
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ midpoints[i] = QuantizerMidpoint(midpoint_prequant, quant[i]);
+ multipliers[i] = GetMultiplier(quant[i]);
+ }
+}
+
+/*!
+ @brief Shift the buffers of horizontal highpass results
+
+ The encoder contains six rows of horizontal lowpass and highpass results
+ for each channel. This routine shifts the buffers by two rows to make
+ room for two more rows of horizontal results for each channel.
+ */
+static void ShiftHorizontalResultBuffers(PIXEL **buffer)
+{
+ PIXEL *buffer01[2];
+
+ memcpy( buffer01, buffer + 0, sizeof(PIXEL*) * 2 );
+
+ memmove( buffer + 0, buffer + 2, sizeof(PIXEL*) * (ROW_BUFFER_COUNT - 2) );
+
+ memcpy( buffer + 4, buffer01, sizeof(PIXEL*) * 2 );
+}
+
+typedef struct _recursive_transform_data
+{
+ PIXEL *input_ptr;
+ DIMENSION input_width;
+ DIMENSION input_height;
+ DIMENSION input_pitch;
+
+ PIXEL *output_ptr[MAX_BAND_COUNT];
+ DIMENSION output_width;
+ DIMENSION output_pitch;
+
+ int32_t prescale;
+
+ int32_t* midpoints;
+ int32_t* multipliers;
+
+ PIXEL **lowpass_buffer;
+ PIXEL **highpass_buffer;
+
+} RECURSIVE_TRANSFORM_DATA;
+
+#define RECURSIVE 1
+
+static void ForwardWaveletTransformRecursive(RECURSIVE_TRANSFORM_DATA *transform_data, int wavelet_stage, uint32_t start_row, uint32_t end_row)
+{
+ uint32_t input_row_index = start_row;
+
+ PIXEL *input_ptr = transform_data[wavelet_stage].input_ptr;
+ DIMENSION input_width = transform_data[wavelet_stage].input_width;
+ DIMENSION input_height = transform_data[wavelet_stage].input_height;
+ DIMENSION input_pitch = transform_data[wavelet_stage].input_pitch;
+
+ PIXEL **output_ptr = transform_data[wavelet_stage].output_ptr;
+ DIMENSION output_width = transform_data[wavelet_stage].output_width;
+ DIMENSION output_pitch = transform_data[wavelet_stage].output_pitch;
+
+ int32_t* midpoints = transform_data[wavelet_stage].midpoints;
+ int32_t* multipliers = transform_data[wavelet_stage].multipliers;
+
+ PIXEL **lowpass_buffer = transform_data[wavelet_stage].lowpass_buffer;
+ PIXEL **highpass_buffer = transform_data[wavelet_stage].highpass_buffer;
+
+ int32_t prescale = transform_data[wavelet_stage].prescale;
+
+ uint32_t bottom_input_row = ((input_height % 2) == 0) ? input_height - 2 : input_height - 1;
+
+ uint32_t last_middle_row = bottom_input_row - 2;
+
+ end_row = minimum( last_middle_row, end_row);
+
+ // --- TOP ROW
+ if( input_row_index == 0 )
+ {
+ int row;
+
+ for (row = 0; row < ROW_BUFFER_COUNT; row++)
+ {
+ PIXEL *input_row_ptr = (PIXEL *)((uintptr_t)input_ptr + row * input_pitch);
+
+ FilterHorizontalRow(input_row_ptr, lowpass_buffer[row], highpass_buffer[row], input_width, prescale);
+ }
+
+ // Process the first row as a special case for the boundary condition
+ FilterVerticalTopRow(lowpass_buffer, highpass_buffer, output_ptr, output_width, output_pitch, midpoints, multipliers, input_row_index );
+ input_row_index += 2;
+ }
+
+ // --- MIDDLE ROWS
+ for (; input_row_index <= end_row; input_row_index += 2)
+ {
+ // Check for errors in the row calculation
+ assert((input_row_index % 2) == 0);
+
+ FilterVerticalMiddleRow(lowpass_buffer, highpass_buffer, output_ptr, output_width, output_pitch, midpoints, multipliers, input_row_index );
+
+ if (input_row_index < last_middle_row)
+ {
+ int row;
+
+ ShiftHorizontalResultBuffers(lowpass_buffer);
+ ShiftHorizontalResultBuffers(highpass_buffer);
+
+ // Get two more rows of horizontal lowpass and highpass results
+ for (row = 4; row < ROW_BUFFER_COUNT; row++)
+ {
+ int next_input_row = minimum( input_row_index + row, input_height - 1 );
+
+ PIXEL *input_row_ptr = (PIXEL *)((uintptr_t)input_ptr + next_input_row * input_pitch);
+
+ FilterHorizontalRow(input_row_ptr, lowpass_buffer[row], highpass_buffer[row], input_width, prescale);
+ }
+ }
+ }
+
+ // --- BOTTOM ROW
+ if( input_row_index == bottom_input_row )
+ {
+ FilterVerticalBottomRow(lowpass_buffer, highpass_buffer, output_ptr, output_width, output_pitch, midpoints, multipliers, input_row_index );
+ }
+
+ if( wavelet_stage < (MAX_WAVELET_COUNT - 1) )
+ {
+ ForwardWaveletTransformRecursive( transform_data, wavelet_stage + 1, 0, 0xFFFF );
+ }
+}
+
+static void SetRecursiveTransformData(RECURSIVE_TRANSFORM_DATA* transform_data,
+ const TRANSFORM *transform,
+ const COMPONENT_ARRAY *input_image_component,
+ int32_t midpoints[MAX_BAND_COUNT], int32_t multipliers[MAX_BAND_COUNT],
+ PIXEL *lowpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT],
+ PIXEL *highpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT],
+ int midpoint_prequant, int wavelet_stage )
+{
+ int i;
+
+ if( wavelet_stage == 0 )
+ {
+ transform_data->input_width = input_image_component->width;
+ transform_data->input_height = input_image_component->height;
+ transform_data->input_pitch = input_image_component->pitch;
+ transform_data->input_ptr = (PIXEL*)input_image_component->data;
+ }
+ else
+ {
+ WAVELET *input_wavelet = transform->wavelet[wavelet_stage - 1];
+
+ transform_data->input_width = input_wavelet->width;
+ transform_data->input_height = input_wavelet->height;
+ transform_data->input_pitch = input_wavelet->pitch;
+ transform_data->input_ptr = WaveletRowAddress(input_wavelet, LL_BAND, 0);
+ }
+
+ WAVELET *output_wavelet = transform->wavelet[wavelet_stage];
+ assert(output_wavelet);
+
+ transform_data->output_width = output_wavelet->width;
+ transform_data->output_pitch = output_wavelet->pitch;
+
+ for (i = 0; i < MAX_BAND_COUNT; i++)
+ {
+ transform_data->output_ptr[i] = output_wavelet->data[i];
+ }
+
+ transform_data->lowpass_buffer = lowpass_buffer[wavelet_stage];
+ transform_data->highpass_buffer = highpass_buffer[wavelet_stage];
+ transform_data->prescale = transform->prescale[wavelet_stage];
+
+ GetQuantizationParameters(midpoint_prequant, output_wavelet->quant, midpoints, multipliers );
+
+ transform_data->midpoints = midpoints;
+ transform_data->multipliers = multipliers;
+}
+
+static void ForwardWaveletTransform(TRANSFORM *transform, const COMPONENT_ARRAY *input_image_component, PIXEL *lowpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT], PIXEL *highpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT], int midpoint_prequant)
+{
+ RECURSIVE_TRANSFORM_DATA transform_data[MAX_WAVELET_COUNT];
+
+ int32_t midpoints[MAX_WAVELET_COUNT][MAX_BAND_COUNT]; //!< Midpoint value for each band (applied during quantization)
+ int32_t multipliers[MAX_WAVELET_COUNT][MAX_BAND_COUNT]; //!< Multiplier value for each band (applied during quantization)
+
+ SetRecursiveTransformData( &transform_data[0], transform, input_image_component, midpoints[0], multipliers[0], lowpass_buffer, highpass_buffer, midpoint_prequant, 0 );
+ SetRecursiveTransformData( &transform_data[1], transform, input_image_component, midpoints[1], multipliers[1], lowpass_buffer, highpass_buffer, midpoint_prequant, 1 );
+ SetRecursiveTransformData( &transform_data[2], transform, input_image_component, midpoints[2], multipliers[2], lowpass_buffer, highpass_buffer, midpoint_prequant, 2 );
+
+ ForwardWaveletTransformRecursive( transform_data, 0, 0, 0xFFFF );
+}
+
+/*!
+ @brief Encode the portion of a sample that corresponds to a single layer
+
+ Samples can be contain multiple subsamples. Each subsample may correspond to
+ a different view. For example, an encoded video sample may contain both the
+ left and right subsamples in a stereo pair.
+
+ Subsamples have been called tracks or channels, but this terminology can be
+ confused with separate video tracks in a multimedia container or the color
+ planes that are called channels elsewhere in this codec.
+
+ The subsamples are decoded seperately and composited to form a single frame
+ that is the output of the complete process of decoding a single video sample.
+ For this reason, the subsamples are called layers.
+
+ @todo Need to reset the codec state for each layer?
+*/
+//CODEC_ERROR EncodeLayer(ENCODER *encoder, void *buffer, size_t pitch, BITSTREAM *stream)
+CODEC_ERROR EncodeMultipleChannels(ENCODER *encoder, const UNPACKED_IMAGE *image, BITSTREAM *stream)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+
+ int channel_count;
+ int channel_index;
+
+ channel_count = encoder->channel_count;
+
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+ if (IsPartEnabled(encoder->enabled_parts, VC5_PART_LAYERS))
+ {
+ // Write the tag value pairs that preceed the encoded wavelet tree
+ error = EncodeLayerHeader(encoder, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+ }
+#endif
+
+
+ CODEC_STATE *codec = &encoder->codec;
+
+ // Compute the wavelet transform tree for each channel
+ for (channel_index = 0; channel_index < channel_count; channel_index++)
+ {
+ int channel_number;
+
+ ForwardWaveletTransform(&encoder->transform[channel_index], &image->component_array_list[channel_index], encoder->lowpass_buffer, encoder->highpass_buffer, encoder->midpoint_prequant );
+
+ channel_number = encoder->channel_order_table[channel_index];
+
+ // Encode the tag value pairs in the header for this channel
+ error = EncodeChannelHeader(encoder, channel_number, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Encode the lowpass and highpass bands in the wavelet tree for this channel
+ error = EncodeChannelSubbands(encoder, channel_number, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Encode the tag value pairs in the trailer for this channel
+ error = EncodeChannelTrailer(encoder, channel_number, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Check that the bitstream is alligned to a segment boundary
+ assert(IsAlignedSegment(stream));
+
+ // Update the codec state for the next channel in the bitstream
+ //codec->channel_number++;
+ codec->channel_number = (channel_number + 1);
+ codec->subband_number = 0;
+ }
+
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+ if (IsPartEnabled(encoder->enabled_parts, VC5_PART_LAYERS))
+ {
+ // Write the tag value pairs that follow the encoded wavelet tree
+ error = EncodeLayerTrailer(encoder, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+ //TODO: Need to align the bitstream between layers?
+ }
+#endif
+
+ return CODEC_ERROR_OKAY;
+}
+
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+/*!
+ @brief Write the sample layer header
+
+ The baseline profile only supports a single layer so the layer header
+ and trailer are not required.
+*/
+CODEC_ERROR EncodeLayerHeader(ENCODER *encoder, BITSTREAM *stream)
+{
+ //TODO: Write the tag-value pair for the layer number
+
+ return CODEC_ERROR_OKAY;
+}
+#endif
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+/*!
+ @brief Write the sample layer trailer
+
+ The baseline profile only supports a single layer so the layer header
+ and trailer are not required.
+
+ If more than one layer is present, the layers must be terminated by a
+ layer trailer. Otherwise, the decoder will continue to parse tag-value
+ pairs that belong to the next layer.
+*/
+CODEC_ERROR EncodeLayerTrailer(ENCODER *encoder, BITSTREAM *stream)
+{
+ // The value in the layer trailer tag-value pair is not used
+ PutTagPairOptional(stream, CODEC_TAG_LAYER_TRAILER, 0);
+
+ return CODEC_ERROR_OKAY;
+}
+#endif
+
+/*!
+ @brief Encode the channel into the bistream
+
+ This routine encodes all of the subbands (lowpass and highpass) in the
+ wavelet tree for the specified channel into the bitstream.
+*/
+CODEC_ERROR EncodeChannelWavelets(ENCODER *encoder, BITSTREAM *stream)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+ CODEC_STATE *codec = &encoder->codec;
+
+ int channel_count;
+ int channel_index;
+
+ // Get the number of channels in the encoder wavelet transform
+ channel_count = encoder->channel_count;
+
+ // Compute the remaining wavelet transforms for each channel
+ //for (channel_index = 0; channel_index < channel_count; channel_index++)
+ for (channel_index = 0; channel_index < channel_count; channel_index++)
+ {
+ int channel_number = encoder->channel_order_table[channel_index];
+
+ // Encode the tag value pairs in the header for this channel
+ error = EncodeChannelHeader(encoder, channel_number, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Encode the lowpass and highpass bands in the wavelet tree for this channel
+ error = EncodeChannelSubbands(encoder, channel_number, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Encode the tag value pairs in the trailer for this channel
+ error = EncodeChannelTrailer(encoder, channel_number, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Check that the bitstream is alligned to a segment boundary
+ assert(IsAlignedSegment(stream));
+
+ // Update the codec state for the next channel in the bitstream
+ //codec->channel_number++;
+ codec->channel_number = (channel_number + 1);
+ codec->subband_number = 0;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the channel header into the bitstream
+
+ The channel header separates channels in the encoded layer. The channel header
+ is not required before the first encoded channel because the codec state is
+ initialized for decoding the first channel.
+
+ The first channel is channel number zero.
+*/
+CODEC_ERROR EncodeChannelHeader(ENCODER *encoder,
+ int channel_number,
+ BITSTREAM *stream)
+{
+ CODEC_STATE *codec = &encoder->codec;
+ DIMENSION channel_width = encoder->channel[channel_number].width;
+ DIMENSION channel_height = encoder->channel[channel_number].height;
+ int bits_per_component = encoder->channel[channel_number].bits_per_component;
+
+ AlignBitsSegment(stream);
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_CHANNEL))
+ {
+ // Write the channel section header into the bitstream
+ BeginChannelSection(encoder, stream);
+ }
+#endif
+
+ // Write the channel number if it does not match the codec state
+ if (channel_number != codec->channel_number)
+ {
+ PutTagPair(stream, CODEC_TAG_ChannelNumber, channel_number);
+ codec->channel_number = channel_number;
+ }
+
+#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS)
+ if (IsPartEnabled(encoder->enabled_parts, VC5_PART_IMAGE_FORMATS))
+ {
+ // The decoder will derive the channel width and height from the image dimensions and format
+ codec->channel_width = channel_width;
+ codec->channel_height = channel_height;
+ }
+ else
+#endif
+ {
+ // Write the component array width if it does not match the codec state
+ if (channel_width != codec->channel_width)
+ {
+ PutTagPair(stream, CODEC_TAG_ChannelWidth, channel_width);
+ codec->channel_width = channel_width;
+ }
+
+ // Write the component array height if it does not match the codec state
+ if (channel_height != codec->channel_height)
+ {
+ PutTagPair(stream, CODEC_TAG_ChannelHeight, channel_height);
+ codec->channel_height = channel_height;
+ }
+ }
+
+ // Write the component array precision if it does not match the codec state
+ if (bits_per_component != codec->bits_per_component)
+ {
+ PutTagPair(stream, CODEC_TAG_BitsPerComponent, bits_per_component);
+ codec->bits_per_component = bits_per_component;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the encoded subbands for this channel into the bitstream
+
+ This routine writes the encoded subbands in the wavelet tree for this channel
+ into the bitstream, including both the lowpass band and all of the highpass
+ bands in each wavelet in this channel.
+*/
+CODEC_ERROR EncodeChannelSubbands(ENCODER *encoder, int channel_number, BITSTREAM *stream)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+ //CODEC_STATE *codec = &encoder->codec;
+
+ int wavelet_count = encoder->wavelet_count;
+ int last_wavelet_index = wavelet_count - 1;
+ int wavelet_index;
+
+ int subband = 0;
+
+ // Start with the lowpass band in the wavelet at the highest level
+ WAVELET *wavelet = encoder->transform[channel_number].wavelet[last_wavelet_index];
+
+ // Check that the bitstream is aligned on a segment boundary
+ assert(IsAlignedSegment(stream));
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_WAVELET))
+ {
+ // Write the wavelet section header into the bitstream
+ BeginWaveletSection(encoder, stream);
+ }
+#endif
+
+ // Encode the lowpass subband in this channel
+ error = EncodeLowpassBand(encoder, wavelet, channel_number, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Advance to the first highpass subband
+ subband++;
+
+ // Encode the highpass bands in order of subband number
+ for (wavelet_index = last_wavelet_index; wavelet_index >= 0; wavelet_index--)
+ {
+ //int wavelet_type = WAVELET_TYPE_SPATIAL;
+ //int wavelet_level = wavelet_index + 1;
+ int band_index;
+
+ //int lowpass_scale = 0;
+ //int lowpass_divisor = 0;
+
+ wavelet = encoder->transform[channel_number].wavelet[wavelet_index];
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_WAVELET))
+ {
+ // Was the wavelet section header already written into the bitstream?
+ if (wavelet_index < last_wavelet_index)
+ {
+ // Write the wavelet section header into the bitstream
+ BeginWaveletSection(encoder, stream);
+ }
+ }
+#endif
+ // Encode the highpass bands in this wavelet
+ for (band_index = 1; band_index < wavelet->band_count; band_index++)
+ {
+ error = EncodeHighpassBand(encoder, wavelet, band_index, subband, stream);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Advance to the next subband
+ subband++;
+ }
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_WAVELET))
+ {
+ // Make sure that the bitstream is aligned to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Update the section header with the actual size of the wavelet section
+ EndSection(stream);
+ }
+#endif
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the channel trailer into the bitstream
+
+ A channel trailer is not required as the channel header functions as a marker
+ between channels in the bitstream.
+
+ It may be necessary to update the channel size in a sample size segment written
+ into the channel header if the channel header includes a sample size segment in
+ the future.
+*/
+CODEC_ERROR EncodeChannelTrailer(ENCODER *encoder, int channel, BITSTREAM *stream)
+{
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_CHANNEL))
+ {
+ // Make sure that the bitstream is aligned to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Update the section header with the actual size of the channel section
+ EndSection(stream);
+ }
+#endif
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Allocate intermediate buffers for the horizontal transform results
+
+ @todo Need to return an error code if allocation fails
+*/
+CODEC_ERROR AllocateEncoderHorizontalBuffers(ENCODER *encoder)
+{
+ gpr_allocator *allocator = encoder->allocator;
+ int channel_index;
+ int wavelet_index;
+ int channel_count = encoder->channel_count;
+
+ int buffer_width = 0;
+
+ for (channel_index = 0; channel_index < channel_count; channel_index++)
+ {
+ buffer_width = maximum(buffer_width, encoder->channel[channel_index].width );
+ }
+
+ buffer_width = ((buffer_width % 2) == 0) ? buffer_width / 2 : (buffer_width + 1) / 2;
+
+ for (wavelet_index = 0; wavelet_index < MAX_WAVELET_COUNT; wavelet_index++)
+ {
+ int row;
+
+ int channel_width = encoder->transform[0].wavelet[wavelet_index]->width;
+
+ for (row = 0; row < ROW_BUFFER_COUNT; row++)
+ {
+ PIXEL *lowpass_buffer = allocator->Alloc(channel_width * sizeof(PIXEL) * 2);
+ PIXEL *highpass_buffer = lowpass_buffer + channel_width;
+
+ assert(lowpass_buffer != NULL);
+ if (! (lowpass_buffer != NULL)) {
+ return CODEC_ERROR_OUTOFMEMORY;
+ }
+
+ encoder->lowpass_buffer[wavelet_index][row] = lowpass_buffer;
+ encoder->highpass_buffer[wavelet_index][row] = highpass_buffer;
+ }
+ }
+
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Deallocate the intermediate buffers for the horizontal transform results
+
+ It is possible to avoid reallocating the buffers for the horizontal transform
+ results if the buffers were not deallocated between encoded frames. In this case,
+ it would be necessary to call this routine inside @ref ReleaseEncoder and it would
+ also be necessary to modify @ref AllocateEncoderHorizontalBuffers to not allocate
+ the buffers if they are already allocated.
+*/
+CODEC_ERROR DeallocateEncoderHorizontalBuffers(ENCODER *encoder)
+{
+ gpr_allocator *allocator = encoder->allocator;
+
+ int wavelet_index;
+
+ for (wavelet_index = 0; wavelet_index < MAX_WAVELET_COUNT; wavelet_index++)
+ {
+ int row;
+
+ for (row = 0; row < ROW_BUFFER_COUNT; row++)
+ {
+ allocator->Free(encoder->lowpass_buffer[wavelet_index][row]);
+ }
+ }
+
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Allocate buffers used for computing the forward wavelet transform
+*/
+CODEC_ERROR AllocateHorizontalBuffers(gpr_allocator *allocator,
+ PIXEL *lowpass_buffer[],
+ PIXEL *highpass_buffer[],
+ int buffer_width)
+{
+ const size_t row_buffer_size = buffer_width * sizeof(PIXEL);
+
+ int row;
+
+ for (row = 0; row < ROW_BUFFER_COUNT; row++)
+ {
+ lowpass_buffer[row] = allocator->Alloc(row_buffer_size);
+ highpass_buffer[row] = allocator->Alloc(row_buffer_size);
+
+ // Check that the memory allocation was successful
+ assert(lowpass_buffer[row] != NULL);
+ if (! (lowpass_buffer[row] != NULL)) {
+ return CODEC_ERROR_OUTOFMEMORY;
+ }
+ assert(highpass_buffer[row] != NULL);
+ if (! (highpass_buffer[row] != NULL)) {
+ return CODEC_ERROR_OUTOFMEMORY;
+ }
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Deallocate buffers used for computing the forward wavelet transform
+*/
+CODEC_ERROR DeallocateHorizontalBuffers(gpr_allocator *allocator,
+ PIXEL *lowpass_buffer[],
+ PIXEL *highpass_buffer[])
+{
+ int row;
+
+ for (row = 0; row < ROW_BUFFER_COUNT; row++)
+ {
+ allocator->Free(lowpass_buffer[row]);
+ allocator->Free(highpass_buffer[row]);
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Allocate all of the wavelets used during encoding
+
+ This routine allocates all of the wavelets in the wavelet tree that
+ may be used during encoding.
+
+ This routine is used to preallocate the wavelets before encoding begins.
+ If the wavelet bands are allocated on demand if not preallocated.
+
+ By default, the wavelet bands are encoded into the bitstream with the bands
+ from the wavelet at the highest level (smallest wavelet) first so that the
+ bands can be processed by the encoder in the order as the sample is decoded.
+
+ @todo Do not allocate wavelets for resolutions that are larger then the
+ decoded resolution. At lower resolutions, the depth of the wavelet tree
+ can be reduced and the highpass bands in the unused wavelets to not have
+ to be decoded.
+
+ @todo Should it be an error if the wavelets are not preallocated?
+*/
+CODEC_ERROR AllocEncoderTransforms(ENCODER *encoder)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+
+ // Use the default allocator for the encoder
+ gpr_allocator *allocator = encoder->allocator;
+ int channel_index;
+ int wavelet_index;
+
+ assert(encoder != NULL);
+ if (! (encoder != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ // Check that the encoded dimensions are valid
+ //assert((encoder->encoded.width % (1 << encoder->wavelet_count)) == 0);
+
+ for (channel_index = 0; channel_index < encoder->channel_count; channel_index++)
+ {
+ // The wavelet at level zero has the same dimensions as the encoded frame
+ DIMENSION wavelet_width = 0;
+ DIMENSION wavelet_height = 0;
+ error = GetChannelDimensions(encoder, channel_index, &wavelet_width, &wavelet_height);
+ assert(wavelet_width > 0 && wavelet_height > 0);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ for (wavelet_index = 0; wavelet_index < encoder->wavelet_count; wavelet_index++)
+ {
+ WAVELET *wavelet = NULL;
+
+ // Pad the wavelet width if not divisible by two
+ if ((wavelet_width % 2) != 0) {
+ wavelet_width++;
+ }
+
+ // Pad the wavelet height if not divisible by two
+ if ((wavelet_height % 2) != 0) {
+ wavelet_height++;
+ }
+
+ // Reduce the dimensions of the next wavelet by half
+ wavelet_width /= 2;
+ wavelet_height /= 2;
+
+ // Dimensions of the current wavelet must be divisible by two
+ //assert((wavelet_width % 2) == 0 && (wavelet_height % 2) == 0);
+
+ // The wavelet width must be divisible by two
+ //assert((wavelet_width % 2) == 0);
+
+ // Allocate the wavelet
+ wavelet = CreateWavelet(allocator, wavelet_width, wavelet_height);
+ if (wavelet == NULL) {
+ return CODEC_ERROR_OUTOFMEMORY;
+ }
+
+ // Add the wavelet to the transform
+ encoder->transform[channel_index].wavelet[wavelet_index] = wavelet;
+ }
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Allocate all of the buffers required for encoding
+
+ This routine allocates buffers required for encoding, not including
+ the wavelet images in the wavelet tree which are allocated by
+ @ref AllocEncoderTransforms
+
+ This routine is used to preallocate buffers before encoding begins.
+ If the buffers are allocated on demand if not preallocated.
+
+ The encoding parameters, including the encoded frame dimensions,
+ resolution of the decoded frame, and the decoded pixel format, are
+ taken into account when the buffers are allocated. For example,
+ buffer space that is only used when encoding to full resolution will
+ not be allocated if the frame is decoded to a smaller size.
+
+ Note that it is not an error to preallocate more buffer space than
+ what is strictly required for encoding. For example, it is okay to
+ allocate buffer space required for full frame encoding even if the
+ encoded sample will be decoded at lower resolution. In many applications,
+ it is simpler to preallocate the maximum buffer space that may be needed.
+
+ Currently, the reference encoder allocates scratch buffers as required
+ by each routine that needs scratch space and the scratch buffers are
+ deallocated at the end each routine that allocates scratch space.
+ A custom memory allocator can make this scheme efficient. See comments
+ in the documentation for the memory allocator module.
+
+ @todo Should it be an error if the buffers are not preallocated?
+*/
+CODEC_ERROR AllocEncoderBuffers(ENCODER *encoder)
+{
+ (void)encoder;
+ return CODEC_ERROR_UNIMPLEMENTED;
+}
+
+/*!
+ @brief Set the quantization parameters in the encoder
+
+ This routine computes the parameters in the quantizer used by
+ the encoder based based on the quality setting and the desired
+ bitrate. The quantization parameters are adjsuted to compensate
+ for the precision of the input pixels.
+
+ Note that the baseline profile does not support quantization to
+ achieve a desired bitrate.
+
+*/
+CODEC_ERROR SetEncoderQuantization(ENCODER *encoder,
+ const ENCODER_PARAMETERS *parameters)
+{
+ int channel_count = encoder->channel_count;
+ int channel_number;
+
+ const int quant_table_length = sizeof(parameters->quant_table)/sizeof(parameters->quant_table[0]);
+
+ // Set the midpoint prequant parameter
+ encoder->midpoint_prequant = 2;
+
+ // Set the quantization table in each channel
+ for (channel_number = 0; channel_number < channel_count; channel_number++)
+ {
+ SetTransformQuantTable(encoder, channel_number, parameters->quant_table, quant_table_length);
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Copy the quantization table into the wavelet bands
+*/
+CODEC_ERROR SetTransformQuantTable(ENCODER *encoder, int channel, const QUANT table[], int table_length)
+{
+ int wavelet_count = encoder->wavelet_count;
+ int wavelet_index;
+ int subband;
+
+ // All lowpass bands use the quantization for subband zero
+ for (wavelet_index = 0; wavelet_index < wavelet_count; wavelet_index++)
+ {
+ WAVELET *wavelet = encoder->transform[channel].wavelet[wavelet_index];
+ wavelet->quant[0] = table[0];
+ }
+
+ // Store the quantization values for the highpass bands in each wavelet
+ for (subband = 1; subband < table_length; subband++)
+ {
+ int wavelet_index = SubbandWaveletIndex(subband);
+ int band_index = SubbandBandIndex(subband);
+ WAVELET *wavelet;
+
+ assert(0 <= wavelet_index && wavelet_index < wavelet_count);
+ assert(0 <= band_index && band_index <= MAX_BAND_COUNT);
+
+ // Store the quantization value for this subband
+ wavelet = encoder->transform[channel].wavelet[wavelet_index];
+ wavelet->quant[band_index] = table[subband];
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Return the encoded dimensions for the specified channel
+
+ The encoded dimensions for each channel may differ due to color
+ difference component sampling.
+*/
+CODEC_ERROR GetChannelDimensions(ENCODER *encoder,
+ int channel_number,
+ DIMENSION *channel_width_out,
+ DIMENSION *channel_height_out)
+{
+ DIMENSION channel_width = 0;
+ DIMENSION channel_height = 0;
+
+ assert(encoder != NULL && channel_width_out != NULL && channel_height_out != NULL);
+ if (! (encoder != NULL && channel_width_out != NULL && channel_height_out != NULL)) {
+ return CODEC_ERROR_NULLPTR;
+ }
+
+ assert(0 <= channel_number && channel_number < encoder->channel_count);
+ if (! (0 <= channel_number && channel_number < encoder->channel_count)) {
+ return CODEC_ERROR_UNEXPECTED;
+ }
+
+ // Clear the output dimensions in case this routine terminates early
+ *channel_width_out = 0;
+ *channel_height_out = 0;
+
+ channel_width = encoder->channel[channel_number].width;
+ channel_height = encoder->channel[channel_number].height;
+
+ *channel_width_out = channel_width;
+ *channel_height_out = channel_height;
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Adjust the height of encoded layer
+
+ Interleaved frames are encoded as separate layers with half the height.
+*/
+DIMENSION EncodedLayerHeight(ENCODER *encoder, DIMENSION height)
+{
+#if VC5_ENABLED_PART(VC5_PART_LAYERS)
+ assert(encoder != NULL);
+ if (encoder->progressive == 0) {
+ height /= 2;
+ }
+#endif
+
+ return height;
+}
+
+/*!
+ @brief Compute the dimensions of the image as reported by the ImageWidth and ImageHeight parameters
+
+ The image width is the maximum width of all component arrays and the image height is the maximum height
+ of all component arrays.
+*/
+CODEC_ERROR GetMaximumChannelDimensions(const UNPACKED_IMAGE *image, DIMENSION *width_out, DIMENSION *height_out)
+{
+ DIMENSION width = 0;
+ DIMENSION height = 0;
+ int channel_number;
+
+ if (image == NULL) {
+ return CODEC_ERROR_UNEXPECTED;
+ }
+
+ for (channel_number = 0; channel_number < image->component_count; channel_number++)
+ {
+ if (width < image->component_array_list[channel_number].width) {
+ width = image->component_array_list[channel_number].width;
+ }
+
+ if (height < image->component_array_list[channel_number].height) {
+ height = image->component_array_list[channel_number].height;
+ }
+ }
+
+ if (width_out != NULL) {
+ *width_out = width;
+ }
+
+ if (height_out != NULL) {
+ *height_out = height;
+ }
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Set the bit for the specified subband in the decoded band mask
+
+ The decoded subband mask is used to track which subbands have been
+ decoded in teh current channel. It is reset at the start of each
+ channel.
+
+ The decoded subband mask is used when decoding a sample at less
+ than full resolution. The mask indicates when enough subbands
+ have been decoded for a channel and that remaining portion of the
+ encoded sample for the current channel may be skipped.
+*/
+CODEC_ERROR SetEncodedBandMask(CODEC_STATE *codec, int subband)
+{
+ if (0 <= subband && subband < MAX_SUBBAND_COUNT) {
+ codec->decoded_subband_mask |= (1 << subband);
+ }
+ return CODEC_ERROR_OKAY;
+}
+
+
+/*!
+ @brief Encoded the lowpass band from the bitstream
+
+ The wavelet at the highest level is passes as an argument.
+ This routine decodes lowpass band in the bitstream into the
+ lowpass band of the wavelet.
+*/
+CODEC_ERROR EncodeLowpassBand(ENCODER *encoder, WAVELET *wavelet, int channel_number, BITSTREAM *stream)
+{
+ CODEC_STATE *codec = &encoder->codec;
+ //FILE *logfile = encoder->logfile;
+ //int subband = 0;
+ //int level = encoder->wavelet_count;
+ int width = wavelet->width;
+ int height = wavelet->height;
+ uint8_t *lowpass_row_ptr;
+ int lowpass_pitch;
+ int row;
+
+ PRECISION lowpass_precision = encoder->channel[channel_number].lowpass_precision;
+
+ lowpass_row_ptr = (uint8_t *)wavelet->data[LL_BAND];
+ lowpass_pitch = wavelet->pitch;
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND))
+ {
+ // Make sure that the bitstream is aligned to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Write the channel section header into the bitstream
+ BeginSubbandSection(encoder, stream);
+ }
+#endif
+
+ // Write the tag-value pairs for the lowpass band to the bitstream
+ PutVideoLowpassHeader(encoder, channel_number, stream);
+
+ // Check that the bitstream is tag aligned before writing the pixels
+ assert(IsAlignedSegment(stream));
+
+ for (row = 0; row < height; row++)
+ {
+ uint16_t *lowpass = (uint16_t *)lowpass_row_ptr;
+ int column;
+
+ for (column = 0; column < width; column++)
+ {
+ BITWORD coefficient = lowpass[column];
+ //assert(0 <= lowpass[column] && lowpass[column] <= COEFFICIENT_MAX);
+ assert(lowpass[column] <= COEFFICIENT_MAX);
+ assert(coefficient <= COEFFICIENT_MAX);
+ PutBits(stream, coefficient, lowpass_precision);
+ }
+
+ lowpass_row_ptr += lowpass_pitch;
+ }
+
+ // Align the bitstream to a segment boundary
+ AlignBitsSegment(stream);
+
+ PutVideoLowpassTrailer(stream);
+
+ // Update the subband number in the codec state
+ codec->subband_number++;
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND))
+ {
+ // Make sure that the bitstream is aligned to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Update the section header with the actual size of the subband section
+ EndSection(stream);
+ }
+#endif
+
+ return CODEC_ERROR_OKAY;
+}
+
+CODEC_ERROR PutVideoSubbandHeader(ENCODER *encoder, int subband_number, QUANT quantization, BITSTREAM *stream)
+{
+ CODEC_STATE *codec = &encoder->codec;
+
+ if (subband_number != codec->subband_number) {
+ PutTagPair(stream, CODEC_TAG_SubbandNumber, subband_number);
+ codec->subband_number = subband_number;
+ }
+
+ if (quantization != codec->band.quantization) {
+ PutTagPair(stream, CODEC_TAG_Quantization, quantization);
+ codec->band.quantization = quantization;
+ }
+
+ // Write the chunk header for the codeblock
+ PushSampleSize(stream, CODEC_TAG_LargeCodeblock);
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Encode the highpass band into the bitstream
+
+ The specified wavelet band is decoded from the bitstream
+ using the codebook and encoding method specified in the
+ bitstream.
+*/
+CODEC_ERROR EncodeHighpassBand(ENCODER *encoder, WAVELET *wavelet, int band, int subband, BITSTREAM *stream)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+ CODEC_STATE *codec = &encoder->codec;
+
+ DIMENSION band_width = wavelet->width;
+ DIMENSION band_height = wavelet->height;
+
+ void *band_data = wavelet->data[band];
+ DIMENSION band_pitch = wavelet->pitch;
+
+ QUANT quantization = wavelet->quant[band];
+ //uint16_t scale = wavelet->scale[band];
+
+ //int divisor = 0;
+ //int peaks_coding = 0;
+
+ ENCODER_CODESET *codeset = encoder->codeset;
+
+ //int encoding_method = BAND_ENCODING_RUNLENGTHS;
+
+ // Check that the band header starts on a tag boundary
+ assert(IsAlignedTag(stream));
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND))
+ {
+ // Make sure that the bitstream is aligned to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Write the channel section header into the bitstream
+ BeginSubbandSection(encoder, stream);
+ }
+#endif
+
+ // Output the tag-value pairs for this subband
+ PutVideoSubbandHeader(encoder, subband, quantization, stream);
+
+ // Encode the highpass coefficients for this subband into the bitstream
+ error = EncodeHighpassBandRowRuns(stream, codeset, band_data, band_width, band_height, band_pitch);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Align the bitstream to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Output the band trailer
+ PutVideoSubbandTrailer(encoder, stream);
+
+ // Update the subband number in the codec state
+ codec->subband_number++;
+
+#if VC5_ENABLED_PART(VC5_PART_SECTIONS)
+ if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND))
+ {
+ // Make sure that the bitstream is aligned to a segment boundary
+ AlignBitsSegment(stream);
+
+ // Update the section header with the actual size of the subband section
+ EndSection(stream);
+ }
+#endif
+
+ return CODEC_ERROR_OKAY;
+}
+
+STATIC_INLINE void write_bits(uint8_t** buffer, uint32_t bits)
+{
+ uint32_t word = Swap32(bits);
+ *( (uint32_t*)(*buffer) ) = word;
+}
+
+STATIC_INLINE VLE PutZeroBits(uint8_t** buffer, VLE stream_bits, uint_fast8_t size )
+{
+ BITCOUNT unused_bit_count = bit_word_count - stream_bits.size;
+
+ if ( size > unused_bit_count )
+ {
+ if (stream_bits.size < bit_word_count)
+ {
+ size -= unused_bit_count;
+ }
+
+ write_bits(buffer, stream_bits.bits);
+ *buffer += 4;
+
+ stream_bits.size = size;
+ stream_bits.bits = 0;
+ }
+ else
+ {
+ stream_bits.size += size;
+ }
+
+ return stream_bits;
+}
+
+STATIC_INLINE VLE PutBitsCore(uint8_t** buffer, VLE stream_bits, uint32_t bits, uint_fast8_t size )
+{
+ BITCOUNT unused_bit_count = bit_word_count - stream_bits.size;
+
+ if ( size > unused_bit_count)
+ {
+ if (stream_bits.size < bit_word_count)
+ {
+ stream_bits.bits |= (bits >> (size - unused_bit_count));
+ size -= unused_bit_count;
+ }
+
+ write_bits(buffer, stream_bits.bits);
+ *buffer += 4;
+
+ stream_bits.size = size;
+ stream_bits.bits = bits << (bit_word_count - size);
+ }
+ else
+ {
+ stream_bits.bits |= (bits << (unused_bit_count - size));
+ stream_bits.size += size;
+ }
+
+ return stream_bits;
+}
+
+STATIC_INLINE VLE PutBitsCoreWithSign(uint8_t** buffer, VLE stream_bits, uint32_t bits, uint_fast8_t size, bool positive )
+{
+ stream_bits = PutBitsCore( buffer, stream_bits, bits, size );
+
+ BITCOUNT unused_bit_count = bit_word_count - stream_bits.size;
+
+ if ( unused_bit_count == 0 )
+ {
+ write_bits(buffer, stream_bits.bits);
+ *buffer += 4;
+
+ stream_bits.size = 1;
+
+ if( positive == false )
+ stream_bits.bits = 1 << (bit_word_count - 1);
+ else
+ stream_bits.bits = 0;
+ }
+ else
+ {
+ stream_bits.size += 1;
+
+ if( positive == false )
+ stream_bits.bits |= (1 << (unused_bit_count - 1));
+ }
+
+ return stream_bits;
+}
+
+/*!
+ @brief Encode the highpass band from the bitstream
+
+ This routine does not encode runs of zeros across row boundaries.
+*/
+CODEC_ERROR EncodeHighpassBandRowRuns(BITSTREAM *stream, ENCODER_CODESET *codeset, PIXEL *data,
+ DIMENSION width, DIMENSION height, DIMENSION pitch)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+
+ int row_padding;
+ int row = 0;
+ //int column = 0;
+ //size_t index = 0;
+
+ // The encoder uses the codebooks for magnitudes and runs of zeros
+ const MAGS_TABLE *mags_table = codeset->mags_table;
+ const RUNS_TABLE *runs_table = codeset->runs_table;
+ uint32_t runs_table_length = runs_table->length;
+ RLC *rlc = (RLC *)((uint8_t *)runs_table + sizeof(RUNS_TABLE));
+
+ // The band is terminated by the band end codeword in the codebook
+ const CODEBOOK *codebook = codeset->codebook;
+
+ PIXEL *rowptr = data;
+
+ // Convert the pitch to units of pixels
+ assert((pitch % sizeof(PIXEL)) == 0);
+ pitch /= sizeof(PIXEL);
+
+ // Check that the band dimensions are reasonable
+ assert(width <= pitch);
+
+ // Compute the number of values of padding at the end of each row
+ row_padding = pitch - width;
+
+ VLE *mags_table_entry = (VLE *)((uint8_t *)mags_table + sizeof(MAGS_TABLE));
+
+ VLE stream_bits;
+
+ stream_bits.bits = stream->buffer;
+ stream_bits.size = stream->count;
+
+ struct _stream *bit_stream = stream->stream;
+
+ int mags_table_length_minus_1 = mags_table->length - 1;
+
+ uint8_t* stream_buffer = (uint8_t *)bit_stream->location.memory.buffer + bit_stream->byte_count;
+ uint8_t* stream_buffer_orig = stream_buffer;
+
+ uint32_t count = 0;
+ for (row = 0; row < height; row++)
+ {
+ uint32_t index = 0; // Start at the beginning of the row
+
+ // Search the row for runs of zeros and nonzero values
+ while (1)
+ {
+ // Loop invariant
+ assert(index < width);
+
+ {
+ PIXEL* start = rowptr + index;
+ PIXEL* end = rowptr + width;
+
+ for (; *(start) == 0 && start != end; start++)
+ {
+
+ }
+
+ uint32_t x = start - (rowptr + index);
+
+ index += x;
+ count += x;
+ }
+
+ // Need to output a value?
+ if (index < width)
+ {
+ while (count > 0)
+ {
+ if( count < 12 )
+ {
+ stream_bits = PutZeroBits(&stream_buffer, stream_bits, count );
+ break;
+ }
+ else
+ {
+ uint32_t count_index = minimum(count, runs_table_length - 1);
+ assert(count_index < runs_table->length);
+
+ RLC rlc_val = rlc[count_index];
+
+ stream_bits = PutBitsCore(&stream_buffer, stream_bits, rlc_val.bits, rlc_val.size );
+
+ // Reduce the length of the run by the amount output
+ count -= rlc_val.count;
+ }
+ }
+
+ count = 0;
+
+ // The value zero is run length coded and handled by another routine
+ {
+ PIXEL value = rowptr[index++];
+ assert(value != 0);
+
+ PIXEL abs_value = minimum( abs(value), mags_table_length_minus_1 );
+
+ stream_bits = PutBitsCoreWithSign(&stream_buffer, stream_bits, mags_table_entry[abs_value].bits, mags_table_entry[abs_value].size, value > 0 );
+ }
+ }
+
+ // Add the end of row padding to the encoded length
+ if (index == width)
+ {
+ count += row_padding;
+ break;
+ }
+ }
+
+ // Should have processed the entire row
+ assert(index == width);
+
+ // Advance to the next row
+ rowptr += pitch;
+ }
+
+ stream->count = stream_bits.size;
+ stream->buffer = stream_bits.bits;
+ bit_stream->byte_count += (stream_buffer - stream_buffer_orig);
+
+ // // Need to output a pending run of zeros?
+ if (count > 0)
+ {
+ error = PutZeros(stream, runs_table, count);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+ }
+
+ // Insert the special codeword that marks the end of the highpass band
+ error = PutSpecial(stream, codebook, SPECIAL_MARKER_BAND_END);
+
+ return error;
+}
+
+CODEC_ERROR PutVideoSubbandTrailer(ENCODER *encoder, BITSTREAM *stream)
+{
+ // Set the size of the large chunk for the highpass band codeblock
+ PopSampleSize(stream);
+
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Read the segment at the specified offset in the bitstream
+
+ This routine is used to read a segment that was previously written at a previous
+ location in the encoded sample. This allows the encoder to update, rather than
+ overwrite, a segment that has already been written. Typically, this is done to
+ insert the size or offset to a portion of the sample (syntax element) into a
+ segment that acts as an index to the syntax element.
+ */
+CODEC_ERROR GetSampleOffsetSegment(BITSTREAM *bitstream, uint32_t offset, TAGVALUE *segment)
+{
+ CODEC_ERROR error = CODEC_ERROR_OKAY;
+ uint32_t buffer;
+
+ error = GetBlock(bitstream->stream, &buffer, sizeof(buffer), offset);
+ if (error != CODEC_ERROR_OKAY) {
+ return error;
+ }
+
+ // Translate the segment to native byte order
+ segment->longword = Swap32(buffer);
+
+ // Cannot return a segment if the offset stack is empty
+ return CODEC_ERROR_OKAY;
+}
+
+/*!
+ @brief Write the lowpass band header into the bitstream
+
+ Each channel is encoded separately, so the lowpass band (subband zero)
+ is the lowpass band in the wavelet at the highest level for each channel.
+
+ The last element in the lowpass band header is a segment that contains the
+ size of this subband. The actual size is updated when the lowpass trailer
+ is written (see @ref PutVideoLowpassTrailer).
+
+ The lowpass start code is used to uniquely identify the start of the lowpass
+ band header and is used by the decode to navigate to the next channel in the
+ bitstream.
+
+ @todo Consider writing a composite lowpass band for all channels with
+ interleaved rows to facilitate access to the thumbnail image in the
+ encoded sample.
+ */
+CODEC_ERROR PutVideoLowpassHeader(ENCODER *encoder, int channel_number, BITSTREAM *stream)
+{
+ CODEC_STATE *codec = &encoder->codec;
+ PRECISION lowpass_precision = encoder->channel[channel_number].lowpass_precision;
+
+ // Output the subband number
+ if (codec->subband_number != 0)
+ {
+ PutTagPair(stream, CODEC_TAG_SubbandNumber, 0);
+ codec->subband_number = 0;
+ }
+
+ // Output the lowpass precision
+ //if (encoder->lowpass.precision != codec->lowpass.precision)
+ if (lowpass_precision != codec->lowpass_precision)
+ {
+ PutTagPair(stream, CODEC_TAG_LowpassPrecision, lowpass_precision);
+ codec->lowpass_precision = lowpass_precision;
+ }
+
+ // Write the chunk header for the codeblock
+ PushSampleSize(stream, CODEC_TAG_LargeCodeblock);
+
+ return CODEC_ERROR_OKAY;
+}
+