blob: 05c0148ee35f59f4c07bbbf270c5a670bb760a64 [file] [log] [blame]
// Copyright 2017 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "core/fpdfapi/page/cpdf_sampledfunc.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_stream_acc.h"
#include "core/fxcrt/cfx_bitstream.h"
#include "core/fxcrt/cfx_fixedbufgrow.h"
#include "core/fxcrt/fx_safe_types.h"
#include "third_party/base/stl_util.h"
namespace {
// See PDF Reference 1.7, page 170, table 3.36.
bool IsValidBitsPerSample(uint32_t x) {
switch (x) {
case 1:
case 2:
case 4:
case 8:
case 12:
case 16:
case 24:
case 32:
return true;
default:
return false;
}
}
} // namespace
CPDF_SampledFunc::CPDF_SampledFunc() : CPDF_Function(Type::kType0Sampled) {}
CPDF_SampledFunc::~CPDF_SampledFunc() = default;
bool CPDF_SampledFunc::v_Init(const CPDF_Object* pObj,
std::set<const CPDF_Object*>* pVisited) {
const CPDF_Stream* pStream = pObj->AsStream();
if (!pStream)
return false;
const CPDF_Dictionary* pDict = pStream->GetDict();
const CPDF_Array* pSize = pDict->GetArrayFor("Size");
if (!pSize || pSize->IsEmpty())
return false;
m_nBitsPerSample = pDict->GetIntegerFor("BitsPerSample");
if (!IsValidBitsPerSample(m_nBitsPerSample))
return false;
FX_SAFE_UINT32 nTotalSampleBits = m_nBitsPerSample;
nTotalSampleBits *= m_nOutputs;
const CPDF_Array* pEncode = pDict->GetArrayFor("Encode");
m_EncodeInfo.resize(m_nInputs);
for (uint32_t i = 0; i < m_nInputs; i++) {
int size = pSize->GetIntegerAt(i);
if (size <= 0)
return false;
m_EncodeInfo[i].sizes = size;
nTotalSampleBits *= m_EncodeInfo[i].sizes;
if (pEncode) {
m_EncodeInfo[i].encode_min = pEncode->GetNumberAt(i * 2);
m_EncodeInfo[i].encode_max = pEncode->GetNumberAt(i * 2 + 1);
} else {
m_EncodeInfo[i].encode_min = 0;
m_EncodeInfo[i].encode_max =
m_EncodeInfo[i].sizes == 1 ? 1 : m_EncodeInfo[i].sizes - 1;
}
}
FX_SAFE_UINT32 nTotalSampleBytes = (nTotalSampleBits + 7) / 8;
if (!nTotalSampleBytes.IsValid() || nTotalSampleBytes.ValueOrDie() == 0)
return false;
m_SampleMax = 0xffffffff >> (32 - m_nBitsPerSample);
m_pSampleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pStream);
m_pSampleStream->LoadAllDataFiltered();
if (nTotalSampleBytes.ValueOrDie() > m_pSampleStream->GetSize())
return false;
const CPDF_Array* pDecode = pDict->GetArrayFor("Decode");
m_DecodeInfo.resize(m_nOutputs);
for (uint32_t i = 0; i < m_nOutputs; i++) {
if (pDecode) {
m_DecodeInfo[i].decode_min = pDecode->GetNumberAt(2 * i);
m_DecodeInfo[i].decode_max = pDecode->GetNumberAt(2 * i + 1);
} else {
m_DecodeInfo[i].decode_min = m_Ranges[i * 2];
m_DecodeInfo[i].decode_max = m_Ranges[i * 2 + 1];
}
}
return true;
}
bool CPDF_SampledFunc::v_Call(const float* inputs, float* results) const {
int pos = 0;
CFX_FixedBufGrow<float, 16> encoded_input_buf(m_nInputs);
float* encoded_input = encoded_input_buf;
CFX_FixedBufGrow<uint32_t, 32> int_buf(m_nInputs * 2);
uint32_t* index = int_buf;
uint32_t* blocksize = index + m_nInputs;
for (uint32_t i = 0; i < m_nInputs; i++) {
if (i == 0)
blocksize[i] = 1;
else
blocksize[i] = blocksize[i - 1] * m_EncodeInfo[i - 1].sizes;
encoded_input[i] =
Interpolate(inputs[i], m_Domains[i * 2], m_Domains[i * 2 + 1],
m_EncodeInfo[i].encode_min, m_EncodeInfo[i].encode_max);
index[i] = pdfium::clamp(static_cast<uint32_t>(encoded_input[i]), 0U,
m_EncodeInfo[i].sizes - 1);
pos += index[i] * blocksize[i];
}
FX_SAFE_INT32 bits_to_output = m_nOutputs;
bits_to_output *= m_nBitsPerSample;
if (!bits_to_output.IsValid())
return false;
int bits_to_skip;
{
FX_SAFE_INT32 bitpos = pos;
bitpos *= bits_to_output.ValueOrDie();
bits_to_skip = bitpos.ValueOrDefault(-1);
if (bits_to_skip < 0)
return false;
FX_SAFE_INT32 range_check = bitpos;
range_check += bits_to_output.ValueOrDie();
if (!range_check.IsValid())
return false;
}
pdfium::span<const uint8_t> pSampleData = m_pSampleStream->GetSpan();
if (pSampleData.empty())
return false;
CFX_BitStream bitstream(pSampleData);
bitstream.SkipBits(bits_to_skip);
for (uint32_t i = 0; i < m_nOutputs; ++i) {
uint32_t sample = bitstream.GetBits(m_nBitsPerSample);
float encoded = sample;
for (uint32_t j = 0; j < m_nInputs; ++j) {
if (index[j] == m_EncodeInfo[j].sizes - 1) {
if (index[j] == 0)
encoded = encoded_input[j] * sample;
} else {
FX_SAFE_INT32 bitpos2 = blocksize[j];
bitpos2 += pos;
bitpos2 *= m_nOutputs;
bitpos2 += i;
bitpos2 *= m_nBitsPerSample;
int bits_to_skip2 = bitpos2.ValueOrDefault(-1);
if (bits_to_skip2 < 0)
return false;
CFX_BitStream bitstream2(pSampleData);
bitstream2.SkipBits(bits_to_skip2);
float sample2 =
static_cast<float>(bitstream2.GetBits(m_nBitsPerSample));
encoded += (encoded_input[j] - index[j]) * (sample2 - sample);
}
}
results[i] =
Interpolate(encoded, 0, m_SampleMax, m_DecodeInfo[i].decode_min,
m_DecodeInfo[i].decode_max);
}
return true;
}
#if defined(_SKIA_SUPPORT_) || defined(_SKIA_SUPPORT_PATHS_)
RetainPtr<CPDF_StreamAcc> CPDF_SampledFunc::GetSampleStream() const {
return m_pSampleStream;
}
#endif