blob: 0faabce8931fccfcbd7b6e070a99bd0597fe6997 [file] [log] [blame]
// 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.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "core/fpdfapi/page/cpdf_devicecs.h"
#include <algorithm>
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_stream_acc.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fxcodec/fx_codec.h"
#include "core/fxge/dib/cfx_cmyk_to_srgb.h"
#include "third_party/base/check.h"
#include "third_party/base/notreached.h"
namespace {
float NormalizeChannel(float fVal) {
return std::clamp(fVal, 0.0f, 1.0f);
}
} // namespace
CPDF_DeviceCS::CPDF_DeviceCS(Family family) : CPDF_ColorSpace(family) {
DCHECK(family == Family::kDeviceGray || family == Family::kDeviceRGB ||
family == Family::kDeviceCMYK);
SetComponentsForStockCS(ComponentsForFamily(GetFamily()));
}
CPDF_DeviceCS::~CPDF_DeviceCS() = default;
uint32_t CPDF_DeviceCS::v_Load(CPDF_Document* pDoc,
const CPDF_Array* pArray,
std::set<const CPDF_Object*>* pVisited) {
// Unlike other classes that inherit from CPDF_ColorSpace, CPDF_DeviceCS is
// never loaded by CPDF_ColorSpace.
NOTREACHED_NORETURN();
}
bool CPDF_DeviceCS::GetRGB(pdfium::span<const float> pBuf,
float* R,
float* G,
float* B) const {
switch (GetFamily()) {
case Family::kDeviceGray:
*R = NormalizeChannel(pBuf[0]);
*G = *R;
*B = *R;
return true;
case Family::kDeviceRGB:
*R = NormalizeChannel(pBuf[0]);
*G = NormalizeChannel(pBuf[1]);
*B = NormalizeChannel(pBuf[2]);
return true;
case Family::kDeviceCMYK:
if (IsStdConversionEnabled()) {
float k = pBuf[3];
*R = 1.0f - std::min(1.0f, pBuf[0] + k);
*G = 1.0f - std::min(1.0f, pBuf[1] + k);
*B = 1.0f - std::min(1.0f, pBuf[2] + k);
} else {
std::tie(*R, *G, *B) = AdobeCMYK_to_sRGB(
NormalizeChannel(pBuf[0]), NormalizeChannel(pBuf[1]),
NormalizeChannel(pBuf[2]), NormalizeChannel(pBuf[3]));
}
return true;
default:
NOTREACHED_NORETURN();
}
}
void CPDF_DeviceCS::TranslateImageLine(pdfium::span<uint8_t> dest_span,
pdfium::span<const uint8_t> src_span,
int pixels,
int image_width,
int image_height,
bool bTransMask) const {
uint8_t* pDestBuf = dest_span.data();
const uint8_t* pSrcBuf = src_span.data();
switch (GetFamily()) {
case Family::kDeviceGray:
for (int i = 0; i < pixels; i++) {
// Compiler can not conclude that src/dest don't overlap, avoid
// duplicate loads.
const uint8_t pix = pSrcBuf[i];
*pDestBuf++ = pix;
*pDestBuf++ = pix;
*pDestBuf++ = pix;
}
break;
case Family::kDeviceRGB:
fxcodec::ReverseRGB(pDestBuf, pSrcBuf, pixels);
break;
case Family::kDeviceCMYK:
if (bTransMask) {
for (int i = 0; i < pixels; i++) {
// Compiler can't conclude src/dest don't overlap, avoid interleaved
// loads and stores.
const uint8_t s0 = pSrcBuf[0];
const uint8_t s1 = pSrcBuf[1];
const uint8_t s2 = pSrcBuf[2];
const int k = 255 - pSrcBuf[3];
pDestBuf[0] = ((255 - s0) * k) / 255;
pDestBuf[1] = ((255 - s1) * k) / 255;
pDestBuf[2] = ((255 - s2) * k) / 255;
pDestBuf += 3;
pSrcBuf += 4;
}
} else {
if (IsStdConversionEnabled()) {
for (int i = 0; i < pixels; i++) {
// Compiler can't conclude src/dest don't overlap, avoid
// interleaved loads and stores.
const uint8_t s0 = pSrcBuf[0];
const uint8_t s1 = pSrcBuf[1];
const uint8_t s2 = pSrcBuf[2];
const uint8_t k = pSrcBuf[3];
pDestBuf[2] = 255 - std::min(255, s0 + k);
pDestBuf[1] = 255 - std::min(255, s1 + k);
pDestBuf[0] = 255 - std::min(255, s2 + k);
pSrcBuf += 4;
pDestBuf += 3;
}
} else {
for (int i = 0; i < pixels; i++) {
std::tie(pDestBuf[2], pDestBuf[1], pDestBuf[0]) =
AdobeCMYK_to_sRGB1(pSrcBuf[0], pSrcBuf[1], pSrcBuf[2],
pSrcBuf[3]);
pSrcBuf += 4;
pDestBuf += 3;
}
}
}
break;
default:
NOTREACHED_NORETURN();
}
}