| // 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/skia_jpeg_context.h" |
| |
| #include <utility> |
| |
| #include "core/fxcodec/cfx_codec_memory.h" |
| #include "core/fxcodec/codec_memory_sk_stream.h" |
| #include "core/fxcodec/progressive_decoder_context_delegate.h" |
| #include "core/fxcrt/check.h" |
| #include "core/fxcrt/check_op.h" |
| #include "core/fxcrt/fx_safe_types.h" |
| #include "core/fxcrt/notreached.h" |
| #include "core/fxcrt/span.h" |
| #include "core/fxge/dib/cfx_dibitmap.h" |
| #include "third_party/skia/include/codec/SkCodec.h" |
| #include "third_party/skia/include/codec/SkJpegDecoder.h" |
| #include "third_party/skia/include/core/SkColorType.h" |
| #include "third_party/skia/include/core/SkImageInfo.h" |
| #include "third_party/skia/include/core/SkStream.h" |
| |
| namespace fxcodec { |
| |
| SkiaJpegContext::SkiaJpegContext(ProgressiveDecoderContextDelegate* delegate) |
| : delegate_(delegate) {} |
| |
| SkiaJpegContext::~SkiaJpegContext() = default; |
| |
| bool SkiaJpegContext::ReadHeader(RetainPtr<CFX_CodecMemory> codec_memory) { |
| if (state_ == State::kGotDecoder || state_ == State::kFinishedDecoding) { |
| return true; |
| } |
| if (state_ == State::kError) { |
| return false; |
| } |
| |
| CHECK_EQ(state_, State::kNoDecoder); |
| CHECK(!decoder_); |
| |
| if (!codec_memory_) { |
| codec_memory_ = codec_memory; |
| } |
| CHECK_EQ(&*codec_memory_, &*codec_memory); |
| |
| auto stream = std::make_unique<CodecMemorySkStream>(std::move(codec_memory)); |
| SkCodec::Result result = SkCodec::kSuccess; |
| decoder_ = SkJpegDecoder::Decode(std::move(stream), &result); |
| switch (result) { |
| case SkCodec::kSuccess: { |
| SkImageInfo info = decoder_->getInfo(); |
| if (!delegate_->PrepareDirectOutput( |
| info.width(), info.height(), |
| ProgressiveDecoderContextDelegate::Format::kArgb)) { |
| decoder_.reset(); |
| state_ = State::kError; |
| return false; |
| } |
| state_ = State::kGotDecoder; |
| return true; |
| } |
| case SkCodec::kIncompleteInput: |
| codec_memory_->Seek(0); |
| return true; |
| default: |
| decoder_.reset(); |
| state_ = State::kError; |
| return false; |
| } |
| } |
| |
| bool SkiaJpegContext::ProcessData(RetainPtr<CFX_CodecMemory> codec_memory) { |
| if (state_ == State::kNoDecoder) { |
| if (!ReadHeader(std::move(codec_memory))) { |
| return false; |
| } |
| if (state_ == State::kNoDecoder) { |
| return true; |
| } |
| } |
| |
| if (state_ == State::kGotDecoder) { |
| CHECK(bitmap_); |
| SkImageInfo dst_info = |
| decoder_->getInfo().makeColorType(kBGRA_8888_SkColorType); |
| |
| pdfium::span<uint8_t> dst_buffer = delegate_->AskImageBuf(); |
| FX_SAFE_SIZE_T row_bytes = dst_buffer.size(); |
| row_bytes /= dst_info.height(); |
| |
| SkCodec::Result result = decoder_->getPixels(dst_info, dst_buffer.data(), |
| row_bytes.ValueOrDie()); |
| decoder_.reset(); |
| switch (result) { |
| case SkCodec::kSuccess: |
| state_ = State::kFinishedDecoding; |
| return true; |
| case SkCodec::kIncompleteInput: |
| // SkJpegDecoder does not support incremental decoding. When |
| // incomplete, retry the entire operation from the beginning. |
| state_ = State::kNoDecoder; |
| codec_memory_->Seek(0); |
| return true; |
| default: |
| state_ = State::kError; |
| return false; |
| } |
| } |
| |
| CHECK_EQ(state_, State::kFinishedDecoding); |
| return true; |
| } |
| |
| void SkiaJpegContext::Input(RetainPtr<CFX_CodecMemory> codec_memory) { |
| codec_memory_ = std::move(codec_memory); |
| } |
| |
| FXCODEC_STATUS SkiaJpegContext::StartDecode(RetainPtr<CFX_DIBitmap> bitmap) { |
| bitmap_ = std::move(bitmap); |
| CHECK_EQ(bitmap_->GetFormat(), FXDIB_Format::kBgra); |
| FXCODEC_STATUS status = FXCODEC_STATUS::kDecodeToBeContinued; |
| if (!delegate_->ReadMoreData(0, &status)) { |
| return status; |
| } |
| return FXCODEC_STATUS::kDecodeToBeContinued; |
| } |
| |
| FXCODEC_STATUS SkiaJpegContext::ContinueDecode() { |
| FXCODEC_STATUS status = FXCODEC_STATUS::kDecodeFinished; |
| while (state_ != State::kFinishedDecoding) { |
| if (!ProcessData(codec_memory_)) { |
| status = FXCODEC_STATUS::kError; |
| break; |
| } |
| if (state_ == State::kFinishedDecoding) { |
| break; |
| } |
| status = FXCODEC_STATUS::kError; |
| if (!delegate_->ReadMoreData(std::nullopt, &status)) { |
| break; |
| } |
| } |
| bitmap_ = nullptr; |
| return state_ == State::kFinishedDecoding ? FXCODEC_STATUS::kDecodeFinished |
| : status; |
| } |
| |
| } // namespace fxcodec |