blob: 247e587d780fc2baab0f4effd66a8e655f047a0d [file] [log] [blame]
// Copyright 2020 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.
// -----------------------------------------------------------------------------
//
// Bypass with raw pixel coding
//
// Author: Yannis Guyon (yguyon@google.com)
#include "src/common/global_params.h"
#include "src/common/header_enc_dec.h"
#include "src/common/lossy/block.h"
#include "src/common/lossy/block_size.h"
#include "src/dec/tile_dec.h"
#include "src/dec/wp2_dec_i.h"
#include "src/dsp/math.h"
#include "src/wp2/base.h"
#include "src/wp2/format_constants.h"
namespace WP2 {
//------------------------------------------------------------------------------
WP2Status TileDecoder::BypassTileDec() {
const uint32_t num_read_bytes_before =
tile_->input->GetNumDiscardedBytes() + tile_->input->GetNumReadBytes();
if (gparams_->type_ == GlobalParams::GP_LOSSLESS) {
tile_->output_is_yuv = false;
assert(!tile_->rgb_output.IsEmpty());
const uint32_t bytes_per_row =
tile_->rect.width * (gparams_->has_alpha_ ? 4 : 3);
assert(bytes_per_row * tile_->rect.height ==
GetTileMaxNumBytes(*gparams_, tile_->rect));
uint8_t* row = (uint8_t*)tile_->rgb_output.GetRow(0);
tile_->num_decoded_rows = 0;
while (tile_->num_decoded_rows < tile_->rect.height) {
DataSource::DataHandle handle;
WP2_CHECK_OK(tile_->input->TryReadNext(bytes_per_row, &handle),
WP2_STATUS_NOT_ENOUGH_DATA); // TODO(yguyon): Or OOM?
if (gparams_->has_alpha_) {
std::memcpy(row, handle.GetBytes(), handle.GetSize());
} else {
for (uint32_t x = 0; x < tile_->rect.width; ++x) {
row[x * 4 + 0] = kAlphaMax;
row[x * 4 + 1] = handle.GetBytes()[x * 3 + 0];
row[x * 4 + 2] = handle.GetBytes()[x * 3 + 1];
row[x * 4 + 3] = handle.GetBytes()[x * 3 + 2];
}
}
row += tile_->rgb_output.stride();
++tile_->num_decoded_rows;
WP2_CHECK_STATUS(tile_->row_progress.AdvanceBy(1.));
}
} else { // !GP_LOSSLESS
tile_->output_is_yuv = true;
assert(!tile_->yuv_output.IsEmpty());
const uint32_t num_bits = gparams_->transf_.GetYUVPrecisionBits() + 1u;
const uint32_t min_num_bytes_per_px =
((gparams_->has_alpha_ ? kAlphaBits : 0) + num_bits * 3) / 8;
tile_->block_map.Init(tile_->rect, num_bits, gparams_->transf_.GetYUVMin(),
gparams_->transf_.GetYUVMax(), &tile_->yuv_output);
if (IsFilterBlockMapNeeded(*config_, *features_, *gparams_,
*tiles_layout_)) {
WP2_CHECK_STATUS(tile_->block_map.Allocate());
}
// Simulate a grid of blocks for the IntertileFilter settings.
// Even though this is lossless it is likely to border a lossy tile which
// might benefit from slight deblocking.
constexpr BlockSize kSimulateBlockSize = BLK_4x4;
const uint32_t sim_block_height = BlockHeightPix(kSimulateBlockSize);
const uint32_t sim_block_width = BlockWidthPix(kSimulateBlockSize);
CodedBlock sim_block; // For FilterBlockMap::RegisterBlock().
sim_block.alpha_mode_ = kBlockAlphaLossless;
sim_block.id_ = 0; // Set segment id as the least quantized one.
sim_block.is420_ = false;
for (Channel c : {kYChannel, kUChannel, kVChannel, kAChannel}) {
std::fill(&sim_block.coeffs_[c][0][0], // Maximum residual density.
&sim_block.coeffs_[c][0][0] + kMaxBlockSizePix2, 1);
}
HeaderDec bit_unpacker(tile_->input, "raw_pixels");
if (!gparams_->has_alpha_ && !tile_->yuv_output.A.IsEmpty()) {
tile_->yuv_output.A.Fill(kAlphaMax);
}
tile_->num_decoded_rows = 0;
while (tile_->num_decoded_rows < tile_->rect.height) {
const uint32_t y = tile_->num_decoded_rows;
// Read interleaved channels to output samples line by line.
for (Channel channel : {kYChannel, kUChannel, kVChannel}) {
int16_t* const row = tile_->yuv_output.GetChannel(channel).Row(y);
for (uint32_t x = 0; x < tile_->rect.width; ++x) {
row[x] = (int16_t)bit_unpacker.ReadSBits(num_bits, "yuv");
}
}
if (gparams_->has_alpha_) {
int16_t* const row = tile_->yuv_output.A.Row(y);
for (uint32_t x = 0; x < tile_->rect.width; ++x) {
row[x] = (int16_t)bit_unpacker.ReadBits(kAlphaBits, "alpha");
}
}
++tile_->num_decoded_rows;
if (tile_->num_decoded_rows % sim_block_height == 0 ||
tile_->num_decoded_rows == tile_->rect.height) {
// Register simulated blocks for intertile filtering when complete.
for (uint32_t x = 0; x < tile_->rect.width; x += sim_block_width) {
sim_block.SetDimDefault(
{x / kMinBlockSizePix, y / kMinBlockSizePix, kSimulateBlockSize});
tile_->block_map.RegisterBlock(sim_block, min_num_bytes_per_px);
}
}
WP2_CHECK_STATUS(tile_->row_progress.AdvanceBy(1.));
}
WP2_CHECK_STATUS(bit_unpacker.GetStatus());
}
const uint32_t num_read_bytes = tile_->input->GetNumDiscardedBytes() +
tile_->input->GetNumReadBytes() -
num_read_bytes_before;
if (num_read_bytes != GetTileMaxNumBytes(*gparams_, tile_->rect)) {
assert(false);
}
#if defined(WP2_BITTRACE)
if (config_->info != nullptr) {
WP2_CHECK_STATUS(tiles_layout_->assignment_lock.Acquire());
RegisterBitTrace(*config_, num_read_bytes, "BypassPixels");
tiles_layout_->assignment_lock.Release();
}
#endif
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
} // namespace WP2