blob: b89a9101252c79bdc993e2681713c1389024dd9b [file] [log] [blame] [edit]
// Copyright 2014 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <stdint.h>
#include <limits>
#include "core/fxcodec/jpx/cjpx_decoder.h"
#include "core/fxcodec/jpx/jpx_decode_utils.h"
#include "core/fxcrt/fx_memory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libopenjpeg/opj_malloc.h"
namespace fxcodec {
static const OPJ_OFF_T kSkipError = static_cast<OPJ_OFF_T>(-1);
static const OPJ_SIZE_T kReadError = static_cast<OPJ_SIZE_T>(-1);
static const uint8_t stream_data[] = {
0x00, 0x01, 0x02, 0x03,
0x84, 0x85, 0x86, 0x87, // Include some hi-bytes, too.
};
TEST(fxcodec, DecodeDataNullDecodeData) {
uint8_t buffer[16];
DecodeData* ptr = nullptr;
// Error codes, not segvs, should callers pass us a nullptr pointer.
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), ptr));
EXPECT_EQ(kSkipError, opj_skip_from_memory(1, ptr));
EXPECT_FALSE(opj_seek_from_memory(1, ptr));
}
TEST(fxcodec, DecodeDataNullStream) {
DecodeData dd(nullptr, 0);
uint8_t buffer[16];
// Reads of size 0 do nothing but return an error code.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
EXPECT_EQ(0xbd, buffer[0]);
// Reads of nonzero size do nothing but return an error code.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
EXPECT_EQ(0xbd, buffer[0]);
// Skips of size 0 always return an error code.
EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd));
// Skips of nonzero size always return an error code.
EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd));
// Seeks to 0 offset return in error.
EXPECT_FALSE(opj_seek_from_memory(0, &dd));
// Seeks to non-zero offsets return in error.
EXPECT_FALSE(opj_seek_from_memory(1, &dd));
}
TEST(fxcodec, DecodeDataZeroSize) {
DecodeData dd(stream_data, 0);
uint8_t buffer[16];
// Reads of size 0 do nothing but return an error code.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
EXPECT_EQ(0xbd, buffer[0]);
// Reads of nonzero size do nothing but return an error code.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, sizeof(buffer), &dd));
EXPECT_EQ(0xbd, buffer[0]);
// Skips of size 0 always return an error code.
EXPECT_EQ(kSkipError, opj_skip_from_memory(0, &dd));
// Skips of nonzero size always return an error code.
EXPECT_EQ(kSkipError, opj_skip_from_memory(1, &dd));
// Seeks to 0 offset return in error.
EXPECT_FALSE(opj_seek_from_memory(0, &dd));
// Seeks to non-zero offsets return in error.
EXPECT_FALSE(opj_seek_from_memory(1, &dd));
}
TEST(fxcodec, DecodeDataReadInBounds) {
uint8_t buffer[16];
{
DecodeData dd(stream_data, sizeof(stream_data));
// Exact sized read in a single call.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer), &dd));
EXPECT_EQ(0x00, buffer[0]);
EXPECT_EQ(0x01, buffer[1]);
EXPECT_EQ(0x02, buffer[2]);
EXPECT_EQ(0x03, buffer[3]);
EXPECT_EQ(0x84, buffer[4]);
EXPECT_EQ(0x85, buffer[5]);
EXPECT_EQ(0x86, buffer[6]);
EXPECT_EQ(0x87, buffer[7]);
EXPECT_EQ(0xbd, buffer[8]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Simple read.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(2u, opj_read_from_memory(buffer, 2, &dd));
EXPECT_EQ(0x00, buffer[0]);
EXPECT_EQ(0x01, buffer[1]);
EXPECT_EQ(0xbd, buffer[2]);
// Read of size 0 doesn't affect things.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(0u, opj_read_from_memory(buffer, 0, &dd));
EXPECT_EQ(0xbd, buffer[0]);
// Read exactly up to end of data.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
EXPECT_EQ(0x02, buffer[0]);
EXPECT_EQ(0x03, buffer[1]);
EXPECT_EQ(0x84, buffer[2]);
EXPECT_EQ(0x85, buffer[3]);
EXPECT_EQ(0x86, buffer[4]);
EXPECT_EQ(0x87, buffer[5]);
EXPECT_EQ(0xbd, buffer[6]);
// Read of size 0 at EOF is still an error.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 0, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
}
TEST(fxcodec, DecodeDataReadBeyondBounds) {
uint8_t buffer[16];
{
DecodeData dd(stream_data, sizeof(stream_data));
// Read beyond bounds in a single step.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(8u, opj_read_from_memory(buffer, sizeof(buffer) + 1, &dd));
EXPECT_EQ(0x00, buffer[0]);
EXPECT_EQ(0x01, buffer[1]);
EXPECT_EQ(0x02, buffer[2]);
EXPECT_EQ(0x03, buffer[3]);
EXPECT_EQ(0x84, buffer[4]);
EXPECT_EQ(0x85, buffer[5]);
EXPECT_EQ(0x86, buffer[6]);
EXPECT_EQ(0x87, buffer[7]);
EXPECT_EQ(0xbd, buffer[8]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Read well beyond bounds in a single step.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(8u, opj_read_from_memory(
buffer, std::numeric_limits<OPJ_SIZE_T>::max(), &dd));
EXPECT_EQ(0x00, buffer[0]);
EXPECT_EQ(0x01, buffer[1]);
EXPECT_EQ(0x02, buffer[2]);
EXPECT_EQ(0x03, buffer[3]);
EXPECT_EQ(0x84, buffer[4]);
EXPECT_EQ(0x85, buffer[5]);
EXPECT_EQ(0x86, buffer[6]);
EXPECT_EQ(0x87, buffer[7]);
EXPECT_EQ(0xbd, buffer[8]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Read of size 6 gets first 6 bytes.
// rest of buffer intact.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(6u, opj_read_from_memory(buffer, 6, &dd));
EXPECT_EQ(0x00, buffer[0]);
EXPECT_EQ(0x01, buffer[1]);
EXPECT_EQ(0x02, buffer[2]);
EXPECT_EQ(0x03, buffer[3]);
EXPECT_EQ(0x84, buffer[4]);
EXPECT_EQ(0x85, buffer[5]);
EXPECT_EQ(0xbd, buffer[6]);
// Read of size 6 gets remaining two bytes.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(2u, opj_read_from_memory(buffer, 6, &dd));
EXPECT_EQ(0x86, buffer[0]);
EXPECT_EQ(0x87, buffer[1]);
EXPECT_EQ(0xbd, buffer[2]);
// Read of 6 more gets nothing and leaves rest of buffer intact.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 6, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
}
// Note: Some care needs to be taken here because the skip/seek functions
// take OPJ_OFF_T's as arguments, which are typically a signed type.
TEST(fxcodec, DecodeDataSkip) {
uint8_t buffer[16];
{
DecodeData dd(stream_data, sizeof(stream_data));
// Skiping within buffer is allowed.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(1u, opj_skip_from_memory(1, &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x01, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Skiping 0 bytes changes nothing.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(0, opj_skip_from_memory(0, &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x02, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Skiping to EOS-1 is possible.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x87, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Next read fails.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Skiping directly to EOS is allowed.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
// Next read fails.
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Skipping beyond end of stream is allowed and returns full distance.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(9u, opj_skip_from_memory(9, &dd));
// Next read fails.
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Skipping way beyond EOS is allowd, doesn't wrap, and returns
// full distance.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
EXPECT_EQ(std::numeric_limits<OPJ_OFF_T>::max(),
opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
// Next read fails. If it succeeds, it may mean we wrapped.
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Negative skip within buffer not is allowed, position unchanged.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
EXPECT_EQ(kSkipError, opj_skip_from_memory(-2, &dd));
// Next read succeeds as if nothing has happenned.
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x84, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Negative skip before buffer is not allowed, position unchanged.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
// Next read succeeds as if nothing has happenned.
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x85, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Negative skip way before buffer is not allowed, doesn't wrap
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(4u, opj_skip_from_memory(4, &dd));
EXPECT_EQ(kSkipError,
opj_skip_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
// Next read succeeds. If it fails, it may mean we wrapped.
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x84, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
}
{
DecodeData dd(stream_data, sizeof(stream_data));
// Negative skip after EOS isn't alowed, still EOS.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_EQ(8u, opj_skip_from_memory(8, &dd));
EXPECT_EQ(kSkipError, opj_skip_from_memory(-4, &dd));
// Next read fails.
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
}
TEST(fxcodec, DecodeDataSeek) {
uint8_t buffer[16];
DecodeData dd(stream_data, sizeof(stream_data));
// Seeking within buffer is allowed and read succeeds
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_TRUE(opj_seek_from_memory(1, &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x01, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Seeking before start returns error leaving position unchanged.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_FALSE(opj_seek_from_memory(-1, &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x02, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Seeking way before start returns error leaving position unchanged.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_FALSE(
opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::min(), &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x03, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Seeking exactly to EOS is allowed but read fails.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_TRUE(opj_seek_from_memory(8, &dd));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
// Seeking back to zero offset is allowed and read succeeds.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_TRUE(opj_seek_from_memory(0, &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x00, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Seeking beyond end of stream is allowed but read fails.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_TRUE(opj_seek_from_memory(16, &dd));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
// Seeking within buffer after seek past EOF restores good state.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_TRUE(opj_seek_from_memory(4, &dd));
EXPECT_EQ(1u, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0x84, buffer[0]);
EXPECT_EQ(0xbd, buffer[1]);
// Seeking way beyond EOS is allowed, doesn't wrap, and read fails.
memset(buffer, 0xbd, sizeof(buffer));
EXPECT_TRUE(opj_seek_from_memory(std::numeric_limits<OPJ_OFF_T>::max(), &dd));
EXPECT_EQ(kReadError, opj_read_from_memory(buffer, 1, &dd));
EXPECT_EQ(0xbd, buffer[0]);
}
TEST(fxcodec, YUV420ToRGB) {
opj_image_comp_t u;
memset(&u, 0, sizeof(u));
u.dx = 1;
u.dy = 1;
u.w = 16;
u.h = 16;
u.prec = 8;
u.bpp = 8;
opj_image_comp_t v;
memset(&v, 0, sizeof(v));
v.dx = 1;
v.dy = 1;
v.w = 16;
v.h = 16;
v.prec = 8;
v.bpp = 8;
opj_image_comp_t y;
memset(&y, 0, sizeof(y));
y.dx = 1;
y.dy = 1;
y.prec = 8;
y.bpp = 8;
opj_image_t img;
memset(&img, 0, sizeof(img));
img.numcomps = 3;
img.color_space = OPJ_CLRSPC_SYCC;
img.comps = FX_Alloc(opj_image_comp_t, 3);
const struct {
OPJ_UINT32 w;
bool expected;
} cases[] = {{0, false}, {1, false}, {30, false}, {31, true},
{32, true}, {33, false}, {34, false}, {UINT_MAX, false}};
for (const auto& testcase : cases) {
y.w = testcase.w;
y.h = y.w;
img.x1 = y.w;
img.y1 = y.h;
y.data = static_cast<OPJ_INT32*>(
opj_image_data_alloc(y.w * y.h * sizeof(OPJ_INT32)));
v.data = static_cast<OPJ_INT32*>(
opj_image_data_alloc(v.w * v.h * sizeof(OPJ_INT32)));
u.data = static_cast<OPJ_INT32*>(
opj_image_data_alloc(u.w * u.h * sizeof(OPJ_INT32)));
memset(y.data, 1, y.w * y.h * sizeof(OPJ_INT32));
memset(u.data, 0, u.w * u.h * sizeof(OPJ_INT32));
memset(v.data, 0, v.w * v.h * sizeof(OPJ_INT32));
img.comps[0] = y;
img.comps[1] = u;
img.comps[2] = v;
CJPX_Decoder::Sycc420ToRgbForTesting(&img);
if (testcase.expected) {
EXPECT_EQ(img.comps[0].w, img.comps[1].w);
EXPECT_EQ(img.comps[0].h, img.comps[1].h);
EXPECT_EQ(img.comps[0].w, img.comps[2].w);
EXPECT_EQ(img.comps[0].h, img.comps[2].h);
} else {
EXPECT_NE(img.comps[0].w, img.comps[1].w);
EXPECT_NE(img.comps[0].h, img.comps[1].h);
EXPECT_NE(img.comps[0].w, img.comps[2].w);
EXPECT_NE(img.comps[0].h, img.comps[2].h);
}
opj_image_data_free(img.comps[0].data);
opj_image_data_free(img.comps[1].data);
opj_image_data_free(img.comps[2].data);
}
FX_Free(img.comps);
}
} // namespace fxcodec