blob: bbbd537f69b13a0ace0d9ad1471a49a06d8e7d75 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// 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.
// -----------------------------------------------------------------------------
//
// WP2 encoder: internal header.
//
// Author: Skal (pascal.massimino@gmail.com)
#ifndef WP2_WP2_ENC_I_H_
#define WP2_WP2_ENC_I_H_
#include <array>
#include "src/common/global_params.h"
#include "src/common/lossy/block_size_io.h"
#include "src/common/lossy/context.h"
#include "src/common/lossy/residuals.h"
#include "src/common/progress_watcher.h"
#include "src/enc/block_enc.h"
#include "src/enc/lossless/losslessi_enc.h"
#include "src/enc/symbols_enc.h"
#include "src/utils/ans.h"
#include "src/utils/front_mgr.h"
#include "src/utils/plane.h"
#include "src/wp2/base.h"
#include "src/wp2/encode.h"
namespace WP2 {
// Resets 'EncoderConfig::info' if any. Not thread-safe.
WP2Status SetupEncoderInfo(uint32_t width, uint32_t height,
const EncoderConfig& config);
// Writes WP2 header to 'output' based on 'config' and other arugments.
WP2Status EncodeHeader(const EncoderConfig& config,
uint32_t width, uint32_t height, uint32_t rgb_bit_depth,
bool has_alpha, bool is_anim, bool loop_forever,
Argb38b background_color, RGB12b preview_color,
bool has_icc, bool has_trailing_data,
Writer* const output);
// Creates and compresses a preview from 'buffer' based on 'config' and writes
// it to 'output'.
WP2Status EncodePreview(const ArgbBuffer& buffer, const EncoderConfig& config,
const ProgressRange& progress, Writer* const output);
// Writes 'iccp' to 'output'.
WP2Status EncodeICC(DataView iccp, Writer* const output);
// Writes global parameters 'gparams' to 'output'.
WP2Status EncodeGLBL(const EncoderConfig& config, const GlobalParams& gparams,
bool image_has_alpha, Writer* const output);
// Writes 'metadata' to 'output'.
WP2Status EncodeMetadata(const Metadata& metadata, Writer* const output);
// Returns the TileShape, converting from TILE_SHAPE_AUTO to a concrete
// one if necessary.
TileShape FinalTileShape(const EncoderConfig& config);
// Returns the partition method used at encoding.
// 'tile_width' and 'tile_height' are in pixels, padded or not.
PartitionMethod FinalPartitionMethod(const EncoderConfig& config,
uint32_t tile_width, uint32_t tile_height);
//------------------------------------------------------------------------------
// Choose encoding settings based on the given 'config'.
ChromaSubsampling DecideChromaSubsampling(const EncoderConfig& config,
bool more_than_one_block);
bool DecideAOMCoeffs(const EncoderConfig& config, const Rectangle& tile_rect);
WP2Status DecideModes(const EncoderConfig& config, const GlobalParams& gparams,
BlockModes* const y_modes, BlockModes* const uv_modes,
BlockModes* const a_modes = nullptr);
//------------------------------------------------------------------------------
WP2Status NeuralEncode(const ArgbBuffer& buffer, const EncoderConfig& config,
ANSEnc* const enc);
//------------------------------------------------------------------------------
// Class specialized in writing transform residuals to a stream.
class ResidualWriter : public ResidualIO {
public:
// Initializes the underlying memory.
WP2Status Init(bool use_aom_coeffs, bool has_alpha);
// Default copy is enough to create a deep clone of the full state.
void CopyFrom(const ResidualWriter& other) { operator=(other); }
// Finds the best encoding method for the given 'num_coeffs'.
// If 'cost' is not nullptr, it is set to the cost of storing the residuals
// with this method (coefficients only, not including dictionaries).
static void FindBestEncodingMethod(TrfSize dim, const int16_t* const coeffs,
uint32_t num_coeffs, bool first_is_dc,
Channel channel, uint32_t num_channels,
SymbolCounter* const counter,
EncodingMethod* const encoding_method,
float* const cost = nullptr);
// Records residuals from 'cb'.
void RecordCoeffs(const CodedBlock& cb, Channel channel,
SymbolRecorder* const recorder) const;
// Writes the data (e.g. dictionaries) needed to interpret the symbols that
// will later be written one by one.
WP2Status WriteHeader(uint32_t num_coeffs_max_y, uint32_t num_coeffs_max_uv,
uint32_t num_transforms, bool has_lossy_alpha,
const SymbolRecorder& recorder,
ANSDictionaries* const dicts, ANSEncBase* const enc,
SymbolWriter* const sw);
// Writes residuals from 'cb'.
void WriteCoeffs(const CodedBlock& cb, Channel channel, ANSEncBase* enc,
SymbolManager* const sm) const;
// Returns a (very) crude estimation of the number of bits needed to code
// the residuals.
// The logic for storing the residuals is the same as WriteCoeffs
// except symbol statistics and adaptive symbols do not use global statistics
// but fixed ones.
static float GetPseudoRate(Channel channel, uint32_t num_channels,
TrfSize dim, const int16_t* const coeffs,
uint32_t num_coeffs, bool first_is_dc,
SymbolCounter* const counter);
// Returns a slightly less crude estimation of the number of bits needed to
// code the residuals. Much slower than the pseudo rate above.
static float GetRate(Channel channel, uint32_t num_channels, TrfSize dim,
const int16_t* const coeffs, uint32_t num_coeffs,
bool first_is_dc, SymbolCounter* const counter,
EncodingMethod* const encoding_method = nullptr);
static float GetRateAOM(const CodedBlock& cb, Channel channel,
SymbolCounter* const counter);
private:
// Calls WriteHeader on SymbolWriter for symbols that do depend on a
// specific residual method.
WP2Status WriteHeaderForResidualSymbols(Channel channel,
uint32_t num_coeffs_max,
const SymbolRecorder& recorder,
ANSEncBase* const enc,
SymbolWriter* const sw,
ANSDictionaries* const dicts_in);
// Stores the DC separately.
// 'range' is the maximum absolute value of 'v'.
// 'can_be_zero' specifies whether v can be 0.
static void StoreDC(Channel channel, uint32_t num_channels,
ANSEncBase* const enc, SymbolManager* const sm, int16_t v,
bool can_be_zero);
// Stores residuals.
// The residuals in 'coeffs' are stored in 'enc' and 'sm'.
static void StoreCoeffs(const int16_t* const coeffs, uint32_t num_coeffs,
bool first_is_dc, TrfSize dim, Channel channel,
uint32_t num_channels, EncodingMethod method,
ANSEncBase* enc, SymbolManager* const sm,
bool is_pseudo_rate);
uint32_t num_channels_;
};
// Class for writing the alpha plane.
class AlphaWriter {
public:
// Setup. 'tile_rect' is in pixels, not padded.
WP2Status Init(const EncoderConfig& config, const GlobalParams& gparams,
const BlockContext& context, const YUVPlane& yuv,
const Rectangle& tile_rect,
const ProgressRange& progress);
// Deep copy. 'yuv, context, dicts' must be the clones of 'other' instances.
WP2Status CopyFrom(const AlphaWriter& other, const BlockContext& context);
// Decides cb->alpha_mode_. If lossy is used,
// coefficients will be quantized and an encoding method decided.
WP2Status DecideAlpha(CodedBlock* const cb, const BlockModes& modes,
const ResidualWriter& residual_writer,
Counters* const counters, BlockScorer* const scorer);
WP2Status ResetRecord();
WP2Status Record(const CodedBlock& cb);
void WriteBlockBeforeCoeffs(const CodedBlock& cb, SymbolManager* const sm,
ANSEncBase* const enc);
WP2Status Write(const CodedBlock& cb, ANSEnc* const enc);
// Freezes and writes the dictionaries.
WP2Status WriteHeader(uint32_t num_coeffs_max, ANSEncBase* const enc);
AlphaMode GetAlphaMode() const { return alpha_mode_; }
private:
WP2Status WriteLossless(const CodedBlock& cb, ANSEnc* const enc);
// Decides whether to use lossy residuals or plain black/white for a given
// block. If using residuals, encoding params (predictors/transform) and
// residuals are computed.
WP2Status ProcessLossy(CodedBlock* const cb, const BlockModes& modes,
const ResidualWriter& residual_writer,
const Vector_f& bits_per_pixel,
Counters* const counters, BlockScorer* const scorer);
BlockAlphaMode GetBlockAlphaMode(const CodedBlock& cb,
const Plane16& alpha) const;
const GlobalParams* gparams_ = nullptr;
Rectangle tile_rect_; // In pixels, not padded.
EncoderConfig config_;
AlphaMode alpha_mode_; // Global alpha mode.
// *** Members variables for lossy encoding. ***
// Mode predictor for individual blocks.
AlphaModePredictor mode_predictor_;
// *** Members variables for lossless encoding. ***
// TODO(maryla): ArgbBuffer uses 4 channels even though we only need one...
ArgbBuffer alpha_;
// Separate encoder for lossless.
ANSEnc lossless_enc_;
WP2L::EncodeInfo lossless_encode_info_;
// Next line to write.
uint32_t next_line_ = 0;
const BlockContext* context_;
};
// Class for holding ANS encoder and dictionaries.
class SyntaxWriter : public WP2Allocable {
public:
// 'has_alpha' is the same as buffer.HasTransparency. It is just here to
// save CPU. 'tile_rect' is in pixels, not padded.
WP2Status Init(ANSDictionaries* const dicts, const EncoderConfig& config,
const GlobalParams& gparams, const YUVPlane& yuv,
ChromaSubsampling chroma_subsampling,
const Rectangle& tile_rect, uint32_t num_blocks,
bool use_aom_coeffs, bool use_splits,
const ProgressRange& alpha_progress);
// Deep copy. 'dicts' must be copied beforehand.
WP2Status CopyFrom(const SyntaxWriter& other, ANSDictionaries* const dicts);
// Initializes some data based on results from the previous pass. To be called
// at the beginning of every pass.
WP2Status InitPass();
// Records the block's content, except for size and alpha.
void Record(const CodedBlock& cb);
// Records the block's size.
void RecordSize(const FrontMgrDoubleOrderBase& mgr, BlockSize dim);
void RecordSize(BlockSize dim, BlockSize max_possible_size,
PartitionSet partition_set);
// Records the block's split.
WP2Status RecordSplit(const SplitIteratorBase& mgr, uint32_t split_idx);
// Writes the headers (frozen dictionaries, etc.).
WP2Status WriteHeader(ANSEncBase* const enc);
// These write the block header (segment id, preds, etc.).
void WriteBlockBeforeCoeffs(const CodedBlock& cb, SymbolManager* const sm,
ANSEncBase* const enc);
// Writes the predictors used by the given block for the given channel.
static void WritePredictors(const CodedBlock& cb, Channel channel,
SymbolManager* const sm, ANSEncBase* const enc);
// Writes whether the block is split into smaller square transforms.
static void WriteSplitTransform(const CodedBlock& cb, Channel channel,
SymbolManager* const sm,
ANSEncBase* const enc);
// Writes whether at least one coeff is not zero.
static void WriteHasCoeffs(const CodedBlock& cb, Channel channel,
SymbolManager* const sm, ANSEncBase* const enc);
// Writes the transform if it is not implicit nor all zero.
static void WriteTransform(const CodedBlock& cb, Channel channel,
SymbolManager* const sm, ANSEncBase* const enc);
// Writes all blocks to 'enc'. 'size_order_indices' should be the same size
// as 'cblocks', and contain the indices of a permutation of 'cblocks' that
// puts it in size order (order in which block sizes are written).
WP2Status WriteBlocks(const Vector<CodedBlock>& cblocks,
const Vector_u16& size_order_indices,
FrontMgrDoubleOrderBase* const mgr, ANSEnc* const enc);
WP2Status WriteBlocksUseSplits(const Vector<CodedBlock>& cblocks,
const Vector_u32& splits, ANSEnc* const enc);
SymbolWriter* symbol_writer() { return &symbol_writer_; }
const SymbolRecorder& symbol_recorder() const { return symbol_recorder_; }
Counters* counters() const { return &counters_; }
const BlockContext& context() const { return context_; }
// Finds the best encoding method for the given coeffs
void FindBestEncodingMethods(CodedBlock* const cb);
// Decide alpha mode and encoding params if needed
WP2Status DecideAlpha(CodedBlock* const cb, const BlockModes& modes,
BlockScorer* const scorer);
WP2Status RecordAlpha(const CodedBlock& cb);
// Fills 'segment_ids_' with default values taken from 'gparams_'.
WP2Status SetInitialSegmentIds();
ChromaSubsampling chroma_subsampling() const { return chroma_subsampling_; }
// Final writing of the block, except for its size.
WP2Status WriteBlock(const CodedBlock& cb, uint32_t block_index,
ANSEnc* const enc);
private:
// Resets all records, called by InitPass.
WP2Status ResetRecord();
void RecordBlockHeader(const CodedBlock& cb);
const EncoderConfig* config_;
ANSDictionaries* dicts_;
AlphaWriter alpha_writer_;
// Actual number of blocks and transforms if known or upper bound.
uint32_t num_blocks_, num_transforms_;
// Number of times Record() has been called.
uint32_t recorded_blocks_;
// Pass number.
uint32_t pass_number_ = 0u;
Rectangle tile_rect_; // In pixels, not padded.
ChromaSubsampling chroma_subsampling_;
bool use_splits_;
const GlobalParams* gparams_;
BlockContext context_;
SymbolWriter symbol_writer_;
SymbolRecorder symbol_recorder_;
mutable Counters counters_; // temporary data made to be shared
ResidualWriter residual_writer_;
// Debugging.
// Set to true to print debug information for checking accuracy of estimated
// rates (bit costs).
static const bool kDebugPrintRate = false;
// For each block, estimated residual rate per channel followed by actual rate
// per channel.
Vector<std::array<float, 8>> residual_rate_;
// Encodes the 'block' boundaries and the 'pixels'.
void PutRawPixels(const CodedBlockBase& cb, const YUVPlane& pixels,
ANSEnc* const enc);
};
//------------------------------------------------------------------------------
} // namespace WP2
#endif /* WP2_WP2_ENC_I_H_ */