blob: 36d9db9dee608b785b0cb3b309124a9de6dd0ecc [file] [log] [blame]
/*
* Copyright 2023 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/codec/SkJpegGainmap.h"
#include "include/core/SkColor.h"
#include "include/core/SkData.h"
#include "include/core/SkScalar.h"
#include "include/core/SkStream.h"
#include "include/private/SkGainmapInfo.h"
#include "include/private/base/SkFloatingPoint.h"
#include "src/codec/SkCodecPriv.h"
#include "src/codec/SkJpegMultiPicture.h"
#include "src/codec/SkJpegPriv.h"
#include "src/codec/SkJpegSegmentScan.h"
#include "src/codec/SkJpegSourceMgr.h"
#include "src/codec/SkJpegXmp.h"
#include <cstdint>
#include <cstring>
#include <utility>
#include <vector>
bool SkJpegGetMultiPictureGainmap(const SkJpegMultiPictureParameters* mpParams,
SkJpegSourceMgr* decoderSource,
SkGainmapInfo* outInfo,
std::unique_ptr<SkStream>* outGainmapImageStream) {
// Extract the Multi-Picture image streams in the original decoder stream (we needed the scan to
// find the offsets of the MP images within the original decoder stream).
auto mpStreams = SkJpegExtractMultiPictureStreams(mpParams, decoderSource);
if (!mpStreams) {
SkCodecPrintf("Failed to extract MP image streams.\n");
return false;
}
// Iterate over the MP image streams.
for (auto& mpImage : mpStreams->images) {
if (!mpImage.stream) {
continue;
}
// Create a temporary source manager for this MP image.
auto mpImageSource = SkJpegSourceMgr::Make(mpImage.stream.get());
// Collect the potential XMP segments.
std::vector<sk_sp<SkData>> app1Params;
for (const auto& segment : mpImageSource->getAllSegments()) {
if (segment.marker != kXMPMarker) {
continue;
}
auto parameters = mpImageSource->getSegmentParameters(segment);
if (!parameters) {
continue;
}
app1Params.push_back(std::move(parameters));
}
// Build XMP if possible.
auto xmp = SkJpegXmp::Make(app1Params);
if (!xmp) {
continue;
}
// Check if this is an MPF gainmap.
SkGainmapInfo info;
if (!xmp->getGainmapInfoHDRGM(&info) && !xmp->getGainmapInfoHDRGainMap(&info)) {
continue;
}
// This MP image is the gainmap image. Populate its stream and the rendering parameters
// for its format.
if (outGainmapImageStream) {
if (!mpImage.stream->rewind()) {
SkCodecPrintf("Failed to rewind gainmap image stream.\n");
return false;
}
*outGainmapImageStream = std::move(mpImage.stream);
}
*outInfo = info;
return true;
}
return false;
}
bool SkJpegGetJpegRGainmap(const SkJpegXmp* xmp,
SkJpegSourceMgr* decoderSource,
SkGainmapInfo* outInfo,
std::unique_ptr<SkStream>* outGainmapImageStream) {
// Parse the XMP metadata of the original image, to see if it specifies a RecoveryMap.
SkGainmapInfo info;
size_t itemOffsetFromEndOfImage = 0;
size_t itemSize = 0;
if (!xmp->getGainmapInfoJpegR(&info, &itemOffsetFromEndOfImage, &itemSize)) {
return false;
}
// The offset read from the XMP metadata is relative to the end of the EndOfImage marker in the
// original decoder stream. Create a full scan of the original decoder stream, so we can find
// that EndOfImage marker's offset in the decoder stream.
const std::vector<SkJpegSegment>& segments = decoderSource->getAllSegments();
if (segments.empty() || segments.back().marker != SkJpegSegmentScanner::kMarkerEndOfImage) {
SkCodecPrintf("Failed to construct segments through EndOfImage.\n");
return false;
}
const auto& lastSegment = segments.back();
const size_t endOfImageOffset = lastSegment.offset + SkJpegSegmentScanner::kMarkerCodeSize;
const size_t itemOffsetFromStartOfImage = endOfImageOffset + itemOffsetFromEndOfImage;
// Extract the gainmap image's stream.
auto gainmapImageStream = decoderSource->getSubsetStream(itemOffsetFromStartOfImage, itemSize);
if (!gainmapImageStream) {
SkCodecPrintf("Failed to extract gainmap stream.");
return false;
}
// Populate the output parameters for this format.
if (outGainmapImageStream) {
if (!gainmapImageStream->rewind()) {
SkCodecPrintf("Failed to rewind gainmap image stream.");
return false;
}
*outGainmapImageStream = std::move(gainmapImageStream);
}
*outInfo = info;
return true;
}