blob: b26a7573f9b8e2dabddec03d26406cf19730fae4 [file]
// Copyright 2022 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.
//
// Tests LZW-like algorithm implementation, both encoding and decoding.
#include <algorithm>
#include <cstdint>
#include <unordered_set>
#include "imageio/image_dec.h"
#include "src/dsp/math.h"
#include "src/utils/utils.h"
#include "src/wp2/base.h"
#include "src/wp2/decode.h"
#include "src/wp2/encode.h"
#include "src/wp2/format_constants.h"
#include "tests/include/helpers.h"
namespace WP2 {
namespace {
constexpr uint32_t kSegmentCapacity = 4000;
// Build artificial 3x3 RGB square for testing.
WP2Status BuildArgbBuffer(WP2::ArgbBuffer& buf) {
WP2_CHECK_STATUS(buf.SetFormat(WP2_ARGB_32));
constexpr uint32_t kSize = 3;
WP2_CHECK_STATUS(buf.Resize(kSize, kSize));
uint8_t color[4] = {0xFF, 0xFF, 0x00, 0x00};
buf.Fill({0, 0, 3, 1}, color);
color[2] = color[1];
color[1] = 0x00;
buf.Fill({0, 1, 3, 1}, color);
color[3] = color[2];
color[2] = 0x00;
buf.Fill({0, 2, 3, 1}, color);
return WP2_STATUS_OK;
}
WP2Status LZWEncodeDecode(const WP2::ArgbBuffer& input_buf,
WP2::ArgbBuffer& decoded_buf,
uint32_t segment_capacity = kSegmentCapacity) {
WP2MathInit();
MemoryWriter memory_writer;
EncoderConfig encoder_config;
encoder_config.quality = 100;
encoder_config.lossless_algorithm = WP2L::EncodingAlgorithm::kLZW;
encoder_config.keep_unmultiplied = true;
WP2_ASSERT_STATUS(Encode(input_buf, &memory_writer, encoder_config));
DecoderConfig decoder_config;
WP2_ASSERT_STATUS(Decode(memory_writer.mem_, memory_writer.size_,
&decoded_buf, decoder_config));
return WP2_STATUS_OK;
}
// Tests if the LZW-like algorithm correctly encodes and decodes a simple image.
TEST(LZW, LZWEncodeAndDecodeSimpleImage) {
ArgbBuffer input_buf(WP2_ARGB_32);
ASSERT_WP2_OK(BuildArgbBuffer(input_buf));
ArgbBuffer decoded_buf(WP2_ARGB_32);
ASSERT_WP2_OK(LZWEncodeDecode(input_buf, decoded_buf));
EXPECT_TRUE(
testutil::Compare(input_buf, decoded_buf, /*file_name=*/"input_file"));
}
// Tests if the LZW-like algorithm correctly encodes and decodes another image.
TEST(LZW, LZWEncodeAndDecodeNormalImage) {
ArgbBuffer input_buf(WP2_ARGB_32);
constexpr const char kInputFileName[] = "source1_64x48.png";
ASSERT_WP2_OK(
ReadImage(testutil::GetTestDataPath(kInputFileName).c_str(), &input_buf));
ArgbBuffer decoded_buf(WP2_ARGB_32);
ASSERT_WP2_OK(LZWEncodeDecode(input_buf, decoded_buf));
EXPECT_TRUE(testutil::Compare(input_buf, decoded_buf, kInputFileName));
}
// Tests if the LZW-like algorithm correctly encodes and decodes a large image.
TEST(LZW, LZWEncodeAndDecodeLargeImage) {
ArgbBuffer input_buf(WP2_ARGB_32);
constexpr const char kInputFileName[] = "large.png";
ASSERT_WP2_OK(
ReadImage(testutil::GetTestDataPath(kInputFileName).c_str(), &input_buf));
// Limit to kMaxPaletteSize colors per 256x256 tile.
for (uint32_t Y = 0; Y < input_buf.height(); Y += 256) {
for (uint32_t X = 0; X < input_buf.width(); X += 256) {
std::unordered_set<uint32_t> colors;
bool fill = false;
for (uint32_t y = Y; y < std::min(Y + 256, input_buf.height()); ++y) {
uint32_t* row = reinterpret_cast<uint32_t*>(input_buf.GetRow(y));
for (uint32_t x = X; x < std::min(X + 256, input_buf.width()); ++x) {
uint32_t* const pixel = row + x;
if (fill) {
*pixel = *colors.begin();
} else {
colors.insert(*pixel);
fill = colors.size() >= WP2L::kMaxPaletteSize;
}
}
}
// TODO(vrabaud) fix this.
// Make sure there are at least 2 colors for LZW as it needs a palette.
if (colors.size() == 1) {
reinterpret_cast<uint32_t*>(input_buf.GetRow(Y))[X] =
~reinterpret_cast<uint32_t*>(input_buf.GetRow(Y))[X];
}
}
}
ArgbBuffer decoded_buf(WP2_ARGB_32);
ASSERT_WP2_OK(LZWEncodeDecode(input_buf, decoded_buf));
EXPECT_TRUE(testutil::Compare(input_buf, decoded_buf, kInputFileName));
}
} // namespace
} // namespace WP2