blob: 2652465f317584dd3c50eb3e7c43a23bff42233b [file]
// Copyright 2026 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/fxcodec/jpeg/rust_jpeg_context.h"
#include <utility>
#include "core/fxcodec/cfx_codec_memory.h"
#include "core/fxcodec/jpeg/rust_jpeg_ffi.rs.h"
#include "core/fxcodec/progressive_decoder_context_delegate.h"
#include "core/fxcrt/check.h"
#include "core/fxcrt/check_op.h"
#include "core/fxcrt/data_vector.h"
#include "core/fxcrt/fx_safe_types.h"
#include "core/fxcrt/span.h"
#include "core/fxge/dib/cfx_dibitmap.h"
namespace fxcodec {
RustJpegContext::RustJpegContext(ProgressiveDecoderContextDelegate* delegate)
: delegate_(delegate) {}
RustJpegContext::~RustJpegContext() = default;
bool RustJpegContext::ReadHeader() {
if (state_ == State::kGotHeader || state_ == State::kFinishedDecoding) {
return true;
}
if (state_ == State::kError || !codec_memory_) {
return false;
}
CHECK_EQ(state_, State::kNoDecoder);
pdfium::span<const uint8_t> src_span = codec_memory_->GetBufferSpan();
rust_jpeg::JpegHeaderInfo header_info{};
rust::Slice<const uint8_t> src_slice(src_span);
if (!rust_jpeg::read_jpeg_info(src_slice, header_info)) {
// More data may be needed to parse the header during progressive decoding,
// so do not transition to `State::kError` here.
return false;
}
switch (header_info.num_components) {
case 1:
src_format_ = ProgressiveDecoderContextDelegate::Format::k8bppGray;
break;
case 3:
src_format_ = ProgressiveDecoderContextDelegate::Format::kRgb;
break;
case 4:
src_format_ = ProgressiveDecoderContextDelegate::Format::kCmyk;
break;
default:
state_ = State::kError;
return false;
}
width_ = header_info.width;
height_ = header_info.height;
num_components_ = header_info.num_components;
x_density_ = header_info.x_density;
y_density_ = header_info.y_density;
density_unit_ = header_info.density_unit;
state_ = State::kGotHeader;
return true;
}
bool RustJpegContext::ProcessData() {
if (state_ == State::kFinishedDecoding) {
return true;
}
if (state_ == State::kError) {
return false;
}
if (state_ == State::kNoDecoder && !ReadHeader()) {
return false;
}
CHECK_EQ(state_, State::kGotHeader);
if (!delegate_->PrepareScanlineResampling(width_, height_, src_format_)) {
state_ = State::kError;
return false;
}
pdfium::span<const uint8_t> src_span = codec_memory_->GetBufferSpan();
rust::Slice<const uint8_t> src_slice(src_span);
FX_SAFE_SIZE_T row_bytes = width_;
row_bytes *= num_components_;
row_bytes += 3;
row_bytes /= 4;
row_bytes *= 4;
if (!row_bytes.IsValid()) {
state_ = State::kError;
return false;
}
FX_SAFE_SIZE_T total_bytes = row_bytes;
total_bytes *= height_;
if (!total_bytes.IsValid()) {
state_ = State::kError;
return false;
}
DataVector<uint8_t> decoded_buf(total_bytes.ValueOrDie());
rust::Slice<uint8_t> out_slice(decoded_buf);
if (!rust_jpeg::decode_jpeg_to_buf(src_slice, out_slice,
row_bytes.ValueOrDie(),
/*scale_denom=*/1)) {
state_ = State::kError;
return false;
}
size_t stride = row_bytes.ValueOrDie();
for (uint32_t y = 0; y < height_; ++y) {
pdfium::span<const uint8_t> scanline =
pdfium::span(decoded_buf).subspan(y * stride, stride);
delegate_->ResampleScanline(y, scanline);
}
state_ = State::kFinishedDecoding;
return true;
}
void RustJpegContext::Input(RetainPtr<CFX_CodecMemory> codec_memory) {
codec_memory_ = std::move(codec_memory);
}
FXCODEC_STATUS RustJpegContext::StartDecode(RetainPtr<CFX_DIBitmap> bitmap) {
bitmap_ = std::move(bitmap);
FXCODEC_STATUS status = FXCODEC_STATUS::kDecodeToBeContinued;
if (!delegate_->ReadMoreData(0, &status)) {
return status;
}
return FXCODEC_STATUS::kDecodeToBeContinued;
}
FXCODEC_STATUS RustJpegContext::ContinueDecode() {
FXCODEC_STATUS status = FXCODEC_STATUS::kDecodeFinished;
while (delegate_->ReadMoreData(std::nullopt, &status)) {
}
if (status == FXCODEC_STATUS::kError || !ProcessData()) {
state_ = State::kError;
status = FXCODEC_STATUS::kError;
}
bitmap_ = nullptr;
return state_ == State::kFinishedDecoding ? FXCODEC_STATUS::kDecodeFinished
: status;
}
} // namespace fxcodec