1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/core/SkTypes.h"
9cb93a386Sopenharmony_ci#include "src/codec/SkCodecPriv.h"
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_cistatic bool parse_encoded_origin(const uint8_t* exifData, size_t data_length, uint64_t offset,
12cb93a386Sopenharmony_ci                                 bool littleEndian, bool is_root, SkEncodedOrigin* orientation) {
13cb93a386Sopenharmony_ci    // Require that the marker is at least large enough to contain the number of entries.
14cb93a386Sopenharmony_ci    if (data_length < offset + 2) {
15cb93a386Sopenharmony_ci        return false;
16cb93a386Sopenharmony_ci    }
17cb93a386Sopenharmony_ci    uint32_t numEntries = get_endian_short(exifData + offset, littleEndian);
18cb93a386Sopenharmony_ci
19cb93a386Sopenharmony_ci    // Tag (2 bytes), Datatype (2 bytes), Number of elements (4 bytes), Data (4 bytes)
20cb93a386Sopenharmony_ci    const uint32_t kEntrySize = 12;
21cb93a386Sopenharmony_ci    const auto max = SkTo<uint32_t>((data_length - offset - 2) / kEntrySize);
22cb93a386Sopenharmony_ci    numEntries = std::min(numEntries, max);
23cb93a386Sopenharmony_ci
24cb93a386Sopenharmony_ci    // Advance the data to the start of the entries.
25cb93a386Sopenharmony_ci    auto data = exifData + offset + 2;
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_ci    const uint16_t kOriginTag = 0x112;
28cb93a386Sopenharmony_ci    const uint16_t kOriginType = 3;
29cb93a386Sopenharmony_ci    const uint16_t kSubIFDOffsetTag = 0x8769;
30cb93a386Sopenharmony_ci    const uint16_t kSubIFDOffsetType = 4;
31cb93a386Sopenharmony_ci
32cb93a386Sopenharmony_ci    for (uint32_t i = 0; i < numEntries; i++, data += kEntrySize) {
33cb93a386Sopenharmony_ci        uint16_t tag = get_endian_short(data, littleEndian);
34cb93a386Sopenharmony_ci        uint16_t type = get_endian_short(data + 2, littleEndian);
35cb93a386Sopenharmony_ci        uint32_t count = get_endian_int(data + 4, littleEndian);
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_ci        if (kOriginTag == tag && kOriginType == type && 1 == count) {
38cb93a386Sopenharmony_ci            uint16_t val = get_endian_short(data + 8, littleEndian);
39cb93a386Sopenharmony_ci            if (0 < val && val <= kLast_SkEncodedOrigin) {
40cb93a386Sopenharmony_ci                *orientation = (SkEncodedOrigin)val;
41cb93a386Sopenharmony_ci                return true;
42cb93a386Sopenharmony_ci            }
43cb93a386Sopenharmony_ci        } else if (kSubIFDOffsetTag == tag && kSubIFDOffsetType == type && 1 == count && is_root) {
44cb93a386Sopenharmony_ci            uint32_t subifd = get_endian_int(data + 8, littleEndian);
45cb93a386Sopenharmony_ci            if (0 < subifd && subifd < data_length) {
46cb93a386Sopenharmony_ci                if (parse_encoded_origin(exifData, data_length, subifd, littleEndian, false,
47cb93a386Sopenharmony_ci                                        orientation)) {
48cb93a386Sopenharmony_ci                    return true;
49cb93a386Sopenharmony_ci                }
50cb93a386Sopenharmony_ci            }
51cb93a386Sopenharmony_ci        }
52cb93a386Sopenharmony_ci    }
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ci    return false;
55cb93a386Sopenharmony_ci}
56cb93a386Sopenharmony_ci
57cb93a386Sopenharmony_cibool SkParseEncodedOrigin(const uint8_t* data, size_t data_length, SkEncodedOrigin* orientation) {
58cb93a386Sopenharmony_ci    SkASSERT(orientation);
59cb93a386Sopenharmony_ci    bool littleEndian;
60cb93a386Sopenharmony_ci    // We need eight bytes to read the endian marker and the offset, below.
61cb93a386Sopenharmony_ci    if (data_length < 8 || !is_valid_endian_marker(data, &littleEndian)) {
62cb93a386Sopenharmony_ci        return false;
63cb93a386Sopenharmony_ci    }
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    // Get the offset from the start of the marker.
66cb93a386Sopenharmony_ci    // Though this only reads four bytes, use a larger int in case it overflows.
67cb93a386Sopenharmony_ci    uint64_t offset = get_endian_int(data + 4, littleEndian);
68cb93a386Sopenharmony_ci
69cb93a386Sopenharmony_ci    return parse_encoded_origin(data, data_length, offset, littleEndian, true, orientation);
70cb93a386Sopenharmony_ci}
71