blob: c001a758ad1e7baf156aa396c494a9d75cb9f533 [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 <cassert>
#include <cstdint>
#include <cstring>
#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/filters/block_map_filter.h"
#include "src/dec/tile_dec.h"
#include "src/dsp/math.h"
#include "src/utils/data_source.h"
#include "src/utils/utils.h"
#include "src/wp2/base.h"
#include "src/wp2/format_constants.h"
#if defined(WP2_BITTRACE)
#include "src/dec/wp2_dec_i.h"
#endif
namespace WP2 {
//------------------------------------------------------------------------------
WP2Status TileDecoder::BypassTileDec() {
const uint32_t num_read_bytes_before =
tile_->input->GetNumDiscardedBytes() + tile_->input->GetNumReadBytes();
if (gparams_->type_ == GlobalParams::GP_LOSSLESS ||
gparams_->type_ == GlobalParams::GP_AV1) {
tile_->output_is_yuv = false;
assert(!tile_->rgb_output.IsEmpty());
if (features_->rgb_bit_depth == 8) {
WP2_CHECK_STATUS(BypassTileDecRgb8b());
} else {
assert(features_->rgb_bit_depth == 10);
WP2_CHECK_STATUS(BypassTileDecRgb10b());
}
} else {
tile_->output_is_yuv = true;
assert(!tile_->yuv_output.IsEmpty());
WP2_CHECK_STATUS(BypassTileDecYuv());
}
const uint32_t num_read_bytes = tile_->input->GetNumDiscardedBytes() +
tile_->input->GetNumReadBytes() -
num_read_bytes_before;
(void)num_read_bytes;
#if !defined(WP2_ENC_DEC_MATCH)
assert(num_read_bytes ==
GetTileMaxNumBytes(features_->rgb_bit_depth, *gparams_, tile_->rect));
#endif
#if defined(WP2_BITTRACE)
if (config_->info != nullptr) {
WP2_CHECK_STATUS(tiles_layout_->assignment_lock.Acquire());
WP2_CHECK_STATUS(
RegisterBitTrace(*config_, num_read_bytes, "BypassPixels"));
tiles_layout_->assignment_lock.Release();
}
#endif
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
WP2Status TileDecoder::BypassTileDecRgb8b() {
const uint32_t bytes_per_row =
tile_->rect.width * (gparams_->has_alpha_ ? 4 : 3);
#if !defined(WP2_ENC_DEC_MATCH)
assert(bytes_per_row * tile_->rect.height ==
GetTileMaxNumBytes(features_->rgb_bit_depth, *gparams_, tile_->rect));
#endif
for (tile_->num_decoded_rows = 0;
tile_->num_decoded_rows < tile_->rect.height;
++tile_->num_decoded_rows) {
const uint32_t y = tile_->num_decoded_rows;
DataSource::DataHandle handle;
WP2_CHECK_OK(tile_->input->TryReadNext(bytes_per_row, &handle),
WP2_STATUS_NOT_ENOUGH_DATA);
if (tile_->rgb_output.format() == WP2_Argb_32 ||
tile_->rgb_output.format() == WP2_ARGB_32) {
uint8_t* const row = tile_->rgb_output.GetRow8(y);
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;
std::memcpy(row + x * 4 + 1, handle.GetBytes() + x * 3, 3);
}
}
} else {
assert(tile_->rgb_output.format() == WP2_Argb_38);
uint16_t* const row = tile_->rgb_output.GetRow16(y);
if (gparams_->has_alpha_) {
for (uint32_t x = 0; x < tile_->rect.width; ++x) {
row[x * 4 + 0] = handle.GetBytes()[x * 4];
const uint8_t* const rgb = handle.GetBytes() + x * 4 + 1;
for (uint32_t i : {0, 1, 2}) {
row[x * 4 + 1 + i] =
ChangePrecision<uint16_t>(rgb[i], /*from=*/8, /*to=*/10);
}
}
} else {
for (uint32_t x = 0; x < tile_->rect.width; ++x) {
row[x * 4 + 0] = kAlphaMax;
const uint8_t* const rgb = handle.GetBytes() + x * 3;
for (uint32_t i : {0, 1, 2}) {
row[x * 4 + 1 + i] =
ChangePrecision<uint16_t>(rgb[i], /*from=*/8, /*to=*/10);
}
}
}
}
WP2_CHECK_STATUS(tile_->row_progress.AdvanceBy(1.));
}
return WP2_STATUS_OK;
}
WP2Status TileDecoder::BypassTileDecRgb10b() {
BitUnpacker bit_unpacker(tile_->input, "raw_pixels");
const uint32_t num_bits_per_row =
((gparams_->has_alpha_ ? kAlphaBits : 0) + 3 * 10) * tile_->rect.width;
for (tile_->num_decoded_rows = 0;
tile_->num_decoded_rows < tile_->rect.height;
++tile_->num_decoded_rows) {
// Make sure enough data is fetched to read an entire row.
// Otherwise the decoding might be suspended in the middle of a row and the
// output buffer invalidated just before resuming in the middle of a pixel.
WP2_CHECK_OK(bit_unpacker.Prefetch(num_bits_per_row),
WP2_STATUS_NOT_ENOUGH_DATA);
const uint32_t y = tile_->num_decoded_rows;
if (tile_->rgb_output.format() == WP2_Argb_32) {
uint8_t* const row = tile_->rgb_output.GetRow8(y);
for (uint32_t x = 0; x < tile_->rect.width; ++x) {
row[x * 4 + 0] = gparams_->has_alpha_
? bit_unpacker.ReadBits(kAlphaBits, "alpha")
: kAlphaMax;
row[x * 4 + 1] = ChangePrecision(bit_unpacker.ReadBits(10, "red"),
/*from=*/10, /*to=*/8);
row[x * 4 + 2] = ChangePrecision(bit_unpacker.ReadBits(10, "green"),
/*from=*/10, /*to=*/8);
row[x * 4 + 3] = ChangePrecision(bit_unpacker.ReadBits(10, "blue"),
/*from=*/10, /*to=*/8);
}
} else {
assert(tile_->rgb_output.format() == WP2_Argb_38);
uint16_t* const row = tile_->rgb_output.GetRow16(y);
for (uint32_t x = 0; x < tile_->rect.width; ++x) {
row[x * 4 + 0] = gparams_->has_alpha_
? bit_unpacker.ReadBits(kAlphaBits, "alpha")
: kAlphaMax;
row[x * 4 + 1] = bit_unpacker.ReadBits(10, "red");
row[x * 4 + 2] = bit_unpacker.ReadBits(10, "green");
row[x * 4 + 3] = bit_unpacker.ReadBits(10, "blue");
}
}
WP2_CHECK_STATUS(tile_->row_progress.AdvanceBy(1.));
}
WP2_CHECK_OK(bit_unpacker.Pad(), WP2_STATUS_BITSTREAM_ERROR);
WP2_CHECK_STATUS(bit_unpacker.GetStatus());
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
WP2Status TileDecoder::BypassTileDecYuv() {
tile_->block_map.Init(tile_->rect, gparams_->transf_.GetYuvDepth(),
gparams_->transf_.GetYUVMin(),
gparams_->transf_.GetYUVMax(), &tile_->yuv_output);
if (IsFilterBlockMapNeeded(*config_, *gparams_, *tiles_layout_)) {
WP2_CHECK_STATUS(tile_->block_map.Allocate());
// Simulate a grid of 4x4 blocks for the IntertileFilter settings.
// Even though this is lossless it is likely to border a lossy tile which
// might benefit from slight deblocking.
// This must be done before 'tile_->num_decoded_rows' is increased because
// the 'tile_->block_map' data may be accessed for each available line.
constexpr BlockSize kSize = BLK_4x4;
const uint32_t block_height = BlockHeightPix(kSize);
const uint32_t block_width = BlockWidthPix(kSize);
constexpr uint32_t kSegmentId = 0; // Least quantized one.
constexpr uint8_t kMinBpp = 255; // Max accepted ~2bpp.
constexpr uint32_t kResDen[4] = {255, 255, 255, 255}; // Full residuals.
for (uint32_t y = 0; y < tile_->rect.height; y += block_height) {
for (uint32_t x = 0; x < tile_->rect.width; x += block_width) {
// Set segment id as the least quantized one.
tile_->block_map.RegisterBlock(
x / kMinBlockSizePix, y / kMinBlockSizePix, kSize, kSegmentId,
kMinBpp, /*has_lossy_alpha=*/false, kResDen);
}
}
}
const uint32_t num_bits = gparams_->transf_.GetYuvDepth().num_bits;
const uint32_t num_bits_per_row =
(gparams_->has_alpha_ ? kAlphaBits : 0) + 3 * num_bits;
BitUnpacker bit_unpacker(tile_->input, "raw_pixels");
if (!gparams_->has_alpha_ && !tile_->yuv_output.A.IsEmpty()) {
tile_->yuv_output.A.Fill(kAlphaMax);
}
for (tile_->num_decoded_rows = 0;
tile_->num_decoded_rows < tile_->rect.height;
++tile_->num_decoded_rows) {
// Make sure enough data is fetched to read an entire row.
// Otherwise the decoding might be suspended in the middle of a row and the
// output buffer invalidated just before resuming in the middle of a pixel.
WP2_CHECK_OK(bit_unpacker.Prefetch(num_bits_per_row),
WP2_STATUS_NOT_ENOUGH_DATA);
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");
}
}
WP2_CHECK_STATUS(tile_->row_progress.AdvanceBy(1.));
}
WP2_CHECK_STATUS(bit_unpacker.GetStatus());
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
} // namespace WP2