blob: 5f10d136ccc7c9b729c72a9786d2c1617767edc3 [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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.
// -----------------------------------------------------------------------------
//
// Generic image-type guessing.
//
// Author: Skal (pascal.massimino@gmail.com)
#include "imageio/image_dec.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <string>
#include "imageio/anim_image_dec.h"
#include "imageio/file_format.h"
#include "imageio/imageio_util.h"
#include "src/utils/utils.h"
#include "src/wp2/base.h"
namespace WP2 {
static constexpr uint32_t kFormatNumBytes = 12;
static inline uint32_t GetBE16(const uint8_t buf[]) {
return ((uint32_t)buf[0] << 8) | buf[1];
}
static inline uint32_t GetBE32(const uint8_t buf[]) {
return ((uint32_t)buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
}
FileFormat GuessImageFormat(const uint8_t* const data, size_t data_size) {
if (data != nullptr && data_size >= kFormatNumBytes) {
const uint32_t magic1 = GetBE32(data + 0);
const uint32_t magic2 = GetBE16(data + 4);
const uint32_t magic3 = GetBE32(data + 8);
// The other possible header for JPEG XL is in hex: 0000000C4A584C200D0A870A
// Which gives the following in bytes.
const std::string data_str(reinterpret_cast<const char*>(data),
std::min(data_size, static_cast<size_t>(12)));
if (magic1 == 0x89504E47U) {
return FileFormat::PNG;
} else if (magic1 >= 0xFFD8FF00U && magic1 <= 0xFFD8FFFFU) {
return FileFormat::JPEG;
} else if ((data_str.substr(0, 2) == "\xff\x0a") ||
(data_str.size() >= 12 &&
(data_str.substr(0, 12) ==
// std::string is needed here because of the zero bytes!
std::string("\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a",
12)))) {
return FileFormat::JPEG_XL;
} else if (magic1 == 0x49492A00 || magic1 == 0x4D4D002A) {
return FileFormat::TIFF;
} else if (magic1 == 0x47494638 && (magic2 == 0x3761 || magic2 == 0x3961)) {
return FileFormat::GIF;
} else if (magic1 == 0x52494646 && magic3 == 0x57454250) {
return FileFormat::WEBP;
} else if ((magic1 >> 8) == 0xf4ff6f) {
return FileFormat::WP2;
} else if ((magic1 >> 16) == 0x424d) {
return FileFormat::BMP;
} else if (((magic1 >> 24) & 0xff) == 'P') {
const int type = (magic1 >> 16) & 0xff;
// we only support 'P5 -> P7' for now.
if (type == '5') return FileFormat::PGM;
if (type == '6') return FileFormat::PPM;
if (type == '7') return FileFormat::PAM;
} else if (magic1 == GetBE32((const uint8_t*)"YUV4")) {
// Try to find the color space tag in available 'data'.
const char* const header = reinterpret_cast<const char*>(data);
const size_t header_size = std::min(data_size, (size_t)64);
if (strnstr(header, header_size, " C")) {
if (strnstr(header, header_size, " C420")) return FileFormat::Y4M_420;
if (strnstr(header, header_size, " C444")) return FileFormat::Y4M_444;
} else if (data_size < 64) {
// No color tag yet. Assume 420, there are probably more bytes to come.
return FileFormat::Y4M_420;
}
}
}
return FileFormat::UNSUPPORTED;
}
WP2Status ReadImage(const uint8_t* const data, size_t data_size,
ArgbBuffer* const buffer, FileFormat format,
LogLevel log_level) {
ImageReader image_reader(data, data_size, buffer, format, log_level);
return image_reader.ReadFrame();
}
WP2Status ReadImage(const char* const file_path, ArgbBuffer* const buffer,
size_t* const file_size, FileFormat format,
LogLevel log_level) {
Data data;
WP2_CHECK_STATUS(IoUtilReadFile(file_path, &data));
if (file_size != nullptr) *file_size = data.size;
return ReadImage(data.bytes, data.size, buffer, format, log_level);
}
} // namespace WP2