普通文本  |  283行  |  8.48 KB

// Copyright 2015 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

#include "src/tiff_directory/tiff_directory.h"

#include <assert.h>
#include <climits>

#include "src/binary_parse/range_checked_byte_ptr.h"

namespace piex {
namespace tiff_directory {
namespace {

using binary_parse::Get16s;
using binary_parse::Get16u;
using binary_parse::Get32s;
using binary_parse::Get32u;
using binary_parse::MemoryStatus;
using binary_parse::RANGE_CHECKED_BYTE_SUCCESS;
using binary_parse::RangeCheckedBytePtr;

}  // namespace

TiffDirectory::TiffDirectory(Endian endian) : endian_(endian) {}

bool TiffDirectory::Has(const Tag tag) const {
  return directory_entries_.count(tag) == 1;
}

bool TiffDirectory::Get(const Tag tag, std::vector<std::uint8_t>* value) const {
  const DirectoryEntry* directory_entry = Find(tag);
  if (directory_entry == NULL ||
      (directory_entry->type != TIFF_TYPE_BYTE &&
       directory_entry->type != TIFF_TYPE_UNDEFINED)) {
    return false;
  }

  *value = directory_entry->value;
  return true;
}

bool TiffDirectory::Get(const Tag tag, std::string* value) const {
  const DirectoryEntry* directory_entry = Find(tag);
  if (directory_entry == NULL || directory_entry->type != TIFF_TYPE_ASCII) {
    return false;
  }
  *value =
      std::string(directory_entry->value.begin(), directory_entry->value.end());
  return true;
}

bool TiffDirectory::Get(const Tag tag, std::uint32_t* value) const {
  std::vector<std::uint32_t> my_values;
  if (!Get(tag, &my_values) || my_values.size() != 1) {
    return false;
  }
  *value = my_values[0];
  return true;
}

bool TiffDirectory::Get(const Tag tag,
                        std::vector<std::uint32_t>* value) const {
  const DirectoryEntry* directory_entry = Find(tag);
  if (directory_entry == NULL || (directory_entry->type != TIFF_TYPE_SHORT &&
                                  directory_entry->type != TIFF_TYPE_LONG)) {
    return false;
  }

  RangeCheckedBytePtr value_ptr(&directory_entry->value[0],
                                directory_entry->value.size());
  std::vector<std::uint32_t> my_value(directory_entry->count);
  const bool is_big_endian = (endian_ == kBigEndian);

  MemoryStatus err = RANGE_CHECKED_BYTE_SUCCESS;
  for (std::uint32_t c = 0; c < directory_entry->count; ++c) {
    if (directory_entry->type == TIFF_TYPE_SHORT) {
      my_value[c] = Get16u(value_ptr + c * 2, is_big_endian, &err);
    } else {
      my_value[c] = Get32u(value_ptr + c * 4, is_big_endian, &err);
    }
  }
  if (err != RANGE_CHECKED_BYTE_SUCCESS) {
    return false;
  }

  *value = my_value;
  return true;
}

bool TiffDirectory::Get(const Tag tag, Rational* value) const {
  std::vector<Rational> my_values;
  if (!Get(tag, &my_values) || my_values.size() != 1) {
    return false;
  }
  *value = my_values[0];
  return true;
}

bool TiffDirectory::Get(const Tag tag, std::vector<Rational>* value) const {
  const DirectoryEntry* directory_entry = Find(tag);
  if (directory_entry == NULL ||
      (directory_entry->type != TIFF_TYPE_SHORT &&
       directory_entry->type != TIFF_TYPE_LONG &&
       directory_entry->type != TIFF_TYPE_RATIONAL)) {
    return false;
  }

  RangeCheckedBytePtr value_ptr(&directory_entry->value[0],
                                directory_entry->value.size());
  std::vector<Rational> my_value(directory_entry->count);
  const bool is_big_endian = (endian_ == kBigEndian);

  MemoryStatus err = RANGE_CHECKED_BYTE_SUCCESS;
  for (std::uint32_t c = 0; c < directory_entry->count; ++c) {
    switch (directory_entry->type) {
      case TIFF_TYPE_SHORT: {
        my_value[c].numerator = Get16u(value_ptr + c * 2, is_big_endian, &err);
        my_value[c].denominator = 1;
        break;
      }
      case TIFF_TYPE_LONG: {
        my_value[c].numerator = Get32u(value_ptr + c * 4, is_big_endian, &err);
        my_value[c].denominator = 1;
        break;
      }
      case TIFF_TYPE_RATIONAL: {
        my_value[c].numerator = Get32u(value_ptr + c * 8, is_big_endian, &err);
        my_value[c].denominator =
            Get32u(value_ptr + c * 8 + 4, is_big_endian, &err);
        if (my_value[c].denominator == 0) {
          return false;
        }
        break;
      }
    }
  }
  if (err != RANGE_CHECKED_BYTE_SUCCESS) {
    return false;
  }

  *value = my_value;
  return true;
}

bool TiffDirectory::Get(const Tag tag, SRational* value) const {
  std::vector<SRational> my_values;
  if (!Get(tag, &my_values) || my_values.size() != 1) {
    return false;
  }
  *value = my_values[0];
  return true;
}

bool TiffDirectory::Get(const Tag tag, std::vector<SRational>* value) const {
  const DirectoryEntry* directory_entry = Find(tag);
  if (directory_entry == NULL ||
      (directory_entry->type != TIFF_TYPE_SSHORT &&
       directory_entry->type != TIFF_TYPE_SLONG &&
       directory_entry->type != TIFF_TYPE_SRATIONAL)) {
    return false;
  }

  RangeCheckedBytePtr value_ptr(&directory_entry->value[0],
                                directory_entry->value.size());
  std::vector<SRational> my_value(directory_entry->count);
  const bool is_big_endian = (endian_ == kBigEndian);

  MemoryStatus err = RANGE_CHECKED_BYTE_SUCCESS;
  for (std::uint32_t c = 0; c < directory_entry->count; ++c) {
    switch (directory_entry->type) {
      case TIFF_TYPE_SSHORT: {
        my_value[c].numerator = Get16s(value_ptr + c * 2, is_big_endian, &err);
        my_value[c].denominator = 1;
        break;
      }
      case TIFF_TYPE_SLONG: {
        my_value[c].numerator = Get32s(value_ptr + c * 4, is_big_endian, &err);
        my_value[c].denominator = 1;
        break;
      }
      case TIFF_TYPE_SRATIONAL: {
        my_value[c].numerator = Get32s(value_ptr + c * 8, is_big_endian, &err);
        my_value[c].denominator =
            Get32s(value_ptr + c * 8 + 4, is_big_endian, &err);
        if (my_value[c].denominator == 0) {
          return false;
        }
        break;
      }
    }
  }
  if (err != RANGE_CHECKED_BYTE_SUCCESS) {
    return false;
  }

  *value = my_value;
  return true;
}

bool TiffDirectory::GetOffsetAndLength(const Tag tag, const Type type,
                                       std::uint32_t* offset,
                                       std::uint32_t* length) const {
  const DirectoryEntry* directory_entry = Find(tag);
  if (directory_entry == NULL || directory_entry->type != type) {
    return false;
  }
  *offset = directory_entry->offset;
  *length = static_cast<std::uint32_t>(directory_entry->value.size());
  return true;
}

void TiffDirectory::AddEntry(const Tag tag, const Type type,
                             const std::uint32_t count,
                             const std::uint32_t offset,
                             const std::vector<std::uint8_t>& value) {
  assert(SizeOfType(type, NULL /* success */) * count == value.size());

  const DirectoryEntry directory_entry = {type, count, offset, value};
  directory_entries_[tag] = directory_entry;
  tag_order_.push_back(tag);
}

void TiffDirectory::AddSubDirectory(const TiffDirectory& sub_directory) {
  sub_directories_.push_back(sub_directory);
}

const std::vector<TiffDirectory>& TiffDirectory::GetSubDirectories() const {
  return sub_directories_;
}

const TiffDirectory::DirectoryEntry* TiffDirectory::Find(const Tag tag) const {
  std::map<Tag, DirectoryEntry>::const_iterator iter =
      directory_entries_.find(tag);
  if (iter == directory_entries_.end()) {
    return NULL;
  }
  return &iter->second;
}

size_t SizeOfType(const TiffDirectory::Type type, bool* success) {
  switch (type) {
    case TIFF_TYPE_BYTE:
    case TIFF_TYPE_ASCII:
    case TIFF_TYPE_SBYTE:
    case TIFF_TYPE_UNDEFINED:
      return 1;
    case TIFF_TYPE_SHORT:
    case TIFF_TYPE_SSHORT:
      return 2;
    case TIFF_TYPE_LONG:
    case TIFF_TYPE_SLONG:
    case TIFF_TYPE_FLOAT:
    case TIFF_IFD:
      return 4;
    case TIFF_TYPE_RATIONAL:
    case TIFF_TYPE_SRATIONAL:
    case TIFF_TYPE_DOUBLE:
      return 8;
  }

  if (success != NULL) {
    *success = false;
  }
  return 0;
}

}  // namespace tiff_directory
}  // namespace piex