blob: aefd24cccc7067339df7411186dad851f2c4a55a [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
//
// http://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.
// -----------------------------------------------------------------------------
//
// Fuzzing of animation wp2 encoding settings.
//
// Author: Yannis Guyon (yguyon@google.com)
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
#include "src/utils/orientation.h"
#include "src/wp2/base.h"
#include "src/wp2/decode.h"
#include "src/wp2/encode.h"
#include "tests/fuzz/fuzz_utils.h"
#include "tests/include/helpers.h"
namespace WP2 {
namespace testutil {
int TestAnimEncConfig(const std::vector<ArgbBuffer>& original_frames,
const std::vector<uint32_t>& durations_ms,
bool loop_forever, const EncoderConfig& encoder_config,
float expected_distortion) {
// std::cerr << encoder_config << std::endl; // Uncomment to print config
if (!encoder_config.IsValid()) abort();
if (original_frames.empty()) return 0;
// Add frames to the encoder.
AnimationEncoder animation_encoder;
for (size_t i = 0; i < original_frames.size(); ++i) {
if (animation_encoder.AddFrame(original_frames[i], durations_ms[i]) !=
WP2_STATUS_OK) {
abort();
}
}
// Encode with a reasonable config, without diving into detailed settings.
MemoryWriter memory_writer;
const WP2Status encoder_status =
animation_encoder.Encode(&memory_writer, encoder_config, loop_forever);
if (encoder_status != WP2_STATUS_OK) {
if (encoder_config.progress_hook != nullptr &&
encoder_status == WP2_STATUS_USER_ABORT) {
return 0;
}
abort();
}
// Verify that it decodes fine and that the result is close enough.
ArgbBuffer decoded(original_frames.front().format());
ArgbBuffer decoded_unoriented(original_frames.front().format());
DecoderConfig decoder_config = DecoderConfig::kDefault;
// AnimationEncoder does not take EncoderConfig::exact into account.
// It encodes the input frames in their original premul/unpremul state.
// The decoder has no way to know that, so it must be told.
decoder_config.exact = !WP2IsPremultiplied(original_frames.front().format());
ArrayDecoder decoder(memory_writer.mem_, memory_writer.size_, decoder_config,
&decoded);
for (size_t i = 0; i < original_frames.size(); ++i) {
uint32_t duration_ms;
if (!decoder.ReadFrame(&duration_ms)) abort();
assert(!decoder.Failed());
if (duration_ms != durations_ms[i]) abort();
if (expected_distortion > 0.f) {
// 'decoded' can't be modified as long as 'decoder' is used, so
// 'decoded_unoriented' is compared instead.
if (encoder_config.decoding_orientation == Orientation::kOriginal) {
if (decoded_unoriented.SetView(decoded) != WP2_STATUS_OK) {
abort();
}
} else if (OrientateBuffer(
GetInverseOrientation(encoder_config.decoding_orientation),
decoded, &decoded_unoriented) != WP2_STATUS_OK) {
abort();
}
// Tiny frames are not interesting to compare.
if (original_frames[i].width() >= 8 && original_frames[i].height() >= 8 &&
!Compare(original_frames[i], decoded_unoriented, "anim",
expected_distortion)) {
// Uncomment to save the original and decoded frames as PNG files.
// SaveBeforeAfter(original_frames[i], decoded_unoriented,
// "/tmp/comparison_" + std::to_string(i));
abort();
}
}
}
if (decoder.GetStatus() != WP2_STATUS_OK) abort();
return 0;
}
} // namespace testutil
} // namespace WP2