| // 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/fxcrt/fx_safe_types.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() {} |
| |
| bool CPDF_SampledFunc::v_Init(CPDF_Object* pObj) { |
| CPDF_Stream* pStream = pObj->AsStream(); |
| if (!pStream) |
| return false; |
| |
| CPDF_Dictionary* pDict = pStream->GetDict(); |
| CPDF_Array* pSize = pDict->GetArrayFor("Size"); |
| CPDF_Array* pEncode = pDict->GetArrayFor("Encode"); |
| CPDF_Array* pDecode = pDict->GetArrayFor("Decode"); |
| m_nBitsPerSample = pDict->GetIntegerFor("BitsPerSample"); |
| if (!IsValidBitsPerSample(m_nBitsPerSample)) |
| return false; |
| |
| m_SampleMax = 0xffffffff >> (32 - m_nBitsPerSample); |
| m_pSampleStream = pdfium::MakeRetain<CPDF_StreamAcc>(pStream); |
| m_pSampleStream->LoadAllData(false); |
| FX_SAFE_UINT32 nTotalSampleBits = 1; |
| m_EncodeInfo.resize(m_nInputs); |
| for (uint32_t i = 0; i < m_nInputs; i++) { |
| m_EncodeInfo[i].sizes = pSize ? pSize->GetIntegerAt(i) : 0; |
| if (!pSize && i == 0) |
| m_EncodeInfo[i].sizes = pDict->GetIntegerFor("Size"); |
| nTotalSampleBits *= m_EncodeInfo[i].sizes; |
| if (pEncode) { |
| m_EncodeInfo[i].encode_min = pEncode->GetFloatAt(i * 2); |
| m_EncodeInfo[i].encode_max = pEncode->GetFloatAt(i * 2 + 1); |
| } else { |
| m_EncodeInfo[i].encode_min = 0; |
| m_EncodeInfo[i].encode_max = |
| m_EncodeInfo[i].sizes == 1 ? 1 : (float)m_EncodeInfo[i].sizes - 1; |
| } |
| } |
| nTotalSampleBits *= m_nBitsPerSample; |
| nTotalSampleBits *= m_nOutputs; |
| FX_SAFE_UINT32 nTotalSampleBytes = nTotalSampleBits; |
| nTotalSampleBytes += 7; |
| nTotalSampleBytes /= 8; |
| if (!nTotalSampleBytes.IsValid() || nTotalSampleBytes.ValueOrDie() == 0 || |
| nTotalSampleBytes.ValueOrDie() > m_pSampleStream->GetSize()) { |
| return false; |
| } |
| m_DecodeInfo.resize(m_nOutputs); |
| for (uint32_t i = 0; i < m_nOutputs; i++) { |
| if (pDecode) { |
| m_DecodeInfo[i].decode_min = pDecode->GetFloatAt(2 * i); |
| m_DecodeInfo[i].decode_max = pDecode->GetFloatAt(2 * i + 1); |
| } else { |
| m_DecodeInfo[i].decode_min = m_pRanges[i * 2]; |
| m_DecodeInfo[i].decode_max = m_pRanges[i * 2 + 1]; |
| } |
| } |
| return true; |
| } |
| |
| bool CPDF_SampledFunc::v_Call(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_pDomains[i * 2], m_pDomains[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; |
| |
| FX_SAFE_INT32 bitpos = pos; |
| bitpos *= bits_to_output.ValueOrDie(); |
| if (!bitpos.IsValid()) |
| return false; |
| |
| FX_SAFE_INT32 range_check = bitpos; |
| range_check += bits_to_output.ValueOrDie(); |
| if (!range_check.IsValid()) |
| return false; |
| |
| const uint8_t* pSampleData = m_pSampleStream->GetData(); |
| if (!pSampleData) |
| return false; |
| |
| for (uint32_t j = 0; j < m_nOutputs; j++, bitpos += m_nBitsPerSample) { |
| uint32_t sample = |
| GetBits32(pSampleData, bitpos.ValueOrDie(), m_nBitsPerSample); |
| float encoded = (float)sample; |
| for (uint32_t i = 0; i < m_nInputs; i++) { |
| if (index[i] == m_EncodeInfo[i].sizes - 1) { |
| if (index[i] == 0) |
| encoded = encoded_input[i] * (float)sample; |
| } else { |
| FX_SAFE_INT32 bitpos2 = blocksize[i]; |
| bitpos2 += pos; |
| bitpos2 *= m_nOutputs; |
| bitpos2 += j; |
| bitpos2 *= m_nBitsPerSample; |
| if (!bitpos2.IsValid()) |
| return false; |
| uint32_t sample1 = |
| GetBits32(pSampleData, bitpos2.ValueOrDie(), m_nBitsPerSample); |
| encoded += |
| (encoded_input[i] - index[i]) * ((float)sample1 - (float)sample); |
| } |
| } |
| results[j] = |
| Interpolate(encoded, 0, (float)m_SampleMax, m_DecodeInfo[j].decode_min, |
| m_DecodeInfo[j].decode_max); |
| } |
| return true; |
| } |