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

Quelle  SkExif.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/SkExif.h"

#include "include/core/SkData.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkStream.h"
#include "src/codec/SkCodecPriv.h"
#include "src/codec/SkTiffUtility.h"
#include "src/core/SkStreamPriv.h"

#include <algorithm>
#include <cmath>
#include <cstring>
#include <memory>
#include <utility>

namespace SkExif {

constexpr uint16_t kSubIFDOffsetTag = 0x8769;
constexpr uint16_t kMarkerNoteTag = 0x927c;

static std::optional<float> get_maker_note_hdr_headroom(sk_sp<SkData> data) {
    // No little endian images that specify this data have been observed. Do not add speculative
    // support.
    const bool kLittleEndian = false;
    const uint8_t kSig[] = {
            'A''p''p''l''e'' ''i''O''S', 0, 0, 1, 'M''M',  //
    };
    if (!data || data->size() < sizeof(kSig)) {
        return std::nullopt;
    }
    if (memcmp(data->data(), kSig, sizeof(kSig)) != 0) {
        return std::nullopt;
    }
    auto ifd = SkTiff::ImageFileDirectory::MakeFromOffset(
            std::move(data), kLittleEndian, sizeof(kSig));
    if (!ifd) {
        return std::nullopt;
    }

    // See documentation at:
    // https://developer.apple.com/documentation/appkit/images_and_pdf/applying_apple_hdr_effect_to_your_photos
    bool hasMaker33 = false;
    bool hasMaker48 = false;
    float maker33 = 0.f;
    float maker48 = 0.f;
    for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) {
        switch (ifd->getEntryTag(i)) {
            case 33:
                if (!hasMaker33) {
                    hasMaker33 = ifd->getEntrySignedRational(i, 1, &maker33);
                }
                break;
            case 48:
                if (!hasMaker48) {
                    hasMaker48 = ifd->getEntrySignedRational(i, 1, &maker48);
                }
                break;
            default:
                break;
        }
    }
    // Many images have a maker33 but not a maker48. Treat them as having maker48 of 0.
    if (!hasMaker33) {
        return std::nullopt;
    }
    float stops = 0.f;
    if (maker33 < 1.0f) {
        if (maker48 <= 0.01f) {
            stops = -20.0f * maker48 + 1.8f;
        } else {
            stops = -0.101f * maker48 + 1.601f;
        }
    } else {
        if (maker48 <= 0.01f) {
            stops = -70.0f * maker48 + 3.0f;
        } else {
            stops = -0.303f * maker48 + 2.303f;
        }
    }
    return std::pow(2.f, std::max(stops, 0.f));
}

static void parse_ifd(Metadata& exif,
                      sk_sp<SkData> data,
                      std::unique_ptr<SkTiff::ImageFileDirectory> ifd,
                      bool littleEndian,
                      bool isRoot) {
    if (!ifd) {
        return;
    }
    for (uint32_t i = 0; i < ifd->getNumEntries(); ++i) {
        switch (ifd->getEntryTag(i)) {
            case kOriginTag: {
                uint16_t value = 0;
                if (!exif.fOrigin.has_value() && ifd->getEntryUnsignedShort(i, 1, &value)) {
                    if (0 < value && value <= kLast_SkEncodedOrigin) {
                        exif.fOrigin = static_cast<SkEncodedOrigin>(value);
                    }
                }
                break;
            }
            case kMarkerNoteTag:
                if (!exif.fHdrHeadroom.has_value()) {
                    if (auto makerNoteData = ifd->getEntryUndefinedData(i)) {
                        exif.fHdrHeadroom = get_maker_note_hdr_headroom(std::move(makerNoteData));
                    }
                }
                break;
            case kSubIFDOffsetTag: {
                uint32_t subIfdOffset = 0;
                if (isRoot && ifd->getEntryUnsignedLong(i, 1, &subIfdOffset)) {
                    auto subIfd = SkTiff::ImageFileDirectory::MakeFromOffset(
                            data, littleEndian, subIfdOffset, /*allowTruncated=*/true);
                    parse_ifd(exif,
                              data,
                              std::move(subIfd),
                              littleEndian,
                              /*isRoot=*/false);
                }
                break;
            }
            case kXResolutionTag: {
                float value = 0.f;
                if (!exif.fXResolution.has_value() && ifd->getEntryUnsignedRational(i, 1, &value)) {
                    exif.fXResolution = value;
                }
                break;
            }
            case kYResolutionTag: {
                float value = 0.f;
                if (!exif.fYResolution.has_value() && ifd->getEntryUnsignedRational(i, 1, &value)) {
                    exif.fYResolution = value;
                }
                break;
            }
            case kResolutionUnitTag: {
                uint16_t value = 0;
                if (!exif.fResolutionUnit.has_value() && ifd->getEntryUnsignedShort(i, 1, &value)) {
                    exif.fResolutionUnit = value;
                }
                break;
            }
            case kPixelXDimensionTag: {
                // The type for this tag can be unsigned short or unsigned long (as per the Exif 2.3
                // spec, aka CIPA DC-008-2012). Support for unsigned long was added in
                // https://crrev.com/817600.
                uint16_t value16 = 0;
                if (!exif.fPixelXDimension.has_value() &&
                    ifd->getEntryUnsignedShort(i, 1, &value16)) {
                    exif.fPixelXDimension = value16;
                }
                uint32_t value32 = 0;
                if (!exif.fPixelXDimension.has_value() &&
                    ifd->getEntryUnsignedLong(i, 1, &value32)) {
                    exif.fPixelXDimension = value32;
                }
                break;
            }
            case kPixelYDimensionTag: {
                uint16_t value16 = 0;
                if (!exif.fPixelYDimension.has_value() &&
                    ifd->getEntryUnsignedShort(i, 1, &value16)) {
                    exif.fPixelYDimension = value16;
                }
                uint32_t value32 = 0;
                if (!exif.fPixelYDimension.has_value() &&
                    ifd->getEntryUnsignedLong(i, 1, &value32)) {
                    exif.fPixelYDimension = value32;
                }
                break;
            }
            default:
                break;
        }
    }
}

void Parse(Metadata& metadata, const SkData* data) {
    bool littleEndian = false;
    uint32_t ifdOffset = 0;
    if (data && SkTiff::ImageFileDirectory::ParseHeader(data, &littleEndian, &ifdOffset)) {
        auto dataRef = SkData::MakeWithoutCopy(data->data(), data->size());
        auto ifd = SkTiff::ImageFileDirectory::MakeFromOffset(
                dataRef, littleEndian, ifdOffset, /*allowTruncated=*/true);
        parse_ifd(metadata, std::move(dataRef), std::move(ifd), littleEndian, /*isRoot=*/true);
    }
}

// Helper function to write a single IFD entry.
bool write_entry(uint16_t tag, uint16_t type, uint32_t count, uint32_t value,
                uint32_t* endOfData, SkWStream* stream, SkWStream* buffer) {
    bool success = true;
    success &= SkWStreamWriteU16BE(stream, tag);
    success &= SkWStreamWriteU16BE(stream, type);
    success &= SkWStreamWriteU32BE(stream, count);
    switch (tag) {
      case kOriginTag:
      case kResolutionUnitTag:
        success &= SkWStreamWriteU16BE(stream, value);
        success &= SkWStreamWriteU16BE(stream, 0); // Complete the IFD entry.
        return success;
      case kPixelXDimensionTag:
      case kPixelYDimensionTag:
        success &= SkWStreamWriteU32BE(stream, value);
        return success;
      case kXResolutionTag:
      case kYResolutionTag:
        // If the number of bytes for the type of the entry is greater than 4, we have
        // to append the value to the end and replace the value section with an offset
        // to where the data can be found.
        success &= SkWStreamWriteU32BE(stream, *endOfData);
        *endOfData += 8;
        success &= SkWStreamWriteU32BE(buffer, value); // Numerator
        success &= SkWStreamWriteU32BE(buffer, 1); // Denominator
        return success;
      case kSubIFDOffsetTag:
        // This does not write the subIFD itself, just the IFD0 entry that points
        // to where it is located.
        success &= SkWStreamWriteU32BE(stream, value);
        return success;
      default:
        return false;
    }
}

sk_sp<SkData> WriteExif(Metadata& metadata) {
    // Cannot write an IFD entry for MakerNote from the HDR Headroom. Information
    // about maker48 and maker33 is lost in encode.
    // See documentation at:
    // https://developer.apple.com/documentation/appkit/images_and_pdf/applying_apple_hdr_effect_to_your_photos
    if (metadata.fHdrHeadroom.has_value()) {
        SkCodecPrintf("Cannot encode maker noter from the headroom value.\n");
        return nullptr;
    }

    SkDynamicMemoryWStream stream;
    // If there exists metadata that belongs in a subIFD, we will write that to a
    // separate stream and append it to the end of the data, before |bufferForLargerValues|.
    bool subIFDExists = false;
    // This buffer will hold the values that are more than 4 bytes and will be
    // appended to the end of the data after going through all available fields.
    SkDynamicMemoryWStream bufferForLargerValues;
    constexpr uint32_t kOffset = 8;

    // Write the IFD header.
    if (!stream.write(SkTiff::kEndianBig, sizeof(SkTiff::kEndianBig))) {
      return nullptr;
    }
    // Offset of index IFD.
    if (!SkWStreamWriteU32BE(&stream, kOffset)) {
        return nullptr;
    }
    // Count the number of valid metadata entries.
    uint16_t numTags = 0;
    uint16_t numSubIFDTags = 0;
    if (metadata.fOrigin.has_value()) numTags++;
    if (metadata.fResolutionUnit.has_value()) numTags++;
    if (metadata.fXResolution.has_value()) numTags++;
    if (metadata.fYResolution.has_value()) numTags++;
    if (metadata.fPixelXDimension.has_value()) numSubIFDTags++;
    if (metadata.fPixelYDimension.has_value()) numSubIFDTags++;
    if (numSubIFDTags > 0) {
      subIFDExists = true;
      numTags++;
    }

    // Offset that represents where data will be appended.
    uint32_t endOfData = kOffset
                        + SkTiff::kSizeShort  // Number of tags
                        + (SkTiff::kSizeEntry * numTags) // Entries
                        + SkTiff::kSizeLong; // Next IFD offset
    // Offset that represents where the subIFD will start if it exists.
    const uint32_t kSubIfdOffset = endOfData;
    if (subIFDExists) {
      endOfData += SkTiff::kSizeShort  // Number of subIFD tags
                  + (SkTiff::kSizeEntry * numSubIFDTags) // SubIFD entries
                  + SkTiff::kSizeLong; // SubIFD next offset;
    }

    // Write the number of tags in the IFD.
    SkWStreamWriteU16BE(&stream, numTags);

    // Write the IFD entries.
    if (metadata.fOrigin.has_value()
        && !write_entry(kOriginTag, SkTiff::kTypeUnsignedShort, 1,
                        metadata.fOrigin.value(), &endOfData, &stream,
                        &bufferForLargerValues)) {
          return nullptr;
        }

    if (metadata.fResolutionUnit.has_value()
        && !write_entry(kResolutionUnitTag, SkTiff::kTypeUnsignedShort, 1,
                        metadata.fResolutionUnit.value(), &endOfData, &stream,
                        &bufferForLargerValues)) {
          return nullptr;
        }

    if (metadata.fXResolution.has_value()
        && !write_entry(kXResolutionTag, SkTiff::kTypeUnsignedRational, 1,
                        metadata.fXResolution.value(), &endOfData, &stream,
                        &bufferForLargerValues)) {
          return nullptr;
        }

    if (metadata.fYResolution.has_value()
        && !write_entry(kYResolutionTag, SkTiff::kTypeUnsignedRational, 1,
                        metadata.fYResolution.value(), &endOfData, &stream,
                        &bufferForLargerValues)) {
          return nullptr;
        }

    if (subIFDExists && !write_entry(kSubIFDOffsetTag, SkTiff::kTypeUnsignedLong, 1,
                      kSubIfdOffset, &endOfData, &stream, &bufferForLargerValues)) {
          return nullptr;
        }

    // Next IFD offset (0 for no next IFD).
    if (!SkWStreamWriteU32BE(&stream, 0)) {
        return nullptr;
    }

    // After all IFD0 data has been written, then write the SubIFD (ExifIFD).
    if (subIFDExists) {
      // Write the number of tags in the subIFD.
      if (!SkWStreamWriteU16BE(&stream, numSubIFDTags)) {
        return nullptr;
      }

      if (metadata.fPixelXDimension.has_value()
          && !write_entry(kPixelXDimensionTag, SkTiff::kTypeUnsignedLong, 1,
                          metadata.fPixelXDimension.value(), &endOfData, &stream,
                          &bufferForLargerValues)) {
            return nullptr;
          }

      if (metadata.fPixelYDimension.has_value()
          && !write_entry(kPixelYDimensionTag, SkTiff::kTypeUnsignedLong, 1,
                          metadata.fPixelYDimension.value(), &endOfData, &stream,
                          &bufferForLargerValues)) {
            return nullptr;
          }

      // Write the SubIFD next offset (0).
      if (!SkWStreamWriteU32BE(&stream, 0)) {
        return nullptr;
      }
    }

    // Append the data buffer to the end of the stream.
    if (!bufferForLargerValues.writeToStream(&stream)) {
        return nullptr;
    }

    return stream.detachAsData();
}

}  // namespace SkExif

Messung V0.5
C=87 H=98 G=92

¤ Dauer der Verarbeitung: 0.9 Sekunden  (vorverarbeitet)  ¤

*© 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.