加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
imageinfo.hpp 47.37 KB
一键复制 编辑 原始数据 按行查看 历史
xiaozhuai 提交于 2021-06-09 17:19 . optimize code
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419
//
// Created by xiaozhuai on 2021/4/1.
// https://github.com/xiaozhuai/imageinfo
//
//
// MIT License
//
// Copyright (c) 2021 xiaozhuai
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
#pragma once
#ifndef IMAGEINFO_IMAGEINFO_H
#define IMAGEINFO_IMAGEINFO_H
#include <functional>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <string>
#include <regex>
#include <utility>
#include <unordered_map>
#include <unordered_set>
#include <set>
#include <vector>
#include <tuple>
#include <array>
#include <cstdio>
#include <cassert>
#ifdef ANDROID
#include <android/asset_manager.h>
#endif
static_assert(sizeof(uint8_t) == 1, "sizeof(uint8_t) != 1");
static_assert(sizeof(int8_t) == 1, "sizeof(int8_t) != 1");
static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t) != 2");
static_assert(sizeof(int16_t) == 2, "sizeof(int16_t) != 2");
static_assert(sizeof(uint32_t) == 4, "sizeof(uint32_t) != 4");
static_assert(sizeof(int32_t) == 4, "sizeof(int32_t) != 4");
static_assert(sizeof(uint64_t) == 8, "sizeof(uint64_t) != 8");
static_assert(sizeof(int64_t) == 8, "sizeof(int64_t) != 8");
#ifdef __clang__
#pragma clang diagnostic push
#pragma ide diagnostic ignored "OCUnusedStructInspection"
#pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection"
#endif
enum IIFormat {
II_FORMAT_UNKNOWN = 0,
II_FORMAT_AVIF,
II_FORMAT_BMP,
II_FORMAT_CUR,
II_FORMAT_DDS,
II_FORMAT_GIF,
II_FORMAT_HDR,
II_FORMAT_HEIC,
II_FORMAT_ICNS,
II_FORMAT_ICO,
II_FORMAT_JP2,
II_FORMAT_JPEG,
II_FORMAT_JPX,
II_FORMAT_KTX,
II_FORMAT_PNG,
II_FORMAT_PSD,
II_FORMAT_TGA,
II_FORMAT_TIFF,
II_FORMAT_WEBP,
};
enum IIErrorCode {
II_ERR_OK = 0,
II_ERR_UNRECOGNIZED_FORMAT,
II_ERR_DECODE_SIZE_FAILED,
};
#ifdef ANDROID
class IIAndroidAssetFileReader {
public:
explicit IIAndroidAssetFileReader(AAsset *file) : m_file(file) {}
inline size_t size() const {
if (m_file != nullptr) {
return AAsset_getLength(m_file);
} else {
return 0;
}
}
inline void read(void *buf, off_t offset, size_t size) {
AAsset_seek(m_file, offset, SEEK_SET);
AAsset_read(m_file, buf, size);
}
private:
AAsset *m_file = nullptr;
};
#endif
class IIFileReader {
public:
explicit IIFileReader(FILE *file) : m_file(file) {}
inline size_t size() {
if (m_file != nullptr) {
fseek(m_file, 0, SEEK_END);
return ftell(m_file);
} else {
return 0;
}
}
inline void read(void *buf, off_t offset, size_t size) {
fseek(m_file, offset, SEEK_SET);
fread(buf, 1, size, m_file);
}
private:
FILE *m_file = nullptr;
};
class IIFilePathReader {
public:
explicit IIFilePathReader(const std::string &path) : m_file(path, std::ios::in | std::ios::binary) {}
~IIFilePathReader() {
if (m_file.is_open()) {
m_file.close();
}
}
inline size_t size() {
if (m_file.is_open()) {
m_file.seekg(0, std::ios::end);
return (size_t) m_file.tellg();
} else {
return 0;
}
}
inline void read(void *buf, off_t offset, size_t size) {
m_file.seekg(offset, std::ios::beg);
m_file.read((char *) buf, (std::streamsize) size);
}
private:
std::ifstream m_file;
};
class IIFileStreamReader {
public:
explicit IIFileStreamReader(std::ifstream &file) : m_file(file) {}
explicit IIFileStreamReader(std::ifstream *file) : m_file(*file) {}
inline size_t size() {
if (m_file.is_open()) {
m_file.seekg(0, std::ios::end);
return (size_t) m_file.tellg();
} else {
return 0;
}
}
inline void read(void *buf, off_t offset, size_t size) {
m_file.seekg(offset, std::ios::beg);
m_file.read((char *) buf, (std::streamsize) size);
}
private:
std::ifstream &m_file;
};
struct IIRawData {
IIRawData(const void *d, size_t s) : data(d), length(s) {}
const void *data = nullptr;
size_t length = 0;
};
class IIRawDataReader {
public:
explicit IIRawDataReader(IIRawData data) : m_data(data) {}
inline size_t size() const {
return m_data.length;
}
inline void read(void *buf, off_t offset, size_t size) const {
memcpy(buf, ((char *) m_data.data) + offset, size);
}
private:
IIRawData m_data;
};
class IIBuffer {
public:
IIBuffer() = default;
explicit IIBuffer(size_t size) : m_size(size) {
m_data = std::shared_ptr<uint8_t>(new uint8_t[size], std::default_delete<uint8_t[]>());
}
inline const uint8_t *data() const {
return m_data.get();
}
inline uint8_t *data() {
return m_data.get();
}
inline size_t size() const {
return m_size;
}
inline uint8_t &operator[](int offset) {
return m_data.get()[offset];
}
inline uint8_t operator[](int offset) const {
return m_data.get()[offset];
}
public:
inline uint8_t readU8(off_t offset) {
return readInt<uint8_t>(offset, false);
}
inline int8_t readS8(off_t offset) {
return readInt<int8_t>(offset, false);
}
inline uint16_t readU16LE(off_t offset) {
return readInt<uint16_t>(offset, false);
}
inline uint16_t readU16BE(off_t offset) {
return readInt<uint16_t>(offset, true);
}
inline int16_t readS16LE(off_t offset) {
return readInt<int16_t>(offset, false);
}
inline int16_t readS16BE(off_t offset) {
return readInt<int16_t>(offset, true);
}
inline uint32_t readU32LE(off_t offset) {
return readInt<uint32_t>(offset, false);
}
inline uint32_t readU32BE(off_t offset) {
return readInt<uint32_t>(offset, true);
}
inline int32_t readS32LE(off_t offset) {
return readInt<int32_t>(offset, false);
}
inline int32_t readS32BE(off_t offset) {
return readInt<int32_t>(offset, true);
}
inline uint64_t readU64LE(off_t offset) {
return readInt<uint64_t>(offset, false);
}
inline uint64_t readU64BE(off_t offset) {
return readInt<uint64_t>(offset, true);
}
inline int64_t readS64LE(off_t offset) {
return readInt<int64_t>(offset, false);
}
inline int64_t readS64BE(off_t offset) {
return readInt<int64_t>(offset, true);
}
template<typename T>
inline T readInt(off_t offset, bool swapEndian = false) {
T val = *((T *) (data() + offset));
return swapEndian ? swapE<T>(val) : val;
}
inline std::string readString(off_t offset, size_t size) {
return std::string((char *) data() + offset, size);
}
inline std::string toString() {
return std::string((char *) data(), size());
}
inline bool cmp(off_t offset, size_t size, const void *buf) {
return memcmp(data() + offset, buf, size) == 0;
}
inline bool cmpAnyOf(off_t offset, size_t size, const std::initializer_list<const void *> &bufList) {
return std::any_of(bufList.begin(), bufList.end(), [this, offset, size](const void *buf) {
return memcmp(data() + offset, buf, size) == 0;
});
}
private:
template<typename T>
static T swapE(T u) {
union {
T u;
uint8_t u8[sizeof(T)];
} source{}, dest{};
source.u = u;
for (size_t k = 0; k < sizeof(T); k++)
dest.u8[k] = source.u8[sizeof(T) - k - 1];
return dest.u;
}
private:
std::shared_ptr<uint8_t> m_data = nullptr;
size_t m_size = 0;
};
typedef std::function<void(void *buf, off_t offset, size_t size)> IIReadFunc;
class IIReadInterface {
public:
IIReadInterface() = delete;
IIReadInterface(IIReadFunc &readFunc, size_t length)
: m_readFunc(readFunc),
m_length(length) {}
inline IIBuffer readBuffer(off_t offset, size_t size) {
assert(offset >= 0);
assert(offset + size <= m_length);
IIBuffer buffer(size);
read(buffer.data(), offset, size);
return buffer;
}
inline size_t length() const {
return m_length;
}
private:
inline void read(void *buf, off_t offset, size_t size) {
m_readFunc(buf, offset, size);
}
private:
IIReadFunc &m_readFunc;
size_t m_length = 0;
};
typedef std::function<bool(size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes)> IIProcessFunc;
struct IIDetector {
IIDetector(IIFormat f, const char *e, const char *fe, const char *mt, IIProcessFunc p)
: format(f),
ext(e),
fullExt(fe),
mimetype(mt),
process(std::move(p)) {}
IIFormat format;
const char *ext;
const char *fullExt;
const char *mimetype;
IIProcessFunc process;
};
static const std::vector<IIDetector> s_ii_detectors = { // NOLINT(cert-err58-cpp)
///////////////////////// AVIF /////////////////////////
IIDetector(
II_FORMAT_AVIF,
"avif",
"avif",
"image/avif",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 4) {
return false;
}
auto buffer = ri.readBuffer(0, 4);
uint32_t ftypBoxLength = buffer.readU32BE(0);
if (length < ftypBoxLength + 12) {
return false;
}
buffer = ri.readBuffer(0, ftypBoxLength + 12);
if (!buffer.cmp(4, 4, "ftyp")) {
return false;
}
/**
* Major Brand
*
* AVIF: "avif"
* HEIF: "mif1", "msf1"
* HEIC: "heic", "heix", "hevc", "hevx"
*
*/
if (!buffer.cmpAnyOf(8, 4, {"avif", "mif1", "msf1", "heic", "heix", "hevc", "hevx"})) {
return false;
}
uint32_t compatibleBrandSize = (ftypBoxLength - 16) / 4;
std::unordered_set<std::string> compatibleBrands;
for (uint32_t i = 0; i < compatibleBrandSize; ++i) {
compatibleBrands.insert(buffer.readString(16 + i * 4, 4));
}
// same as heic, compatibleBrands contains "avif"
if (compatibleBrands.find("avif") == compatibleBrands.end()) {
return false;
}
if (!buffer.cmp(ftypBoxLength + 4, 4, "meta")) {
return false;
}
uint32_t metaLength = buffer.readU32BE(ftypBoxLength);
if (length < ftypBoxLength + 12 + metaLength) {
return false;
}
buffer = ri.readBuffer(ftypBoxLength + 12, metaLength);
off_t offset = 0;
off_t end = metaLength;
loop_box:
/**
* find ispe box
*
* meta
* - ...
* - iprp
* - ...
* - ipco
* - ...
* - ispe
*/
while (offset < end) {
// std::string boxType = buffer.readString(offset + 4, 4);
uint32_t boxSize = buffer.readU32BE(offset);
// std::cout << boxSize << ", " << boxType << "\n";
if (buffer.cmpAnyOf(offset + 4, 4, {"iprp", "ipco"})) {
end = offset + boxSize;
offset += 8;
goto loop_box;
}
if (buffer.cmp(offset + 4, 4, "ispe")) {
width = buffer.readU32BE(offset + 12);
height = buffer.readU32BE(offset + 16);
break;
}
offset += boxSize;
}
return true;
}
),
///////////////////////// BMP /////////////////////////
// https://www.fileformat.info/format/bmp/corion.htm
IIDetector(
II_FORMAT_BMP,
"bmp",
"bmp",
"image/bmp",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 26) {
return false;
}
auto buffer = ri.readBuffer(0, 26);
if (!buffer.cmp(0, 2, "BM")) {
return false;
}
width = buffer.readS32LE(18);
// bmp height can be negative, it means flip Y
height = std::abs(buffer.readS32LE(22));
return true;
}
),
///////////////////////// CUR /////////////////////////
IIDetector(
II_FORMAT_CUR,
"cur",
"cur",
"image/cur",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 6) {
return false;
}
auto buffer = ri.readBuffer(0, 6);
// Same with ico, but TYPE == 2
if (!buffer.cmp(0, 4, "\x00\x00\x02\x00")) {
return false;
}
uint16_t entryCount = buffer.readU16LE(4);
if (entryCount == 0) {
return false;
}
const int ENTRY_SIZE = 16;
off_t entryTotalSize = entryCount * ENTRY_SIZE;
off_t offset = 6;
if (length < offset + entryTotalSize) {
return false;
}
buffer = ri.readBuffer(offset, entryTotalSize);
offset += entryTotalSize;
std::vector<std::array<int64_t, 2>> sizes;
for (int i = 0; i < entryCount; ++i) {
uint8_t w1 = buffer.readU8(i * ENTRY_SIZE);
uint8_t h1 = buffer.readU8(i * ENTRY_SIZE + 1);
int64_t w2 = w1 == 0 ? 256 : w1;
int64_t h2 = h1 == 0 ? 256 : h1;
sizes.push_back({w2, h2});
uint32_t bytes = buffer.readS32LE(i * ENTRY_SIZE + 8);
offset += bytes;
}
if (length < (size_t) offset) {
return false;
}
width = sizes.front()[0];
height = sizes.front()[1];
sizes.swap(entrySizes);
return true;
}
),
///////////////////////// DDS /////////////////////////
IIDetector(
II_FORMAT_DDS,
"dds",
"dds",
"image/dds",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 20) {
return false;
}
auto buffer = ri.readBuffer(0, 20);
if (!buffer.cmp(0, 4, "DDS ")) {
return false;
}
height = buffer.readU32LE(12);
width = buffer.readU32LE(16);
return true;
}
),
///////////////////////// GIF /////////////////////////
// https://www.fileformat.info/format/gif/corion.htm
IIDetector(
II_FORMAT_GIF,
"gif",
"gif",
"image/gif",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 10) {
return false;
}
auto buffer = ri.readBuffer(0, 10);
if (!buffer.cmpAnyOf(0, 6, {"GIF87a", "GIF89a"})) {
return false;
}
width = buffer.readU16LE(6);
height = buffer.readU16LE(8);
return true;
}
),
///////////////////////// HDR /////////////////////////
// http://paulbourke.net/dataformats/pic/
IIDetector(
II_FORMAT_HDR,
"hdr",
"hdr",
"image/vnd.radiance",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 6) {
return false;
}
// TODO Max header size ? Or just read header line by line
auto buffer = ri.readBuffer(0, std::min<size_t>(length, 256));
if (!buffer.cmpAnyOf(0, 6, {"#?RGBE", "#?XYZE"})) {
return false;
}
auto header = buffer.toString();
std::smatch results;
static const std::regex XPattern(R"(\s(\-|\+)X\s(\d+)\s)");
static const std::regex YPattern(R"(\s(\-|\+)Y\s(\d+)\s)");
std::string widthStr;
std::string heightStr;
std::regex_search(header, results, XPattern);
if (results.size() >= 3) widthStr = results.str(2);
std::regex_search(header, results, YPattern);
if (results.size() >= 3) heightStr = results.str(2);
if (!widthStr.empty() && !heightStr.empty()) {
width = std::stol(widthStr);
height = std::stol(heightStr);
}
return true;
}
),
///////////////////////// HEIC /////////////////////////
// https://nokiatech.github.io/heif/technical.html
// https://www.jianshu.com/p/b016d10a087d
IIDetector(
II_FORMAT_HEIC,
"heic",
"heic",
"image/heic",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 4) {
return false;
}
auto buffer = ri.readBuffer(0, 4);
uint32_t ftypBoxLength = buffer.readU32BE(0);
if (length < ftypBoxLength + 12) {
return false;
}
buffer = ri.readBuffer(0, ftypBoxLength + 12);
if (!buffer.cmp(4, 4, "ftyp")) {
return false;
}
/**
* Major Brand
*
* AVIF: "avif"
* HEIF: "mif1", "msf1"
* HEIC: "heic", "heix", "hevc", "hevx"
*
*/
if (!buffer.cmpAnyOf(8, 4, {"avif", "mif1", "msf1", "heic", "heix", "hevc", "hevx"})) {
return false;
}
uint32_t compatibleBrandSize = (ftypBoxLength - 16) / 4;
std::unordered_set<std::string> compatibleBrands;
for (uint32_t i = 0; i < compatibleBrandSize; ++i) {
compatibleBrands.insert(buffer.readString(16 + i * 4, 4));
}
// compatibleBrands contains "heic"
if (compatibleBrands.find("heic") == compatibleBrands.end()) {
return false;
}
if (!buffer.cmp(ftypBoxLength + 4, 4, "meta")) {
return false;
}
uint32_t metaLength = buffer.readU32BE(ftypBoxLength);
if (length < ftypBoxLength + 12 + metaLength) {
return false;
}
buffer = ri.readBuffer(ftypBoxLength + 12, metaLength);
off_t offset = 0;
off_t end = metaLength;
loop_box:
/**
* find ispe box
*
* meta
* - ...
* - iprp
* - ...
* - ipco
* - ...
* - ispe
*/
while (offset < end) {
// std::string boxType = buffer.readString(offset + 4, 4);
uint32_t boxSize = buffer.readU32BE(offset);
// std::cout << boxSize << ", " << boxType << "\n";
if (buffer.cmpAnyOf(offset + 4, 4, {"iprp", "ipco"})) {
end = offset + boxSize;
offset += 8;
goto loop_box;
}
if (buffer.cmp(offset + 4, 4, "ispe")) {
width = buffer.readU32BE(offset + 12);
height = buffer.readU32BE(offset + 16);
break;
}
offset += boxSize;
}
return true;
}
),
///////////////////////// ICNS /////////////////////////
IIDetector(
II_FORMAT_ICNS,
"icns",
"icns",
"image/icns",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 8) {
return false;
}
auto buffer = ri.readBuffer(0, 8);
uint32_t fileLength = buffer.readU32BE(4);
if (!buffer.cmp(0, 4, "icns") || fileLength != length) {
return false;
}
static const std::unordered_map<std::string, int64_t> TYPE_SIZE_MAP = {
{"ICON", 32},
{"ICN#", 32},
{"icm#", 16},
{"icm4", 16},
{"icm8", 16},
{"ics#", 16},
{"ics4", 16},
{"ics8", 16},
{"is32", 16},
{"s8mk", 16},
{"icl4", 32},
{"icl8", 32},
{"il32", 32},
{"l8mk", 32},
{"ich#", 48},
{"ich4", 48},
{"ich8", 48},
{"ih32", 48},
{"h8mk", 48},
{"it32", 128},
{"t8mk", 128},
{"icp4", 16},
{"icp5", 32},
{"icp6", 64},
{"ic07", 128},
{"ic08", 256},
{"ic09", 512},
{"ic10", 1024},
{"ic11", 32},
{"ic12", 64},
{"ic13", 256},
{"ic14", 512},
{"ic04", 16},
{"ic05", 32},
{"icsB", 36},
{"icsb", 18},
};
int64_t maxSize = 0;
off_t offset = 8;
while (offset + 8 <= length) {
buffer = ri.readBuffer(offset, 8);
auto type = buffer.readString(0, 4);
uint32_t entrySize = buffer.readU32BE(4);
int64_t s = TYPE_SIZE_MAP.at(type);
entrySizes.push_back({s, s});
maxSize = std::max(maxSize, s);
offset += entrySize;
}
width = maxSize;
height = maxSize;
return true;
}
),
///////////////////////// ICO /////////////////////////
IIDetector(
II_FORMAT_ICO,
"ico",
"ico",
"image/ico",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 6) {
return false;
}
auto buffer = ri.readBuffer(0, 6);
// TYPE == 1
if (!buffer.cmp(0, 4, "\x00\x00\x01\x00")) {
return false;
}
uint16_t entryCount = buffer.readU16LE(4);
if (entryCount == 0) {
return false;
}
const int ENTRY_SIZE = 16;
off_t entryTotalSize = entryCount * ENTRY_SIZE;
off_t offset = 6;
if (length < offset + entryTotalSize) {
return false;
}
buffer = ri.readBuffer(offset, entryTotalSize);
offset += entryTotalSize;
std::vector<std::array<int64_t, 2>> sizes;
for (int i = 0; i < entryCount; ++i) {
uint8_t w1 = buffer.readU8(i * ENTRY_SIZE);
uint8_t h1 = buffer.readU8(i * ENTRY_SIZE + 1);
int64_t w2 = w1 == 0 ? 256 : w1;
int64_t h2 = h1 == 0 ? 256 : h1;
sizes.push_back({w2, h2});
uint32_t bytes = buffer.readS32LE(i * ENTRY_SIZE + 8);
offset += bytes;
}
if (length < (size_t) offset) {
return false;
}
width = sizes.front()[0];
height = sizes.front()[1];
sizes.swap(entrySizes);
return true;
}
),
///////////////////////// JP2 /////////////////////////
// https://docs.fileformat.com/image/jp2/
IIDetector(
II_FORMAT_JP2,
"jp2",
"jp2",
"image/jp2",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 8) {
return false;
}
auto buffer = ri.readBuffer(0, 8);
if (!buffer.cmp(4, 4, "jP ")) {
return false;
}
uint32_t signatureLength = buffer.readU32BE(0);
off_t offset = signatureLength;
if (length < offset + 12) {
return false;
}
buffer = ri.readBuffer(offset, 12);
// type == "jp2 "
if (!buffer.cmp(4, 4, "ftyp") || !buffer.cmp(8, 4, "jp2 ")) {
return false;
}
uint32_t ftypLength = buffer.readU32BE(0);
offset += ftypLength;
while (offset + 24 <= length) {
buffer = ri.readBuffer(offset, 24);
if (buffer.cmp(4, 4, "jp2h")) {
if (buffer.cmp(12, 4, "ihdr")) {
height = buffer.readU32BE(16);
width = buffer.readU32BE(20);
}
break;
}
uint32_t boxLength = buffer.readU32BE(0);
offset += boxLength;
}
return true;
}
),
///////////////////////// JPEG /////////////////////////
// https://www.fileformat.info/format/jpeg/corion.htm
IIDetector(
II_FORMAT_JPEG,
"jpg",
"jpeg",
"image/jpeg",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 2) {
return false;
}
auto buffer = ri.readBuffer(0, 2);
if (!buffer.cmp(0, 2, "\xFF\xD8")) {
return false;
}
off_t offset = 2;
while (offset + 9 <= length) {
buffer = ri.readBuffer(offset, 9);
uint16_t sectionSize = buffer.readU16BE(2);
// 0xFFC0 is baseline standard (SOF0)
// 0xFFC1 is baseline optimized (SOF1)
// 0xFFC2 is progressive (SOF2)
if (buffer.cmpAnyOf(0, 2, {"\xFF\xC0", "\xFF\xC1", "\xFF\xC2"})) {
height = buffer.readU16BE(5);
width = buffer.readU16BE(7);
break;
}
offset += sectionSize + 2;
}
return true;
}
),
///////////////////////// JPX /////////////////////////
// https://docs.fileformat.com/image/jpx/
IIDetector(
II_FORMAT_JPX,
"jpx",
"jpx",
"image/jpx",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 8) {
return false;
}
auto buffer = ri.readBuffer(0, 8);
if (!buffer.cmp(4, 4, "jP ")) {
return false;
}
uint32_t signatureLength = buffer.readU32BE(0);
off_t offset = signatureLength;
if (length < offset + 12) {
return false;
}
buffer = ri.readBuffer(offset, 12);
// same as jp2, type == "jpx "
if (!buffer.cmp(4, 4, "ftyp") || !buffer.cmp(8, 4, "jpx ")) {
return false;
}
uint32_t ftypLength = buffer.readU32BE(0);
offset += ftypLength;
while (offset + 24 <= length) {
buffer = ri.readBuffer(offset, 24);
if (buffer.cmp(4, 4, "jp2h")) {
if (buffer.cmp(12, 4, "ihdr")) {
height = buffer.readU32BE(16);
width = buffer.readU32BE(20);
}
break;
}
uint32_t boxLength = buffer.readU32BE(0);
offset += boxLength;
}
return true;
}
),
///////////////////////// KTX /////////////////////////
IIDetector(
II_FORMAT_KTX,
"ktx",
"ktx",
"image/ktx",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 44) {
return false;
}
auto buffer = ri.readBuffer(0, 44);
if (!buffer.cmp(1, 6, "KTX 11")) {
return false;
}
width = buffer.readU32LE(36);
height = buffer.readU32LE(40);
return true;
}
),
///////////////////////// PNG /////////////////////////
// https://www.fileformat.info/format/png/corion.htm
IIDetector(
II_FORMAT_PNG,
"png",
"png",
"image/png",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 4) {
return false;
}
auto buffer = ri.readBuffer(0, std::min<size_t>(length, 40));
if (!buffer.cmp(0, 4, "\x89PNG")) {
return false;
}
std::string firstChunkType = buffer.readString(12, 4);
if (firstChunkType == "IHDR" && buffer.size() >= 24) {
width = buffer.readU32BE(16);
height = buffer.readU32BE(20);
} else if (firstChunkType == "CgBI") {
if (buffer.readString(28, 4) == "IHDR" && buffer.size() >= 40) {
width = buffer.readU32BE(32);
height = buffer.readU32BE(36);
}
}
return true;
}
),
///////////////////////// PSD /////////////////////////
IIDetector(
II_FORMAT_PSD,
"psd",
"psd",
"image/psd",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 22) {
return false;
}
auto buffer = ri.readBuffer(0, 22);
if (!buffer.cmp(0, 6, "8BPS\x00\x01")) {
return false;
}
height = buffer.readU32BE(14);
width = buffer.readU32BE(18);
return true;
}
),
///////////////////////// TIFF /////////////////////////
// https://www.fileformat.info/format/tiff/corion.htm
IIDetector(
II_FORMAT_TIFF,
"tif",
"tiff",
"image/tiff",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 8) {
return false;
}
auto buffer = ri.readBuffer(0, 8);
if (!buffer.cmpAnyOf(0, 4, {"\x49\x49\x2A\x00", "\x4D\x4D\x00\x2A"})) {
return false;
}
bool swapEndian = buffer[0] == 0x4D;
auto offset = buffer.readInt<uint32_t>(4, swapEndian);
if (length < offset + 2) {
return true;
}
buffer = ri.readBuffer(offset, 2);
auto numEntry = buffer.readInt<uint16_t>(0, swapEndian);
offset += 2;
for (uint16_t i = 0;
i < numEntry
&& length >= offset + 12
&& (width == -1 || height == -1);
++i, offset += 12) {
buffer = ri.readBuffer(offset, 12);
auto tag = buffer.readInt<uint16_t>(0, swapEndian);
auto type = buffer.readInt<uint16_t>(2, swapEndian);
if (tag == 256) { // Found ImageWidth entry
if (type == 3) {
width = buffer.readInt<uint16_t>(8, swapEndian);
} else if (type == 4) {
width = buffer.readInt<uint32_t>(8, swapEndian);
}
} else if (tag == 257) { // Found ImageHeight entry
if (type == 3) {
height = buffer.readInt<uint16_t>(8, swapEndian);
} else if (type == 4) {
height = buffer.readInt<uint32_t>(8, swapEndian);
}
}
}
return true;
}
),
///////////////////////// WEBP /////////////////////////
// https://developers.google.com/speed/webp/docs/riff_container
IIDetector(
II_FORMAT_WEBP,
"webp",
"webp",
"image/webp",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 16) {
return false;
}
auto buffer = ri.readBuffer(0, std::min<size_t>(length, 30));
if (!buffer.cmp(0, 4, "RIFF") || !buffer.cmp(8, 4, "WEBP")) {
return false;
}
std::string type = buffer.readString(12, 4);
if (type == "VP8 " && buffer.size() >= 30) {
width = buffer.readU16LE(26) & 0x3FFF;
height = buffer.readU16LE(28) & 0x3FFF;
} else if (type == "VP8L" && buffer.size() >= 25) {
uint32_t n = buffer.readU32LE(21);
width = (n & 0x3FFF) + 1;
height = ((n >> 14) & 0x3FFF) + 1;
} else if (type == "VP8X" && buffer.size() >= 30) {
uint8_t extendedHeader = buffer.readU8(20);
bool validStart = (extendedHeader & 0xc0) == 0;
bool validEnd = (extendedHeader & 0x01) == 0;
if (validStart && validEnd) {
width = (buffer.readU32LE(24) & 0x00FFFFFF) + 1;
height = ((buffer.readU32LE(26) & 0xFFFFFF00) >> 8) + 1;
} else {
// Invalid
}
}
return true;
}
),
///////////////////////// TGA /////////////////////////
// TODO Not rigorous enough, keep it as last detector
// https://www.fileformat.info/format/tga/corion.htm
IIDetector(
II_FORMAT_TGA,
"tga",
"tga",
"image/tga",
[](size_t length, IIReadInterface &ri,
int64_t &width, int64_t &height,
std::vector<std::array<int64_t, 2>> &entrySizes) -> bool {
if (length < 18) {
return false;
}
auto buffer = ri.readBuffer((off_t) (length - 18), 18);
if (buffer.cmp(0, 18, "TRUEVISION-XFILE.\x00")) {
buffer = ri.readBuffer(0, 18);
width = buffer.readU16LE(12);
height = buffer.readU16LE(14);
return true;
}
buffer = ri.readBuffer(0, 18);
uint8_t idLen = buffer.readU8(0);
if (length < (size_t) idLen + 18) {
return false;
}
uint8_t colorMapType = buffer.readU8(1);
uint8_t imageType = buffer.readU8(2);
uint16_t firstColorMapEntryIndex = buffer.readU16LE(3);
uint16_t colorMapLength = buffer.readU16LE(5);
uint8_t colorMapEntrySize = buffer.readU8(7);
// uint16_t xOrigin = buffer.readU16LE(8);
// uint16_t yOrigin = buffer.readU16LE(10);
uint16_t w = buffer.readU16LE(12);
uint16_t h = buffer.readU16LE(14);
// uint8_t pixelDepth = buffer.readU8(16);
// uint8_t flags = buffer.readU8(17);
if (colorMapType == 0) { // no color map
if (imageType == 0
|| imageType == 2
|| imageType == 3
|| imageType == 10
|| imageType == 11
|| imageType == 32
|| imageType == 33) {
if (firstColorMapEntryIndex == 0
&& colorMapLength == 0
&& colorMapEntrySize == 0) {
width = w;
height = h;
return true;
}
}
} else if (colorMapType == 1) { // 256 entry palette
if (imageType == 1
|| imageType == 9) {
width = w;
height = h;
return true;
}
}
return false;
}
),
};
class ImageInfo {
public:
ImageInfo() = delete;
explicit ImageInfo(IIReadInterface &ri, IIFormat likelyFormat = II_FORMAT_UNKNOWN, bool mustBe = false) {
if (likelyFormat != II_FORMAT_UNKNOWN) {
for (const auto &detector : s_ii_detectors) {
if (detector.format == likelyFormat) {
if (tryDetector(detector, ri)) return;
break;
}
}
if (mustBe) {
m_err = II_ERR_UNRECOGNIZED_FORMAT;
return;
}
}
for (const auto &detector : s_ii_detectors) {
if (detector.format == likelyFormat) continue;
if (tryDetector(detector, ri)) return;
}
m_err = II_ERR_UNRECOGNIZED_FORMAT;
}
bool tryDetector(const IIDetector &detector, IIReadInterface &ri) {
bool match = detector.process(ri.length(), ri, m_width, m_height, m_entrySizes);
if (match) {
m_format = detector.format;
m_ext = detector.ext;
m_fullExt = detector.fullExt;
m_mimetype = detector.mimetype;
m_err = m_width != -1 && m_height != -1
? II_ERR_OK
: II_ERR_DECODE_SIZE_FAILED;
}
return match;
}
inline explicit operator bool() const {
return m_err == II_ERR_OK;
}
inline bool ok() const {
return m_err == II_ERR_OK;
}
inline IIFormat getFormat() const {
return m_format;
}
inline const char *getExt() const {
return m_ext;
}
inline const char *getFullExt() const {
return m_fullExt;
}
inline const char *getMimetype() const {
return m_mimetype;
}
inline int64_t getWidth() const {
return m_width;
}
inline int64_t getHeight() const {
return m_height;
}
inline std::array<int64_t, 2> getSize() const {
return {m_width, m_height};
}
inline std::vector<std::array<int64_t, 2>> getEntrySizes() const {
if (m_entrySizes.empty()) {
return {getSize()};
}
return m_entrySizes;
}
inline IIErrorCode getErrorCode() const {
return m_err;
}
inline const char *getErrorMsg() const {
switch (m_err) {
default:
return "Unknown error";
case II_ERR_OK:
return "Ok";
case II_ERR_UNRECOGNIZED_FORMAT:
return "Unrecognized format";
case II_ERR_DECODE_SIZE_FAILED:
return "Decode image size failed";
}
}
private:
IIFormat m_format = II_FORMAT_UNKNOWN;
const char *m_ext = "";
const char *m_fullExt = "";
const char *m_mimetype = "";
int64_t m_width = -1;
int64_t m_height = -1;
IIErrorCode m_err = II_ERR_OK;
std::vector<std::array<int64_t, 2>> m_entrySizes;
};
template<typename ReaderType, typename InputType>
static inline ImageInfo
getImageInfo(InputType file, IIFormat likelyFormat = II_FORMAT_UNKNOWN, bool mustBe = false) {
ReaderType fileReader(file);
size_t length = fileReader.size();
IIReadFunc read = [&](void *buf, off_t offset, size_t size) { fileReader.read(buf, offset, size); };
IIReadInterface ri(read, length);
return ImageInfo(ri, likelyFormat, mustBe);
}
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#endif //IMAGEINFO_IMAGEINFO_H
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化