| // 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. |
| // ----------------------------------------------------------------------------- |
| // |
| // Lossless encoder: internal header. |
| // |
| // Author: Vincent Rabaud (vrabaud@google.com) |
| |
| #ifndef WP2_ENC_LOSSLESS_LOSSLESSI_ENC_H_ |
| #define WP2_ENC_LOSSLESS_LOSSLESSI_ENC_H_ |
| |
| #include <array> |
| #include <cstdint> |
| |
| #include "src/common/progress_watcher.h" |
| #include "src/common/symbols.h" |
| #include "src/enc/lossless/backward_references_enc.h" |
| #include "src/enc/lossless/palette.h" |
| #include "src/utils/ans_enc.h" |
| #include "src/utils/vector.h" |
| #include "src/wp2/base.h" |
| #include "src/wp2/encode.h" |
| #include "src/wp2/format_constants.h" |
| |
| namespace WP2 { |
| class SymbolManager; |
| class SymbolRecorder; |
| class SymbolWriter; |
| } // namespace WP2 |
| |
| namespace WP2L { |
| |
| // Struct containing signed 4-channel data. The stride is 4 * width. |
| // It is meant to store the original color image, as well as its transformed |
| // versions. The number of channel bits represents the depth of the original |
| // data, before the transforms. |
| struct Buffer_s16 { |
| int16_t* GetRow(uint32_t y) { return &data[4 * y * width]; } |
| const int16_t* GetRow(uint32_t y) const { return &data[4 * y * width]; } |
| int16_t* GetPosition(uint32_t x, uint32_t y) { |
| return &data[4 * (y * width + x)]; |
| } |
| const int16_t* GetPosition(uint32_t x, uint32_t y) const { |
| return &data[4 * (y * width + x)]; |
| } |
| uint32_t width; |
| uint32_t height; |
| int16_t* data = nullptr; |
| bool has_alpha; // true if alpha matters |
| uint32_t channel_bits; // number of bits per non-alpha channel |
| }; |
| struct EncodeInfo; |
| struct CrunchConfig; |
| |
| class Encoder { |
| public: |
| Encoder(const WP2::EncoderConfig& config, const WP2::ArgbBuffer& picture, |
| bool has_alpha, bool is_premultiplied); |
| WP2Status Allocate(); |
| WP2Status AllocateTransformBuffer(const WP2::Vector<CrunchConfig>& configs); |
| // Applies and writes the transforms as well as some more info about the |
| // transforms (number, range ...). |
| // If 'config_prev' is not nullptr, its transforms are supposed to be the |
| // first transforms of 'config' and are skipped: they are not applied and they |
| // are stored in 'enc' by copying them from cached data (that was cached from |
| // a previous call with 'cache_data' set to true). |
| WP2Status ApplyAndWriteTransforms(const CrunchConfig* config_prev, |
| bool cache_data, |
| WP2::ProgressRange progress, |
| CrunchConfig* config, |
| LosslessSymbolsInfo* symbols_info, |
| WP2::ANSEnc* enc, |
| WP2::ANSDictionaries* dicts); |
| // Encodes the image for a given configuration. |
| // 'enc_best' and 'dicts_init' contain the encoded info so far. |
| // 'cost_best' is the cost in bits the final encoding must beat. If a better |
| // cost is found, it is updated and 'enc_best' contains the best encoding. |
| // Otherwise, 'enc_best' is set to empty. |
| // 'dicts_init' is used as temporary data and modified. |
| WP2Status EncodeImageInternal(const CrunchConfig& config, |
| const WP2::ProgressRange& progress, |
| const LosslessSymbolsInfo& symbols_info_init, |
| float* cost_best, WP2::ANSEnc* enc_init, |
| EncodeInfo* encode_info, |
| WP2::ANSDictionaries* dicts_init); |
| |
| const WP2::EncoderConfig& config_; // user configuration and parameters |
| const WP2::ArgbBuffer& pic_; // input picture (tile view) |
| bool has_alpha_; // false if the whole image is opaque (not only this tile) |
| bool image_is_premultiplied_; // true if the whole image is considered |
| // premultiplied, even if this tile is not |
| |
| LosslessSymbolsInfo symbols_info_; |
| |
| Palette palette_; |
| |
| // Some 'scratch' (potentially large) objects. |
| BackwardRefsPool ref_pool_; // Backward Refs array for temporaries. |
| HashChain hash_chain_; // HashChain data for constructing |
| // backward references. |
| private: |
| // Applies and writes the transforms one by one and nothing else. |
| // If 'config_prev' is not nullptr, its transforms are supposed to be the |
| // first transforms of 'config' and are skipped. |
| WP2Status ApplyAndWriteTransformsImpl(const CrunchConfig* config_prev, |
| WP2::ProgressRange progress, |
| CrunchConfig* config, |
| LosslessSymbolsInfo* symbols_info, |
| WP2::ANSEnc* enc, |
| WP2::ANSDictionaries* dicts); |
| // Implementations of the different transforms. |
| void ApplyYCoCgR(); |
| void ApplySubtractGreen(); |
| WP2Status ApplyPredictor(const int32_t min_values[4], |
| const int32_t max_values[4], bool can_use_sub_modes, |
| const WP2::ProgressRange& progress, |
| uint32_t pred_bits, WP2::ANSEnc* enc, |
| WP2::ANSDictionaries* dicts); |
| WP2Status ApplyCrossColor(const WP2::ProgressRange& progress, |
| uint32_t ccolor_transform_bits, WP2::ANSEnc* enc, |
| WP2::ANSDictionaries* dicts); |
| WP2Status ApplyCrossColorGlobal(const WP2::ProgressRange& progress, |
| TransformHeader::Channel c1, |
| TransformHeader::Channel c2, |
| TransformHeader::CCTransform second_transform, |
| TransformHeader::CCTransform third_transform, |
| WP2::ANSEnc* enc); |
| WP2Status ApplyColorIndexing(const CrunchConfig& config); |
| // Copy the original pic_ when need. |
| WP2Status MakeInputImageCopy(const CrunchConfig& config); |
| |
| int16_t* argb_scratch_; // Scratch memory for argb rows |
| // (used for prediction). |
| int16_t* transform_data_; // Scratch memory for transform data. |
| WP2::Vector_s16 transform_mem_; // Currently allocated memory. |
| |
| // When testing different sets of transforms, the results (argb_buffer_, |
| // stream, dictionaries) can be re-used from this cached variables. |
| WP2::ANSEnc cached_transforms_enc_; |
| WP2::ANSDictionaries cached_transforms_dict_; |
| // Image data that is initialized and updated between crunch configurations if |
| // possible. |
| Buffer_s16 argb_buffer_; |
| }; |
| |
| //------------------------------------------------------------------------------ |
| // internal functions. Not public. |
| |
| // Encodes any image that is not the main image (segment image, transform images |
| // (predictors, palette ...)). |
| WP2Status EncodeHelperImage(WP2::ANSEncBase* enc, WP2::ANSDictionaries* dicts, |
| const int16_t* argb, HashChain* hash_chain, |
| BackwardRefsPool* ref_pool, uint32_t width, |
| uint32_t height, |
| const LosslessSymbolsInfo& symbols_info, int effort, |
| const WP2::ProgressRange& progress); |
| |
| // Optional extra info that can be exported by EncodeImage. |
| // Callers should allocate themselves the vectors that they wish filled in. |
| // Empty vectors will not be filled in. |
| struct EncodeInfo { |
| WP2_NO_DISCARD bool CopyFrom(const EncodeInfo& other); |
| |
| // The number of tokens in the ANSEnc at which point each line has finished |
| // being written is returned in it. If non-empty, its size should match the |
| // height of the image. |
| WP2::Vector_u32 line_tokens; |
| // Cost in bits of storing each pixel of the image (not including bits of |
| // headers like palettes, ans stats...), in row major order. If non-empty, |
| // its size should match the number of pixels of the image. |
| WP2::Vector_f bits_per_pixel; |
| }; |
| |
| // Encodes the picture. |
| // 'has_alpha' might be true for another tile even if this one does not. |
| // 'image_is_premultiplied' can be true for the whole image even if this tile |
| // is not, and the format of the input picture is not necessarily matched. |
| WP2Status EncodeImage(const WP2::EncoderConfig& config, |
| const WP2::ArgbBuffer& picture, bool has_alpha, |
| bool image_is_premultiplied, |
| const WP2::ProgressRange& progress, WP2::ANSEnc* enc, |
| EncodeInfo* encode_info = nullptr); |
| |
| // Stores the pixels of the image, without caring about the header. |
| // 'segments' is an ARGB image where the cluster is stored in G and other |
| // components are pre-filled with 0. |
| WP2Status StorePixels(uint32_t width, uint32_t height, uint32_t histo_bits, |
| const BackwardRefs& refs, |
| const WP2::ProgressRange& progress, |
| const int16_t* segments, WP2::ANSEncBase* enc, |
| WP2::SymbolManager* sw, |
| EncodeInfo* encode_info = nullptr); |
| // Same as above except for an image where there are no entropy segments. |
| WP2Status StorePixels(uint32_t width, uint32_t height, const BackwardRefs& refs, |
| WP2::ANSEncBase* enc, WP2::SymbolManager* sw); |
| // Stores the different symbol headers and sets up 'sw' with the right symbol |
| // info. |
| WP2Status WriteHeaders(const WP2::SymbolRecorder& recorder, |
| const LosslessSymbolsInfo& symbols_info, |
| uint32_t num_pixels, int effort, WP2::ANSEncBase* enc, |
| WP2::ANSDictionaries* dicts, WP2::SymbolWriter* sw, |
| float* cost = nullptr); |
| |
| // Converts an offset to a code taking into account the proximity to the |
| // current pixel. |
| uint32_t OffsetToPlaneCode(uint32_t width, uint32_t offset); |
| |
| //------------------------------------------------------------------------------ |
| // Image transforms in predictor_enc.cc |
| |
| // Writes the predictor sub modes (sub angles for now). |
| // 'sub_modes' is a 4-channel image with the sub-mode being stored in index 2. |
| // If 'has_both_modes' is true, the mode is obtained from 'modes' as |
| // modes[4 * x + 2] >> kNumPredictorsBits. |
| // If false, it is simply modes[4 * x + 2]. |
| WP2Status WriteSubModes(const int16_t* modes, bool needs_shift, |
| const WP2::Vector_s8& sub_modes, int effort, |
| WP2::ANSEncBase* enc, WP2::ANSDictionaries* dicts); |
| |
| // Computes the signed residuals after applying the predictors. |
| // 'can_use_sub_modes' indicates whether sub-modes are allowed by the transform |
| // and should be tried to find the best predictors. |
| // If sub-modes are used, 'sub_modes' is not empty and contains the sub-modes. |
| WP2Status CreateResidualImage(uint32_t bits, bool exact, bool can_use_sub_modes, |
| const int32_t min_values[4], |
| const int32_t max_values[4], int effort, |
| const WP2::ProgressRange& progress, |
| Buffer_s16* buffer, int16_t* argb_scratch, |
| int16_t* modes, WP2::Vector_s8* sub_modes); |
| |
| WP2Status ColorSpaceTransform(uint32_t bits, int effort, Buffer_s16* buffer, |
| int16_t* image); |
| |
| //------------------------------------------------------------------------------ |
| |
| // Set of parameters to be used in each iteration of the cruncher. |
| static constexpr uint32_t kCrunchLZ77Max = 2; |
| struct CrunchConfig { |
| uint32_t lz77s_types_to_try[kCrunchLZ77Max]; |
| uint32_t lz77s_types_to_try_size; |
| EncodingAlgorithm algorithm; |
| // Image transforms to apply in order. Each transform is only used once. |
| std::array<TransformHeader, kPossibleTransformCombinationSize> transforms; |
| uint32_t GetNumTransforms() const; |
| // whether pixels should be encoded premultiplied with the alpha value |
| bool use_premultiplied; |
| // Number of bits to subsample the original image dimensions by to get the |
| // histogram and transform image dimensions. |
| uint32_t histo_bits; |
| uint32_t transform_bits; |
| // Use a MoveToFrontCache in group4 to signal new colors. |
| bool group4_use_move_to_front; |
| // How to sort the palette. |
| Palette::Sorting palette_sorting_type; |
| // Whether the config can be skipped if one has already been found to be |
| // valid. |
| bool is_skippable = false; |
| // Returns whether one of the transforms is kPredictorWSub. |
| bool HasSubModes() const { |
| for (uint32_t i = 0; i < kPossibleTransformCombinationSize; ++i) { |
| if (transforms[i].type == TransformType::kPredictorWSub) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| // Return true if caching the current CrunchConfig results will help 'config'. |
| bool ShouldBeCachedFor(const CrunchConfig& config) const; |
| }; |
| |
| WP2Status EncoderAnalyze(const WP2::ProgressRange& progress, Encoder* encoder, |
| WP2::Vector<CrunchConfig>* configs); |
| |
| //------------------------------------------------------------------------------ |
| // Group 4. |
| |
| WP2Status Group4Encode(const Buffer_s16& buffer, uint32_t num_colors, |
| bool use_move_to_front, int effort, |
| const WP2::ProgressRange& progress, WP2::ANSEncBase* enc, |
| EncodeInfo* encode_info); |
| |
| //------------------------------------------------------------------------------ |
| // LZW. |
| |
| // Encodes an image 'input_buffer' to 'enc'. |
| // The 'palette' is the set of all unique colors from the 'input_buffer'. |
| WP2Status LZWEncode(const Buffer_s16& input_buffer, const Palette& palette, |
| int effort, WP2::ANSEncBase* enc, EncodeInfo* encode_info); |
| |
| } // namespace WP2L |
| |
| //------------------------------------------------------------------------------ |
| |
| #endif /* WP2_ENC_LOSSLESS_LOSSLESSI_ENC_H_ */ |