blob: d504c2cc5324ece3779744948e12c36f24371148 [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.
// -----------------------------------------------------------------------------
//
// Functions related to animation decoding.
//
// Author: Yannis Guyon (yguyon@google.com)
#include <cassert>
#include "src/common/color_precision.h"
#include "src/common/header_enc_dec.h"
#include "src/dec/wp2_dec_i.h"
#include "src/utils/ans_utils.h"
#include "src/utils/data_source.h"
#include "src/wp2/base.h"
#include "src/wp2/decode.h"
#include "src/wp2/format_constants.h"
namespace WP2 {
//------------------------------------------------------------------------------
WP2Status DecodeANMF(const DecoderConfig& config, DataSource* const data_source,
const BitstreamFeatures& features, uint32_t frame_index,
AnimationFrame* const frame) {
if (features.is_animation) {
const size_t num_read_bytes_before = data_source->GetNumReadBytes();
BitUnpacker dec(data_source, "anmf_chunk");
const uint32_t tag = dec.ReadBits(kANMFTagNumBits, "anmf_tag");
frame->dispose = (tag == kANMFTagDispose);
frame->blend = (dec.ReadBits(1, "blend") == 1);
frame->is_last = (dec.ReadBits(1, "is_last") == 1);
// TODO(yguyon): Force read 'duration_ms>0' if kMaxNumPreframes || is_last
frame->duration_ms = dec.ReadVarUInt(0, kMaxFrameDurationMs, "duration");
frame->window =
dec.ReadRect(features.raw_width, features.raw_height, "window");
const WP2Status status =
dec.Pad() ? dec.GetStatus() : WP2_STATUS_BITSTREAM_ERROR;
if (status != WP2_STATUS_OK) {
// The data consumption must be atomic for incr dec glimpses.
data_source->UnmarkAllReadBytes();
data_source->MarkNumBytesAsRead(num_read_bytes_before);
}
WP2_CHECK_STATUS(status);
WP2_CHECK_OK((tag == kANMFTagDispose) || (tag == kANMFTagFrame),
WP2_STATUS_BITSTREAM_ERROR);
assert(frame->window.x + frame->window.width <= features.raw_width);
assert(frame->window.y + frame->window.height <= features.raw_height);
if (frame_index == 0) { // The first frame MUST dispose.
WP2_CHECK_OK(frame->dispose, WP2_STATUS_BITSTREAM_ERROR);
} else {
WP2_CHECK_OK(frame_index < kMaxNumFrames, WP2_STATUS_BITSTREAM_ERROR);
if (frame->duration_ms == 0) {
WP2_CHECK_OK(!frame->is_last, WP2_STATUS_BITSTREAM_ERROR);
}
}
WP2_CHECK_REDUCED_STATUS(
RegisterBitTrace(config,
data_source->GetNumReadBytes() - num_read_bytes_before, "ANMF"));
} else {
frame->dispose = true;
frame->blend = false;
frame->duration_ms = kMaxFrameDurationMs;
frame->window = {0, 0, features.raw_width, features.raw_height};
frame->is_last = true;
}
return WP2_STATUS_OK;
}
//------------------------------------------------------------------------------
// Fills the area outside of 'frame.window' with 'features.background_color' if
// 'frame.dispose' is true. The destination is the non-null buffer among
// 'rgb_output' and 'yuv_output'.
static WP2Status FillBorders(const BitstreamFeatures& features,
const AnimationFrame& frame,
ArgbBuffer* const rgb_output,
const CSPTransform* const csp_transform,
YUVPlane* const yuv_output) {
if (!frame.dispose) return WP2_STATUS_OK;
uint32_t width, height;
if (rgb_output != nullptr) {
WP2_CHECK_OK(yuv_output == nullptr, WP2_STATUS_INVALID_PARAMETER);
width = rgb_output->width();
height = rgb_output->height();
} else {
WP2_CHECK_OK(yuv_output != nullptr, WP2_STATUS_INVALID_PARAMETER);
width = yuv_output->GetWidth();
height = yuv_output->GetHeight();
}
const Rectangle& window = frame.window;
assert(window.x + window.width <= width);
assert(window.y + window.height <= height);
Rectangle top, bot, lft, rgt;
Rectangle(0, 0, width, height).Exclude(window, &top, &bot, &lft, &rgt);
if (rgb_output != nullptr) {
if (WP2Formatbpc(rgb_output->format()) <= 8) {
const Argb32b color = ToArgb32b(features.background_color);
rgb_output->Fill(top, color);
rgb_output->Fill(bot, color);
rgb_output->Fill(lft, color);
rgb_output->Fill(rgt, color);
} else {
rgb_output->Fill(top, features.background_color);
rgb_output->Fill(bot, features.background_color);
rgb_output->Fill(lft, features.background_color);
rgb_output->Fill(rgt, features.background_color);
}
} else {
WP2_CHECK_OK(csp_transform != nullptr, WP2_STATUS_INVALID_PARAMETER);
const Ayuv38b color =
csp_transform->ToYuv(ToArgb32b(features.background_color));
yuv_output->Fill(top, color);
yuv_output->Fill(bot, color);
yuv_output->Fill(lft, color);
yuv_output->Fill(rgt, color);
}
return WP2_STATUS_OK;
}
WP2Status FillBorders(const BitstreamFeatures& features,
const AnimationFrame& frame, ArgbBuffer* const output) {
return FillBorders(features, frame, output,
/*csp_transform=*/nullptr, /*yuv_output=*/nullptr);
}
WP2Status FillBorders(const BitstreamFeatures& features,
const AnimationFrame& frame,
const CSPTransform& csp_transform,
YUVPlane* const output) {
return FillBorders(features, frame, /*rgb_output=*/nullptr,
/*csp_transform=*/&csp_transform, /*yuv_output=*/output);
}
//------------------------------------------------------------------------------
} // namespace WP2