plugins: add io_tiff
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Photopea
|
||||
|
||||
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.
|
||||
|
||||
https://github.com/photopea/UTIF.js
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,461 @@
|
||||
#include "iron_array.h"
|
||||
#include "iron_gpu.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void *gpu_create_texture_from_bytes(void *buffer, int width, int height, int format);
|
||||
void console_info(char *s);
|
||||
buffer_t *iron_inflate(buffer_t *bytes, bool raw);
|
||||
|
||||
// TIFF tags
|
||||
#define TAG_IMAGE_WIDTH 256
|
||||
#define TAG_IMAGE_LENGTH 257
|
||||
#define TAG_BITS_PER_SAMPLE 258
|
||||
#define TAG_COMPRESSION 259
|
||||
#define TAG_PHOTOMETRIC 262
|
||||
#define TAG_STRIP_OFFSETS 273
|
||||
#define TAG_SAMPLES_PER_PIXEL 277
|
||||
#define TAG_ROWS_PER_STRIP 278
|
||||
#define TAG_STRIP_BYTE_COUNTS 279
|
||||
#define TAG_PLANAR_CONFIGURATION 284
|
||||
#define TAG_PREDICTOR 317
|
||||
#define TAG_COLOR_MAP 320
|
||||
#define TAG_TILE_WIDTH 322
|
||||
#define TAG_TILE_LENGTH 323
|
||||
#define TAG_TILE_OFFSETS 324
|
||||
#define TAG_TILE_BYTE_COUNTS 325
|
||||
#define TAG_SAMPLE_FORMAT 339
|
||||
|
||||
// Compression types
|
||||
#define COMP_NONE 1
|
||||
#define COMP_LZW 5
|
||||
#define COMP_DEFLATE 8
|
||||
#define COMP_PACKBITS 32773
|
||||
#define COMP_DEFLATE2 32946
|
||||
|
||||
static bool tiff_le;
|
||||
static uint8_t *tiff_buf;
|
||||
static size_t tiff_size;
|
||||
|
||||
static uint16_t r16(size_t pos) {
|
||||
uint8_t *p = tiff_buf + pos;
|
||||
return tiff_le ? (uint16_t)(p[0] | p[1] << 8) : (uint16_t)(p[0] << 8 | p[1]);
|
||||
}
|
||||
|
||||
static uint32_t r32(size_t pos) {
|
||||
uint8_t *p = tiff_buf + pos;
|
||||
return tiff_le ? (uint32_t)(p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24) : (uint32_t)(p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]);
|
||||
}
|
||||
|
||||
static uint32_t ifd_val(size_t vpos, uint16_t type, uint32_t idx) {
|
||||
if (type == 3 || type == 8)
|
||||
return r16(vpos + idx * 2); // SHORT / SSHORT
|
||||
if (type == 4 || type == 9)
|
||||
return r32(vpos + idx * 4); // LONG / SLONG
|
||||
return tiff_buf[vpos + idx]; // BYTE and others
|
||||
}
|
||||
|
||||
static void packbits_decode(uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) {
|
||||
size_t ip = 0, op = 0;
|
||||
while (ip < in_len && op < out_len) {
|
||||
int8_t n = (int8_t)in[ip++];
|
||||
if (n >= 0) {
|
||||
size_t cnt = (size_t)(n + 1);
|
||||
if (op + cnt > out_len)
|
||||
cnt = out_len - op;
|
||||
memcpy(out + op, in + ip, cnt);
|
||||
ip += (size_t)(n + 1);
|
||||
op += cnt;
|
||||
}
|
||||
else if (n != -128) {
|
||||
size_t cnt = (size_t)(-n + 1);
|
||||
if (op + cnt > out_len)
|
||||
cnt = out_len - op;
|
||||
memset(out + op, in[ip++], cnt);
|
||||
op += cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TIFF LZW — MSB-first bit order
|
||||
static void lzw_decode(uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) {
|
||||
uint16_t prefix[4096];
|
||||
uint8_t suffix[4096];
|
||||
uint8_t first[4096];
|
||||
uint8_t stack[4096];
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
prefix[i] = 0xFFFF;
|
||||
suffix[i] = first[i] = (uint8_t)i;
|
||||
}
|
||||
|
||||
int bit_len = 9;
|
||||
int nxt = 258;
|
||||
size_t ipos = 0;
|
||||
size_t opos = 0;
|
||||
int bit_pos = 0;
|
||||
int old_code = -1;
|
||||
|
||||
while (opos < out_len) {
|
||||
// Read bit_len bits, MSB first
|
||||
int code = 0;
|
||||
for (int b = 0; b < bit_len; b++) {
|
||||
if (ipos >= in_len)
|
||||
return;
|
||||
code = (code << 1) | ((in[ipos] >> (7 - bit_pos)) & 1);
|
||||
if (++bit_pos == 8) {
|
||||
bit_pos = 0;
|
||||
ipos++;
|
||||
}
|
||||
}
|
||||
|
||||
if (code == 256) {
|
||||
bit_len = 9;
|
||||
nxt = 258;
|
||||
old_code = -1;
|
||||
continue;
|
||||
}
|
||||
if (code == 257)
|
||||
break;
|
||||
|
||||
int stack_top = 0;
|
||||
int cur = code;
|
||||
|
||||
if (code >= nxt) {
|
||||
// Special case: new code equals next table entry
|
||||
stack[stack_top++] = first[old_code];
|
||||
cur = old_code;
|
||||
}
|
||||
|
||||
while (prefix[cur] != 0xFFFF && stack_top < 4096) {
|
||||
stack[stack_top++] = suffix[cur];
|
||||
cur = prefix[cur];
|
||||
}
|
||||
stack[stack_top++] = suffix[cur]; // root / first char
|
||||
|
||||
if (old_code >= 0 && nxt < 4096) {
|
||||
prefix[nxt] = (uint16_t)old_code;
|
||||
suffix[nxt] = stack[stack_top - 1]; // first char of current string
|
||||
first[nxt] = first[old_code];
|
||||
nxt++;
|
||||
if (nxt == (1 << bit_len) && bit_len < 12)
|
||||
bit_len++;
|
||||
}
|
||||
|
||||
for (int s = stack_top - 1; s >= 0 && opos < out_len; s--)
|
||||
out[opos++] = stack[s];
|
||||
|
||||
old_code = code;
|
||||
}
|
||||
}
|
||||
|
||||
// Undo horizontal differencing predictor (predictor == 2)
|
||||
static void undo_predictor(uint8_t *data, uint32_t w, uint32_t h, uint16_t spp, uint16_t bps) {
|
||||
uint32_t row_bytes = w * spp * ((bps + 7) / 8);
|
||||
for (uint32_t y = 0; y < h; y++) {
|
||||
uint8_t *row = data + (size_t)y * row_bytes;
|
||||
if (bps == 16) {
|
||||
uint16_t *p = (uint16_t *)row;
|
||||
for (uint32_t x = 1; x < w; x++)
|
||||
for (uint16_t c = 0; c < spp; c++)
|
||||
p[x * spp + c] += p[(x - 1) * spp + c];
|
||||
}
|
||||
else { // 8-bit
|
||||
for (uint32_t x = 1; x < w; x++)
|
||||
for (uint16_t c = 0; c < spp; c++)
|
||||
row[x * spp + c] += row[(x - 1) * spp + c];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *io_tiff_parse(uint8_t *buf, size_t buf_size) {
|
||||
tiff_buf = buf;
|
||||
tiff_size = buf_size;
|
||||
|
||||
if (buf_size < 8)
|
||||
return NULL;
|
||||
if (buf[0] == 'I' && buf[1] == 'I')
|
||||
tiff_le = true;
|
||||
else if (buf[0] == 'M' && buf[1] == 'M')
|
||||
tiff_le = false;
|
||||
else
|
||||
return NULL;
|
||||
if (r16(2) != 42)
|
||||
return NULL;
|
||||
|
||||
uint32_t ifd_offset = r32(4);
|
||||
|
||||
uint32_t width = 0, height = 0;
|
||||
uint16_t bps = 8;
|
||||
uint16_t bps_arr[4] = {8, 8, 8, 8};
|
||||
uint16_t compression = COMP_NONE;
|
||||
uint16_t photometric = 2;
|
||||
uint16_t spp = 3;
|
||||
uint16_t predictor = 1;
|
||||
uint32_t rows_per_strip = 0xFFFFFFFF;
|
||||
uint32_t tile_w = 0, tile_h = 0;
|
||||
uint16_t sample_fmt = 1;
|
||||
|
||||
uint32_t *strip_offsets = NULL;
|
||||
uint32_t *strip_counts = NULL;
|
||||
uint32_t num_strips = 0;
|
||||
uint32_t *tile_offsets = NULL;
|
||||
uint32_t *tile_counts = NULL;
|
||||
uint32_t num_tiles = 0;
|
||||
uint16_t *color_map = NULL;
|
||||
uint32_t color_map_len = 0;
|
||||
|
||||
static const size_t type_sizes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
|
||||
|
||||
size_t pos = ifd_offset;
|
||||
uint16_t num_entries = r16(pos);
|
||||
pos += 2;
|
||||
|
||||
for (uint16_t i = 0; i < num_entries; i++, pos += 12) {
|
||||
uint16_t tag = r16(pos);
|
||||
uint16_t type = r16(pos + 2);
|
||||
uint32_t count = r32(pos + 4);
|
||||
uint32_t vraw = r32(pos + 8);
|
||||
|
||||
size_t tsz = (type < 13) ? type_sizes[type] : 1;
|
||||
size_t vpos = (tsz * count <= 4) ? (pos + 8) : vraw;
|
||||
|
||||
switch (tag) {
|
||||
case TAG_IMAGE_WIDTH:
|
||||
width = ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_IMAGE_LENGTH:
|
||||
height = ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_COMPRESSION:
|
||||
compression = (uint16_t)ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_PHOTOMETRIC:
|
||||
photometric = (uint16_t)ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_SAMPLES_PER_PIXEL:
|
||||
spp = (uint16_t)ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_PREDICTOR:
|
||||
predictor = (uint16_t)ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_SAMPLE_FORMAT:
|
||||
sample_fmt = (uint16_t)ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_ROWS_PER_STRIP:
|
||||
rows_per_strip = ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_TILE_WIDTH:
|
||||
tile_w = ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_TILE_LENGTH:
|
||||
tile_h = ifd_val(vpos, type, 0);
|
||||
break;
|
||||
case TAG_BITS_PER_SAMPLE:
|
||||
bps = (uint16_t)ifd_val(vpos, type, 0);
|
||||
for (uint32_t c = 0; c < count && c < 4; c++)
|
||||
bps_arr[c] = (uint16_t)ifd_val(vpos, type, c);
|
||||
break;
|
||||
case TAG_STRIP_OFFSETS:
|
||||
num_strips = count;
|
||||
strip_offsets = (uint32_t *)malloc(count * sizeof(uint32_t));
|
||||
for (uint32_t s = 0; s < count; s++)
|
||||
strip_offsets[s] = ifd_val(vpos, type, s);
|
||||
break;
|
||||
case TAG_STRIP_BYTE_COUNTS:
|
||||
strip_counts = (uint32_t *)malloc(count * sizeof(uint32_t));
|
||||
for (uint32_t s = 0; s < count; s++)
|
||||
strip_counts[s] = ifd_val(vpos, type, s);
|
||||
break;
|
||||
case TAG_TILE_OFFSETS:
|
||||
num_tiles = count;
|
||||
tile_offsets = (uint32_t *)malloc(count * sizeof(uint32_t));
|
||||
for (uint32_t t = 0; t < count; t++)
|
||||
tile_offsets[t] = ifd_val(vpos, type, t);
|
||||
break;
|
||||
case TAG_TILE_BYTE_COUNTS:
|
||||
tile_counts = (uint32_t *)malloc(count * sizeof(uint32_t));
|
||||
for (uint32_t t = 0; t < count; t++)
|
||||
tile_counts[t] = ifd_val(vpos, type, t);
|
||||
break;
|
||||
case TAG_COLOR_MAP:
|
||||
color_map_len = count;
|
||||
color_map = (uint16_t *)malloc(count * sizeof(uint16_t));
|
||||
for (uint32_t c = 0; c < count; c++)
|
||||
color_map[c] = (uint16_t)ifd_val(vpos, 3, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
return NULL;
|
||||
|
||||
uint8_t *rgba = (uint8_t *)calloc((size_t)width * height * 4, 1);
|
||||
uint32_t bps_bytes = (bps + 7) / 8;
|
||||
|
||||
bool tiled = (tile_w > 0 && tile_h > 0 && tile_offsets != NULL);
|
||||
uint32_t tiles_x = tiled ? (width + tile_w - 1) / tile_w : 1;
|
||||
uint32_t blk_cnt = tiled ? num_tiles : num_strips;
|
||||
|
||||
for (uint32_t blk = 0; blk < blk_cnt; blk++) {
|
||||
uint32_t off = tiled ? tile_offsets[blk] : strip_offsets[blk];
|
||||
uint32_t blen = tiled ? tile_counts[blk] : strip_counts[blk];
|
||||
if (off + blen > buf_size)
|
||||
break;
|
||||
|
||||
uint32_t blk_x, blk_y, blk_w, blk_h;
|
||||
if (tiled) {
|
||||
blk_x = (blk % tiles_x) * tile_w;
|
||||
blk_y = (blk / tiles_x) * tile_h;
|
||||
blk_w = tile_w;
|
||||
blk_h = tile_h;
|
||||
}
|
||||
else {
|
||||
blk_x = 0;
|
||||
blk_y = blk * rows_per_strip;
|
||||
blk_w = width;
|
||||
blk_h = rows_per_strip;
|
||||
if (blk_y + blk_h > height)
|
||||
blk_h = height - blk_y;
|
||||
}
|
||||
|
||||
size_t row_stride = (size_t)blk_w * spp * bps_bytes;
|
||||
size_t expected_sz = (size_t)blk_h * row_stride;
|
||||
uint8_t *raw = NULL;
|
||||
bool raw_free = false;
|
||||
|
||||
if (compression == COMP_NONE) {
|
||||
raw = buf + off;
|
||||
}
|
||||
else if (compression == COMP_PACKBITS) {
|
||||
raw = (uint8_t *)malloc(expected_sz);
|
||||
raw_free = true;
|
||||
packbits_decode(buf + off, blen, raw, expected_sz);
|
||||
}
|
||||
else if (compression == COMP_LZW) {
|
||||
raw = (uint8_t *)malloc(expected_sz);
|
||||
raw_free = true;
|
||||
lzw_decode(buf + off, blen, raw, expected_sz);
|
||||
}
|
||||
#ifdef WITH_COMPRESS
|
||||
else if (compression == COMP_DEFLATE || compression == COMP_DEFLATE2) {
|
||||
buffer_t compressed;
|
||||
compressed.buffer = buf + off;
|
||||
compressed.length = compressed.capacity = blen;
|
||||
buffer_t *decomp = iron_inflate(&compressed, false);
|
||||
raw = decomp->buffer;
|
||||
raw_free = false;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
console_info("Error: TIFF compression type not supported");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!raw)
|
||||
continue;
|
||||
|
||||
if (predictor == 2 && compression != COMP_NONE)
|
||||
undo_predictor(raw, blk_w, blk_h, spp, bps);
|
||||
|
||||
for (uint32_t row = 0; row < blk_h; row++) {
|
||||
uint32_t img_y = blk_y + row;
|
||||
if (img_y >= height)
|
||||
break;
|
||||
uint8_t *src = raw + row * row_stride;
|
||||
|
||||
for (uint32_t col = 0; col < blk_w; col++) {
|
||||
uint32_t img_x = blk_x + col;
|
||||
if (img_x >= width)
|
||||
break;
|
||||
uint8_t *dst = rgba + ((size_t)img_y * width + img_x) * 4;
|
||||
|
||||
uint8_t r = 0, g = 0, b = 0, a = 255;
|
||||
|
||||
if (photometric == 3) {
|
||||
// Palette / indexed color
|
||||
uint32_t idx = 0;
|
||||
if (bps == 8)
|
||||
idx = src[col];
|
||||
else if (bps == 4)
|
||||
idx = (col & 1) ? (src[col / 2] & 0x0F) : (src[col / 2] >> 4);
|
||||
else if (bps == 1)
|
||||
idx = (src[col / 8] >> (7 - col % 8)) & 1;
|
||||
uint32_t nc = color_map_len / 3;
|
||||
if (color_map && idx < nc) {
|
||||
r = color_map[idx] >> 8;
|
||||
g = color_map[nc + idx] >> 8;
|
||||
b = color_map[nc * 2 + idx] >> 8;
|
||||
}
|
||||
}
|
||||
else if (bps == 8) {
|
||||
uint8_t *p = src + col * spp;
|
||||
if (photometric <= 1) {
|
||||
uint8_t v = photometric == 0 ? 255 - p[0] : p[0];
|
||||
r = g = b = v;
|
||||
a = spp >= 2 ? p[1] : 255;
|
||||
}
|
||||
else {
|
||||
r = p[0];
|
||||
g = p[1];
|
||||
b = p[2];
|
||||
a = spp >= 4 ? p[3] : 255;
|
||||
}
|
||||
}
|
||||
else if (bps == 16) {
|
||||
uint16_t *p = (uint16_t *)(src + col * spp * 2);
|
||||
if (photometric <= 1) {
|
||||
uint8_t v = photometric == 0 ? 255 - p[0] / 257 : p[0] / 257;
|
||||
r = g = b = v;
|
||||
a = spp >= 2 ? p[1] / 257 : 255;
|
||||
}
|
||||
else {
|
||||
r = p[0] / 257;
|
||||
g = p[1] / 257;
|
||||
b = p[2] / 257;
|
||||
a = spp >= 4 ? p[3] / 257 : 255;
|
||||
}
|
||||
}
|
||||
else if (bps == 32 && sample_fmt == 3) {
|
||||
// 32-bit float
|
||||
float *p = (float *)(src + col * spp * 4);
|
||||
if (photometric <= 1) {
|
||||
float v = photometric == 0 ? 1.0f - p[0] : p[0];
|
||||
v = v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v;
|
||||
r = g = b = (uint8_t)(v * 255.0f);
|
||||
a = spp >= 2 ? (uint8_t)((p[1] < 0.0f ? 0.0f : p[1] > 1.0f ? 1.0f : p[1]) * 255.0f) : 255;
|
||||
}
|
||||
else {
|
||||
float fr = p[0] < 0.0f ? 0.0f : p[0] > 1.0f ? 1.0f : p[0];
|
||||
float fg = p[1] < 0.0f ? 0.0f : p[1] > 1.0f ? 1.0f : p[1];
|
||||
float fb = p[2] < 0.0f ? 0.0f : p[2] > 1.0f ? 1.0f : p[2];
|
||||
r = (uint8_t)(fr * 255.0f);
|
||||
g = (uint8_t)(fg * 255.0f);
|
||||
b = (uint8_t)(fb * 255.0f);
|
||||
a = spp >= 4 ? (uint8_t)((p[3] < 0.0f ? 0.0f : p[3] > 1.0f ? 1.0f : p[3]) * 255.0f) : 255;
|
||||
}
|
||||
}
|
||||
|
||||
dst[0] = r;
|
||||
dst[1] = g;
|
||||
dst[2] = b;
|
||||
dst[3] = a;
|
||||
}
|
||||
}
|
||||
|
||||
if (raw_free)
|
||||
free(raw);
|
||||
}
|
||||
|
||||
free(strip_offsets);
|
||||
free(strip_counts);
|
||||
free(tile_offsets);
|
||||
free(tile_counts);
|
||||
free(color_map);
|
||||
|
||||
buffer_t *b = (buffer_t *)malloc(sizeof(buffer_t));
|
||||
b->buffer = rgba;
|
||||
b->length = b->capacity = (uint32_t)((size_t)width * height * 4);
|
||||
return gpu_create_texture_from_bytes(b, (int)width, (int)height, GPU_TEXTURE_FORMAT_RGBA32);
|
||||
}
|
||||
Reference in New Issue
Block a user