Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/gfx/skia/skia/src/encode/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 16 kB image not shown  

Quelle  SkJpegGainmapEncoder.cpp   Sprache: C

 
/*
 * 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 "include/private/SkJpegGainmapEncoder.h"

#include "include/core/SkBitmap.h"
#include "include/core/SkPixmap.h"
#include "include/core/SkStream.h"
#include "include/encode/SkEncoder.h"
#include "include/encode/SkJpegEncoder.h"
#include "include/private/SkGainmapInfo.h"
#include "src/codec/SkCodecPriv.h"
#include "src/codec/SkJpegConstants.h"
#include "src/codec/SkJpegMultiPicture.h"
#include "src/codec/SkJpegPriv.h"
#include "src/codec/SkJpegSegmentScan.h"
#include "src/codec/SkTiffUtility.h"
#include "src/core/SkStreamPriv.h"
#include "src/encode/SkJpegEncoderImpl.h"

#include <vector>

static bool is_single_channel(SkColor4f c) { return c.fR == c.fG && c.fG == c.fB; };

////////////////////////////////////////////////////////////////////////////////////////////////////
// HDRGM encoding

// Generate the XMP metadata for an HDRGM file.
sk_sp<SkData> get_gainmap_image_xmp_metadata(const SkGainmapInfo& gainmapInfo) {
    SkDynamicMemoryWStream s;
    const float kLog2 = std::log(2.f);
    const SkColor4f gainMapMin = {std::log(gainmapInfo.fGainmapRatioMin.fR) / kLog2,
                                  std::log(gainmapInfo.fGainmapRatioMin.fG) / kLog2,
                                  std::log(gainmapInfo.fGainmapRatioMin.fB) / kLog2,
                                  1.f};
    const SkColor4f gainMapMax = {std::log(gainmapInfo.fGainmapRatioMax.fR) / kLog2,
                                  std::log(gainmapInfo.fGainmapRatioMax.fG) / kLog2,
                                  std::log(gainmapInfo.fGainmapRatioMax.fB) / kLog2,
                                  1.f};
    const SkColor4f gamma = {1.f / gainmapInfo.fGainmapGamma.fR,
                             1.f / gainmapInfo.fGainmapGamma.fG,
                             1.f / gainmapInfo.fGainmapGamma.fB,
                             1.f};
    // Write a scalar attribute.
    auto write_scalar_attr = [&s](const char* attrib, SkScalar value) {
        s.writeText(" ");
        s.writeText(attrib);
        s.writeText("=\"");
        s.writeScalarAsText(value);
        s.writeText("\"\n");
    };

    // Write a scalar attribute only if all channels of |value| are equal (otherwise, write
    // nothing).
    auto maybe_write_scalar_attr = [&write_scalar_attr](const char* attrib, SkColor4f value) {
        if (!is_single_channel(value)) {
            return;
        }
        write_scalar_attr(attrib, value.fR);
    };

    // Write a float3 attribute as a list ony if not all channels of |value| are equal (otherwise,
    // write nothing).
    auto maybe_write_float3_attr = [&s](const char* attrib, SkColor4f value) {
        if (is_single_channel(value)) {
            return;
        }
        s.writeText(" <");
        s.writeText(attrib);
        s.writeText(">\n");
        s.writeText(" \n");
        s.writeText(" ");
        s.writeScalarAsText(value.fR);
        s.writeText("\n");
        s.writeText(" ");
        s.writeScalarAsText(value.fG);
        s.writeText("\n");
        s.writeText(" ");
        s.writeScalarAsText(value.fB);
        s.writeText("\n");
        s.writeText(" \n");
        s.writeText(" );
        s.writeText(attrib);
        s.writeText(">\n");
    };

    s.writeText(
            "adobe:ns:meta/\" x:xmptk=\"XMP Core 5.5.0\">\n"
            " http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"
            " \"\n"
            " xmlns:hdrgm=\"http://ns.adobe.com/hdr-gain-map/1.0/\"\n"
            " hdrgm:Version=\"1.0\"\n");
    maybe_write_scalar_attr("hdrgm:GainMapMin", gainMapMin);
    maybe_write_scalar_attr("hdrgm:GainMapMax", gainMapMax);
    maybe_write_scalar_attr("hdrgm:Gamma", gamma);
    maybe_write_scalar_attr("hdrgm:OffsetSDR", gainmapInfo.fEpsilonSdr);
    maybe_write_scalar_attr("hdrgm:OffsetHDR", gainmapInfo.fEpsilonHdr);
    write_scalar_attr("hdrgm:HDRCapacityMin", std::log(gainmapInfo.fDisplayRatioSdr) / kLog2);
    write_scalar_attr("hdrgm:HDRCapacityMax", std::log(gainmapInfo.fDisplayRatioHdr) / kLog2);
    switch (gainmapInfo.fBaseImageType) {
        case SkGainmapInfo::BaseImageType::kSDR:
            s.writeText(" hdrgm:BaseRenditionIsHDR=\"False\">\n");
            break;
        case SkGainmapInfo::BaseImageType::kHDR:
            s.writeText(" hdrgm:BaseRenditionIsHDR=\"True\">\n");
            break;
    }

    // Write any of the vector parameters that cannot be represented as scalars (and thus cannot
    // be written inline as above).
    maybe_write_float3_attr("hdrgm:GainMapMin", gainMapMin);
    maybe_write_float3_attr("hdrgm:GainMapMax", gainMapMax);
    maybe_write_float3_attr("hdrgm:Gamma", gamma);
    maybe_write_float3_attr("hdrgm:OffsetSDR", gainmapInfo.fEpsilonSdr);
    maybe_write_float3_attr("hdrgm:OffsetHDR", gainmapInfo.fEpsilonHdr);
    s.writeText(
            "
\n"

            "
\n"

            "
"
);
    return s.detachAsData();
}

// Generate the GContainer metadata for an image with a JPEG gainmap.
static sk_sp<SkData> get_base_image_xmp_metadata(size_t gainmapItemLength) {
    SkDynamicMemoryWStream s;
    s.writeText(
            "adobe:ns:meta/\" x:xmptk=\"Adobe XMP Core 5.1.2\">\n"
            " http://www.w3.org/1999/02/22-rdf-syntax-ns#\">\n"
            "
            " xmlns:Container=\"http://ns.google.com/photos/1.0/container/\"\n"
            " xmlns:Item=\"http://ns.google.com/photos/1.0/container/item/\"\n"
            " xmlns:hdrgm=\"http://ns.adobe.com/hdr-gain-map/1.0/\"\n"
            " hdrgm:Version=\"1.0\">\n"
            " \n"
            " \n"
            " Resource\">\n"
            "
            " Item:Semantic=\"Primary\"\n"
            " Item:Mime=\"image/jpeg\"/>\n"
            "
\n"

            " Resource\">\n"
            "
            " Item:Semantic=\"GainMap\"\n"
            " Item:Mime=\"image/jpeg\"\n"
            " Item:Length=\"");
    s.writeDecAsText(gainmapItemLength);
    s.writeText(
            "\"/>\n"
            "
\n"

            " \n"
            " \n"
            " \n"
            "
\n"

            "
\n"
);
    return s.detachAsData();
}

static sk_sp<SkData> encode_to_data(const SkPixmap& pm,
                                    const SkJpegEncoder::Options& options,
                                    const SkJpegMetadataEncoder::SegmentList& metadataSegments) {
    SkDynamicMemoryWStream encodeStream;
    auto encoder = SkJpegEncoderImpl::MakeRGB(&encodeStream, pm, options, metadataSegments);
    if (!encoder || !encoder->encodeRows(pm.height())) {
        return nullptr;
    }
    return encodeStream.detachAsData();
}

static sk_sp<SkData> get_exif_params() {
    SkDynamicMemoryWStream s;

    s.write(kExifSig, sizeof(kExifSig));
    s.write8(0);

    s.write(SkTiff::kEndianBig, sizeof(SkTiff::kEndianBig));
    SkWStreamWriteU32BE(&s, 8);  // Offset of index IFD

    // Write the index IFD.
    {
        constexpr uint16_t kIndexIfdNumberOfTags = 1;
        SkWStreamWriteU16BE(&s, kIndexIfdNumberOfTags);

        constexpr uint16_t kSubIFDOffsetTag = 0x8769;
        constexpr uint32_t kSubIfdCount = 1;
        constexpr uint32_t kSubIfdOffset = 26;
        SkWStreamWriteU16BE(&s, kSubIFDOffsetTag);
        SkWStreamWriteU16BE(&s, SkTiff::kTypeUnsignedLong);
        SkWStreamWriteU32BE(&s, kSubIfdCount);
        SkWStreamWriteU32BE(&s, kSubIfdOffset);

        constexpr uint32_t kIndexIfdNextIfdOffset = 0;
        SkWStreamWriteU32BE(&s, kIndexIfdNextIfdOffset);
    }

    // Write the sub-IFD.
    {
        constexpr uint16_t kSubIfdNumberOfTags = 1;
        SkWStreamWriteU16BE(&s, kSubIfdNumberOfTags);

        constexpr uint16_t kVersionTag = 0x9000;
        constexpr uint32_t kVersionCount = 4;
        constexpr uint8_t kVersion[kVersionCount] = {'0''2''3''2'};
        SkWStreamWriteU16BE(&s, kVersionTag);
        SkWStreamWriteU16BE(&s, SkTiff::kTypeUndefined);
        SkWStreamWriteU32BE(&s, kVersionCount);
        s.write(kVersion, sizeof(kVersion));

        constexpr uint32_t kSubIfdNextIfdOffset = 0;
        SkWStreamWriteU32BE(&s, kSubIfdNextIfdOffset);
    }

    return s.detachAsData();
}

static sk_sp<SkData> get_mpf_segment(const SkJpegMultiPictureParameters& mpParams,
                                     size_t imageNumber) {
    SkDynamicMemoryWStream s;
    auto segmentParameters = mpParams.serialize(static_cast<uint32_t>(imageNumber));
    const size_t mpParameterLength = kJpegSegmentParameterLengthSize + segmentParameters->size();
    s.write8(0xFF);
    s.write8(kMpfMarker);
    s.write8(mpParameterLength / 256);
    s.write8(mpParameterLength % 256);
    s.write(segmentParameters->data(), segmentParameters->size());
    return s.detachAsData();
}

static sk_sp<SkData> get_iso_gainmap_segment_params(sk_sp<SkData> data) {
    SkDynamicMemoryWStream s;
    s.write(kISOGainmapSig, sizeof(kISOGainmapSig));
    s.write(data->data(), data->size());
    return s.detachAsData();
}

bool SkJpegGainmapEncoder::EncodeHDRGM(SkWStream* dst,
                                       const SkPixmap& base,
                                       const SkJpegEncoder::Options& baseOptions,
                                       const SkPixmap& gainmap,
                                       const SkJpegEncoder::Options& gainmapOptions,
                                       const SkGainmapInfo& gainmapInfo) {
    bool includeUltraHDRv1 = gainmapInfo.isUltraHDRv1Compatible();

    // All images will have the same minimial Exif metadata.
    auto exif_params = get_exif_params();

    // Encode the gainmap image.
    sk_sp<SkData> gainmapData;
    {
        SkJpegMetadataEncoder::SegmentList metadataSegments;

        // Start with Exif metadata.
        metadataSegments.emplace_back(kExifMarker, exif_params);

        // MPF segment will be inserted after this.

        // Add XMP metadata.
        if (includeUltraHDRv1) {
            SkJpegMetadataEncoder::AppendXMPStandard(
                    metadataSegments, get_gainmap_image_xmp_metadata(gainmapInfo).get());
        }

        // Include the ICC profile of the alternate color space, if it is used.
        if (gainmapInfo.fGainmapMathColorSpace) {
            SkJpegMetadataEncoder::AppendICC(
                    metadataSegments, gainmapOptions, gainmapInfo.fGainmapMathColorSpace.get());
        }

        // Add the ISO 21946-1 metadata.
        metadataSegments.emplace_back(kISOGainmapMarker,
                                      get_iso_gainmap_segment_params(gainmapInfo.serialize()));

        // Encode the gainmap image.
        gainmapData = encode_to_data(gainmap, gainmapOptions, metadataSegments);
        if (!gainmapData) {
            SkCodecPrintf("Failed to encode gainmap image.\n");
            return false;
        }
    }

    // Encode the base image.
    sk_sp<SkData> baseData;
    {
        SkJpegMetadataEncoder::SegmentList metadataSegments;

        // Start with Exif metadata.
        metadataSegments.emplace_back(kExifMarker, exif_params);

        // MPF segment will be inserted after this.

        // Include XMP.
        if (includeUltraHDRv1) {
            // Add to the gainmap image size the size of the MPF segment for image 1 of a 2-image
            // file.
            SkJpegMultiPictureParameters mpParams(2);
            size_t gainmapImageSize = gainmapData->size() + get_mpf_segment(mpParams, 1)->size();
            SkJpegMetadataEncoder::AppendXMPStandard(
                    metadataSegments,
                    get_base_image_xmp_metadata(static_cast<int32_t>(gainmapImageSize)).get());
        }

        // Include ICC profile metadata.
        SkJpegMetadataEncoder::AppendICC(metadataSegments, baseOptions, base.colorSpace());

        // Include the ISO 21946-1 version metadata.
        metadataSegments.emplace_back(
                kISOGainmapMarker,
                get_iso_gainmap_segment_params(SkGainmapInfo::SerializeVersion()));

        // Encode the base image.
        baseData = encode_to_data(base, baseOptions, metadataSegments);
        if (!baseData) {
            SkCodecPrintf("Failed to encode base image.\n");
            return false;
        }
    }

    // Combine them into an MPF.
    const SkData* images[] = {
            baseData.get(),
            gainmapData.get(),
    };
    return MakeMPF(dst, images, 2);
}

// Compute the offset into |image| at which the MP segment should be inserted. Return 0 on failure.
static size_t mp_segment_offset(const SkData* image) {
    // Scan the image until StartOfScan marker.
    SkJpegSegmentScanner scan(kJpegMarkerStartOfScan);
    scan.onBytes(image->data(), image->size());
    if (!scan.isDone()) {
        SkCodecPrintf("Failed to scan image header.\n");
        return 0;
    }
    const auto& segments = scan.getSegments();

    // Search for the Exif segment and place the MP parameters immediately after. See 5.1.
    // Basic MP File Structure, which indicates "The MP Extensions are specified in the APP2
    // marker segment which follows immediately after the Exif Attributes in the APP1 marker
    // segment except as specified in section 7".
    for (size_t segmentIndex = 0; segmentIndex < segments.size() - 1; ++segmentIndex) {
        const auto& segment = segments[segmentIndex];
        if (segment.marker != kExifMarker) {
            continue;
        }
        auto params = SkJpegSegmentScanner::GetParameters(image, segment);
        if (params->size() < sizeof(kExifSig) ||
            memcmp(params->data(), kExifSig, sizeof(kExifSig)) != 0) {
            continue;
        }
        // Insert the MPF segment at the offset of the next segment.
        return segments[segmentIndex + 1].offset;
    }

    // If there is no Exif segment, then insert the MPF segment just before the StartOfScan.
    return segments.back().offset;
}

bool SkJpegGainmapEncoder::MakeMPF(SkWStream* dst, const SkData** images, size_t imageCount) {
    if (imageCount < 1) {
        return true;
    }

    // The offset into each image at which the MP segment will be written.
    std::vector<size_t> mpSegmentOffsets(imageCount);

    // Populate the MP parameters (image sizes and offsets).
    SkJpegMultiPictureParameters mpParams(imageCount);
    size_t cumulativeSize = 0;
    for (size_t i = 0; i < imageCount; ++i) {
        // Compute the offset into the each image where we will write the MP parameters.
        mpSegmentOffsets[i] = mp_segment_offset(images[i]);
        if (!mpSegmentOffsets[i]) {
            return false;
        }

        // Add the size of the MPF segment to image size. Note that the contents of
        // get_mpf_segment() are incorrect (because we don't have the right offset values), but
        // the size is correct.
        const size_t imageSize = images[i]->size() + get_mpf_segment(mpParams, i)->size();
        mpParams.images[i].dataOffset = SkJpegMultiPictureParameters::GetImageDataOffset(
                cumulativeSize, mpSegmentOffsets[0]);
        mpParams.images[i].size = static_cast<uint32_t>(imageSize);
        cumulativeSize += imageSize;
    }

    // Write the images.
    for (size_t i = 0; i < imageCount; ++i) {
        // Write up to the MP segment.
        if (!dst->write(images[i]->bytes(), mpSegmentOffsets[i])) {
            SkCodecPrintf("Failed to write image header.\n");
            return false;
        }

        // Write the MP segment.
        auto mpfSegment = get_mpf_segment(mpParams, i);
        if (!dst->write(mpfSegment->data(), mpfSegment->size())) {
            SkCodecPrintf("Failed to write MPF segment.\n");
            return false;
        }

        // Write the rest of the image.
        if (!dst->write(images[i]->bytes() + mpSegmentOffsets[i],
                        images[i]->size() - mpSegmentOffsets[i])) {
            SkCodecPrintf("Failed to write image body.\n");
            return false;
        }
    }

    return true;
}

Messung V0.5
C=87 H=95 G=90

¤ Dauer der Verarbeitung: 0.5 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.