mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-16 14:16:09 -06:00
1057 lines
28 KiB
C++
1057 lines
28 KiB
C++
#include <Draw/Draw.h>
|
|
#include "tif.h"
|
|
|
|
#define int8 tif_int8
|
|
#define int32 tif_int32
|
|
#define uint32 tif_uint32
|
|
#include <plugin/tif/lib/tiffio.h>
|
|
#undef int32
|
|
#undef uint32
|
|
|
|
#define LLOG(x) // LOG(x)
|
|
|
|
// #define DBGALLOC 1
|
|
|
|
NAMESPACE_UPP
|
|
|
|
#if DBGALLOC
|
|
double total_allocated = 0, total_freed = 0;
|
|
unsigned alloc_calls = 0, free_calls = 0, realloc_calls = 0;
|
|
Index<tsize_t> size_index;
|
|
Vector<int> size_alloc_calls, size_free_calls;
|
|
int op_id;
|
|
|
|
void dbgAddAlloc(void *p, tsize_t s)
|
|
{
|
|
++op_id;
|
|
total_allocated += s;
|
|
int i = size_index.Find(s);
|
|
if(i >= 0)
|
|
size_alloc_calls[i]++;
|
|
else
|
|
{
|
|
size_index.Add(s);
|
|
size_alloc_calls.Add(1);
|
|
size_free_calls.Add(0);
|
|
}
|
|
LLOG(op_id << " tAlloc(" << s << ") = " << p << ": blks: " << alloc_calls - free_calls << ", alloc = " << total_allocated << ", free = " << total_freed << ", diff = " << (total_allocated - total_freed));
|
|
}
|
|
|
|
void dbgAddFree(void *p, tsize_t s)
|
|
{
|
|
++op_id;
|
|
total_freed += s;
|
|
int i = size_index.Find(s);
|
|
if(i >= 0)
|
|
size_free_calls[i]++;
|
|
else
|
|
{
|
|
size_index.Add(s);
|
|
size_alloc_calls.Add(0);
|
|
size_free_calls.Add(1);
|
|
}
|
|
LLOG(op_id << " tFree(" << p << ") = " << s << ": blks: " << alloc_calls - free_calls << ", alloc = " << total_allocated << ", free = " << total_freed << ", diff = " << (total_allocated - total_freed));
|
|
}
|
|
|
|
void TiffAllocStat()
|
|
{
|
|
for(int i = 0; i < size_index.GetCount(); i++)
|
|
if(size_alloc_calls[i] != size_free_calls[i])
|
|
LOG("Alloc/free mismatch: size = " << size_index[i]
|
|
<< ", alloc = " << size_alloc_calls[i] << ", frees = " << size_free_calls[i]);
|
|
}
|
|
#endif
|
|
|
|
extern "C" tdata_t _TIFFmalloc(tsize_t s)
|
|
{
|
|
byte *p = new byte[s + 4];
|
|
Poke32le(p, s);
|
|
#if DBGALLOC
|
|
alloc_calls++;
|
|
dbgAddAlloc(p, s);
|
|
#endif
|
|
return (tdata_t)(p + 4);
|
|
}
|
|
|
|
extern "C" void _TIFFfree(tdata_t p)
|
|
{
|
|
if(p) {
|
|
byte *rawp = (byte *)p - 4;
|
|
#if DBGALLOC
|
|
free_calls++;
|
|
dbgAddFree(p, Peek32le(rawp));
|
|
#endif
|
|
delete[] (rawp);
|
|
}
|
|
}
|
|
|
|
extern "C" tdata_t _TIFFrealloc(tdata_t p, tsize_t s)
|
|
{
|
|
int oldsize = (p ? Peek32le((const byte *)p - 4) : 0);
|
|
if(s <= oldsize) {
|
|
Poke32le((byte *)p - 4, s);
|
|
return p;
|
|
}
|
|
byte *newptr = new byte[s + 4];
|
|
#if DBGALLOC
|
|
alloc_calls++;
|
|
dbgAddAlloc(newptr, s);
|
|
#endif
|
|
if(oldsize) {
|
|
memcpy(newptr + 4, p, min<int>(oldsize, s));
|
|
#if DBGALLOC
|
|
free_calls++;
|
|
dbgAddFree(newptr, oldsize);
|
|
#endif
|
|
delete[] ((byte *)p - 4);
|
|
}
|
|
Poke32le(newptr, s);
|
|
return (tdata_t)(newptr + 4);
|
|
}
|
|
|
|
extern "C" void _TIFFmemset(void* p, int v, tsize_t c) { memset(p, v, c); }
|
|
extern "C" void _TIFFmemcpy(void* d, const tdata_t s, tsize_t c) { memcpy(d, s, c); }
|
|
extern "C" int _TIFFmemcmp(const tdata_t p1, const tdata_t p2, tsize_t c) { return memcmp(p1, p2, c); }
|
|
|
|
static void Blt2to4(byte *dest, const byte *src, unsigned count)
|
|
{
|
|
byte b;
|
|
|
|
#define BLT2_4_4(o) \
|
|
b = src[(o)]; \
|
|
dest[2 * (o) + 0] = ((b >> 2) & 0x30) | ((b >> 4) & 0x03); \
|
|
dest[2 * (o) + 1] = ((b << 2) & 0x30) | (b & 0x03);
|
|
|
|
for(unsigned rep = count >> 5; rep; rep--) {
|
|
BLT2_4_4(0) BLT2_4_4(1) BLT2_4_4(2) BLT2_4_4(3)
|
|
BLT2_4_4(4) BLT2_4_4(5) BLT2_4_4(6) BLT2_4_4(7)
|
|
dest += 8 * 2;
|
|
src += 8;
|
|
}
|
|
if(count & 16) {
|
|
BLT2_4_4(0) BLT2_4_4(1) BLT2_4_4(2) BLT2_4_4(3)
|
|
dest += 4 * 2;
|
|
src += 4;
|
|
}
|
|
if(count & 8) {
|
|
BLT2_4_4(0) BLT2_4_4(1)
|
|
dest += 2 * 2;
|
|
src += 2;
|
|
}
|
|
if(count & 4) {
|
|
BLT2_4_4(0)
|
|
dest += 2;
|
|
src++;
|
|
}
|
|
switch(count & 3) {
|
|
case 0:
|
|
break;
|
|
|
|
case 1:
|
|
*dest = ((*src >> 2) & 0x30);
|
|
break;
|
|
|
|
case 2:
|
|
*dest = ((*src >> 2) & 0x30) | ((*src >> 4) & 0x03);
|
|
break;
|
|
|
|
case 3:
|
|
*dest++ = ((*src >> 2) & 0x30) | ((*src >> 4) & 0x03);
|
|
*dest = (*src << 2) & 0x30;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void BltPack11(byte *dest, const byte *src, byte bit_shift, unsigned count)
|
|
{
|
|
if(bit_shift == 0)
|
|
{ // simple case
|
|
#if defined(CPU_IA32)
|
|
#define BLT_PACK_11_4(o) *(unsigned *)(dest + (o)) = *(const unsigned *)(src + (o));
|
|
#else
|
|
#define BLT_PACK_11_4(o) dest[(o) + 0] = src[(o) + 0]; dest[(o) + 1] = src[(o) + 1]; \
|
|
dest[(o) + 2] = src[(o) + 2]; dest[(o) + 3] = src[(o) + 3];
|
|
#endif
|
|
for(unsigned rep = count >> 7; rep; rep--)
|
|
{
|
|
BLT_PACK_11_4(0) BLT_PACK_11_4(4) BLT_PACK_11_4(8) BLT_PACK_11_4(12)
|
|
dest += 16;
|
|
src += 16;
|
|
}
|
|
if(count & 0x40)
|
|
{
|
|
BLT_PACK_11_4(0) BLT_PACK_11_4(4)
|
|
dest += 8;
|
|
src += 8;
|
|
}
|
|
if(count & 0x20)
|
|
{
|
|
BLT_PACK_11_4(0)
|
|
dest += 4;
|
|
src += 4;
|
|
}
|
|
if(count & 0x10)
|
|
{
|
|
dest[0] = src[0]; dest[1] = src[1];
|
|
dest += 2;
|
|
src += 2;
|
|
}
|
|
if(count & 8)
|
|
*dest++ = *src++;
|
|
switch(count & 7)
|
|
{
|
|
case 0: break;
|
|
case 1: *dest = (*src & 0x80) | (*dest | 0x7f); break;
|
|
case 2: *dest = (*src & 0xc0) | (*dest | 0x3f); break;
|
|
case 3: *dest = (*src & 0xe0) | (*dest | 0x1f); break;
|
|
case 4: *dest = (*src & 0xf0) | (*dest | 0x0f); break;
|
|
case 5: *dest = (*src & 0xf8) | (*dest | 0x07); break;
|
|
case 6: *dest = (*src & 0xfc) | (*dest | 0x03); break;
|
|
case 7: *dest = (*src & 0xfe) | (*dest | 0x01); break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const byte shift1 = bit_shift, shift2 = 8 - bit_shift;
|
|
byte mask;
|
|
if(count + shift1 <= 8)
|
|
{ // touch just 1 byte
|
|
mask = ((1 << count) - 1) << (8 - count - shift1);
|
|
*dest = (*dest & ~mask) | ((*src >> shift1) & mask);
|
|
return;
|
|
}
|
|
mask = 0xff00 >> shift1;
|
|
*dest = (*dest & ~mask) | ((*src >> shift1) & mask);
|
|
dest++;
|
|
count -= shift2;
|
|
#define BLT_SHIFT_11_1(o) dest[(o)] = (src[(o)] << shift2) | (src[(o) + 1] >> shift1);
|
|
#define BLT_SHIFT_11_4(o) BLT_SHIFT_11_1((o)) BLT_SHIFT_11_1((o) + 1) BLT_SHIFT_11_1((o) + 2) BLT_SHIFT_11_1((o) + 3)
|
|
for(unsigned rep = count >> 6; rep; rep--)
|
|
{
|
|
BLT_SHIFT_11_4(0) BLT_SHIFT_11_4(4)
|
|
dest += 8;
|
|
src += 8;
|
|
}
|
|
if(count & 0x20)
|
|
{
|
|
BLT_SHIFT_11_4(0)
|
|
dest += 4;
|
|
src += 4;
|
|
}
|
|
if(count & 0x10)
|
|
{
|
|
BLT_SHIFT_11_1(0) BLT_SHIFT_11_1(1)
|
|
dest += 2;
|
|
src += 2;
|
|
}
|
|
if(count & 8)
|
|
{
|
|
BLT_SHIFT_11_1(0)
|
|
dest++;
|
|
src++;
|
|
}
|
|
if(count &= 7)
|
|
{
|
|
byte data = (count <= shift1 ? src[1] << shift2 : (src[1] << shift2) | (src[2] >> shift1));
|
|
mask = 0xff00 >> count;
|
|
*dest = (*dest & ~mask) | (data & mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void BltPack44(byte *dest, const byte *src, bool shift, unsigned count)
|
|
{
|
|
// RTIMING("BltPack44");
|
|
ASSERT(count > 0);
|
|
if(shift)
|
|
{
|
|
byte c = *src++, d;
|
|
*dest = (*dest & 0xF0) | (c >> 4);
|
|
dest++;
|
|
count--;
|
|
while(count >= 8)
|
|
{
|
|
d = src[0]; dest[0] = (c << 4) | (d >> 4);
|
|
c = src[1]; dest[1] = (d << 4) | (c >> 4);
|
|
d = src[2]; dest[2] = (c << 4) | (d >> 4);
|
|
c = src[3]; dest[3] = (d << 4) | (c >> 4);
|
|
src += 4;
|
|
dest += 4;
|
|
count -= 8;
|
|
}
|
|
if(count & 4)
|
|
{
|
|
d = src[0]; dest[0] = (c << 4) | (d >> 4);
|
|
c = src[1]; dest[1] = (d << 4) | (c >> 4);
|
|
src += 2;
|
|
dest += 2;
|
|
}
|
|
if(count & 2)
|
|
{
|
|
d = src[0]; dest[0] = (c << 4) | (d >> 4);
|
|
c = d;
|
|
src++;
|
|
dest++;
|
|
}
|
|
if(count & 1)
|
|
dest[0] = (dest[0] & 15) | (c << 4);
|
|
}
|
|
else
|
|
{
|
|
unsigned c2 = count >> 1;
|
|
if(c2)
|
|
memcpy(dest, src, c2);
|
|
if(count & 1)
|
|
dest[c2] = (dest[c2] & 15) | (src[c2] & 0xF0);
|
|
}
|
|
}
|
|
|
|
static void BltPack4(byte *dest, const byte *src, unsigned count)
|
|
{
|
|
#define BLT_PACK_4_4(o) dest[(o)] = src[4 * (o)]; dest[(o) + 1] = src[4 * (o) + 4]; \
|
|
dest[(o) + 2] = src[4 * (o) + 8]; dest[(o) + 3] = src[4 * (o) + 12];
|
|
for(unsigned rep = count >> 4; rep; rep--)
|
|
{
|
|
BLT_PACK_4_4(0) BLT_PACK_4_4(4) BLT_PACK_4_4(8) BLT_PACK_4_4(12)
|
|
dest += 16;
|
|
src += 4 * 16;
|
|
}
|
|
if(count & 8)
|
|
{
|
|
BLT_PACK_4_4(0) BLT_PACK_4_4(4)
|
|
dest += 8;
|
|
src += 4 * 8;
|
|
}
|
|
if(count & 4)
|
|
{
|
|
BLT_PACK_4_4(0)
|
|
dest += 4;
|
|
src += 4 * 4;
|
|
}
|
|
if(count & 2)
|
|
{
|
|
dest[0] = src[0]; dest[1] = src[4];
|
|
dest += 2;
|
|
src += 4 * 2;
|
|
}
|
|
if(count & 1)
|
|
dest[0] = src[0];
|
|
}
|
|
|
|
struct TIFRaster::Data : public TIFFRGBAImage {
|
|
Data(Stream& stream);
|
|
~Data();
|
|
|
|
bool Create();
|
|
Raster::Info GetInfo();
|
|
Raster::Line GetLine(int i);
|
|
bool SeekPage(int page);
|
|
|
|
static void Warning(const char* module, const char* fmt, va_list ap);
|
|
static void Error(const char* module, const char* fmt, va_list ap);
|
|
|
|
RasterFormat format;
|
|
|
|
Stream& stream;
|
|
TIFF *tiff;
|
|
|
|
static tsize_t ReadStream(thandle_t fd, tdata_t buf, tsize_t size);
|
|
static tsize_t WriteStream(thandle_t fd, tdata_t buf, tsize_t size);
|
|
static toff_t SeekStream(thandle_t fd, toff_t off, int whence);
|
|
static int CloseStream(thandle_t fd);
|
|
static toff_t SizeStream(thandle_t fd);
|
|
static int MapStream(thandle_t fd, tdata_t *pbase, toff_t *psize);
|
|
static void UnmapStream(thandle_t fd, tdata_t base, toff_t size);
|
|
|
|
struct Page {
|
|
uint32 width, height;
|
|
uint16 bits_per_sample;
|
|
uint16 samples_per_pixel;
|
|
uint16 photometric;
|
|
Size dot_size;
|
|
bool alpha;
|
|
};
|
|
Array<Page> pages;
|
|
int page_index;
|
|
|
|
byte *MapDown(int x, int y, int count, bool read);
|
|
byte *MapUp(int x, int y, int count, bool read);
|
|
void Flush();
|
|
void Flush(int y);
|
|
|
|
Size size;
|
|
int bpp;
|
|
int row_bytes;
|
|
int cache_size;
|
|
bool alpha;
|
|
Vector<byte> imagebuf;
|
|
FileStream filebuffer;
|
|
struct Row {
|
|
Row() : x(0), size(0) {}
|
|
|
|
Buffer<byte> mapping;
|
|
int x, size;
|
|
};
|
|
enum { MAX_CACHE_SIZE = 50000000 };
|
|
RGBA palette[256];
|
|
int palette_count;
|
|
Buffer<Row> rows;
|
|
int64 mapping_offset;
|
|
int mapping_size;
|
|
Vector<uint32> buffer;
|
|
tileContigRoutine contig;
|
|
tileSeparateRoutine separate;
|
|
int skewfac;
|
|
// void (*pack)(TIFFImageHelper *helper, uint32 x, uint32 y, uint32 w, uint32 h);
|
|
// String warnings;
|
|
// String errors;
|
|
};
|
|
|
|
extern "C" {
|
|
|
|
TIFFErrorHandler _TIFFwarningHandler = TIFRaster::Data::Warning;
|
|
TIFFErrorHandler _TIFFerrorHandler = TIFRaster::Data::Error;
|
|
|
|
};
|
|
static void packTileRGB(TIFRaster::Data *helper, uint32 x, uint32 y, uint32 w, uint32 h)
|
|
{
|
|
if(helper->alpha) {
|
|
int x4 = 4 * x, w4 = 4 * w;
|
|
// byte *dest = helper->dest.GetUpScan(y) + 3 * x;
|
|
const byte *src = (const byte *)helper->buffer.Begin();
|
|
// unsigned srow = sizeof(uint32) * w; //, drow = helper->dest.GetUpRowBytes();
|
|
for(; h; h--, /*src += srow,*/ /*dest += drow*/ y++) {
|
|
for(byte *dest = helper->MapUp(x4, y, w4, false), *end = dest + w4; dest < end; dest += 4, src += 4) {
|
|
dest[0] = src[2];
|
|
dest[1] = src[1];
|
|
dest[2] = src[0];
|
|
dest[3] = src[3];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int x3 = 3 * x, w3 = 3 * w;
|
|
// byte *dest = helper->dest.GetUpScan(y) + 3 * x;
|
|
const byte *src = (const byte *)helper->buffer.Begin();
|
|
// unsigned srow = sizeof(uint32) * w; //, drow = helper->dest.GetUpRowBytes();
|
|
for(; h; h--, /*src += srow,*/ /*dest += drow*/ y++) {
|
|
for(byte *dest = helper->MapUp(x3, y, w3, false), *end = dest + w3; dest < end; dest += 3, src += 4) {
|
|
dest[0] = src[2];
|
|
dest[1] = src[1];
|
|
dest[2] = src[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void putContig1(TIFFRGBAImage *img, tif_uint32 *cp,
|
|
tif_uint32 x, tif_uint32 y, tif_uint32 w, tif_uint32 h,
|
|
tif_int32 fromskew, tif_int32 toskew, byte *pp)
|
|
{
|
|
TIFRaster::Data *helper = (TIFRaster::Data *)img;
|
|
Size size = helper->size;
|
|
int iw = toskew + w;
|
|
bool keep_y = (iw >= 0);
|
|
int x8 = x >> 3;
|
|
int w8 = ((x + w + 7) >> 3) - x8;
|
|
bool read = !!((x | w) & 7) && (int)w < helper->size.cx;
|
|
// byte *dest = helper->dest.GetUpScan(y) + (x >> 3);
|
|
// int drow = (keep_y ? helper->dest.GetUpRowBytes() : -helper->dest.GetUpRowBytes());
|
|
int drow = keep_y ? 1 : -1;
|
|
const byte *src = pp;
|
|
int srow = (fromskew + w - 1) / helper->skewfac + 1;
|
|
for(; h; h--, y += drow /*dest += drow*/, src += srow)
|
|
BltPack11(helper->MapUp(x8, y, w8, read), src, (byte)(x & 7), w);
|
|
}
|
|
|
|
static void putContig4(TIFFRGBAImage *img, tif_uint32 *cp,
|
|
tif_uint32 x, tif_uint32 y, tif_uint32 w, tif_uint32 h,
|
|
tif_int32 fromskew, tif_int32 toskew, byte *pp)
|
|
{
|
|
TIFRaster::Data *helper = (TIFRaster::Data *)img;
|
|
Size size = helper->size; //dest.GetSize();
|
|
int iw = toskew + w;
|
|
bool keep_y = (iw >= 0);
|
|
int x2 = x >> 1;
|
|
int w2 = ((x + w + 1) >> 1) - x2;
|
|
bool read = !!((x | w) & 1) && (int)w < helper->size.cx;
|
|
// byte *dest = helper->dest.GetUpScan(y) + (x >> 1);
|
|
bool shift = (x & 1);
|
|
// int drow = (keep_y ? helper->dest.GetUpRowBytes() : -helper->dest.GetUpRowBytes());
|
|
int drow = (keep_y ? 1 : -1);
|
|
const byte *src = pp;
|
|
int srow = (fromskew + w - 1) / helper->skewfac + 1;
|
|
for(; h; h--, y /*dest*/ += drow, src += srow)
|
|
BltPack44(helper->MapUp(x2, y, w2, read), src, shift, w);
|
|
}
|
|
|
|
static void putContig8(TIFFRGBAImage *img, tif_uint32 *cp,
|
|
tif_uint32 x, tif_uint32 y, tif_uint32 w, tif_uint32 h,
|
|
tif_int32 fromskew, tif_int32 toskew, byte *pp)
|
|
{
|
|
TIFRaster::Data *helper = (TIFRaster::Data *)img;
|
|
Size size = helper->size;
|
|
int iw = toskew + w;
|
|
bool keep_y = (iw >= 0);
|
|
// byte *dest = helper->dest.GetUpScan(y) + x;
|
|
// int drow = (keep_y ? helper->dest.GetUpRowBytes() : -helper->dest.GetUpRowBytes());
|
|
int drow = (keep_y ? 1 : -1);
|
|
const byte *src = pp;
|
|
int srow = (fromskew + w - 1) / helper->skewfac + 1;
|
|
for(; h; h--, y /*dest*/ += drow, src += srow)
|
|
memcpy(helper->MapUp(x, y, w, false), src, w);
|
|
}
|
|
|
|
static void putContigRGB(TIFFRGBAImage *img, tif_uint32 *cp, tif_uint32 x, tif_uint32 y, tif_uint32 w, tif_uint32 h,
|
|
tif_int32 fromskew, tif_int32 toskew, byte *pp)
|
|
{
|
|
TIFRaster::Data *helper = (TIFRaster::Data *)img;
|
|
Size size = helper->size;
|
|
int iw = toskew + w;
|
|
int wh = w * h;
|
|
if(wh > helper->buffer.GetCount())
|
|
helper->buffer.SetCount(wh);
|
|
bool keep_y = (iw >= 0);
|
|
helper->contig(img, (tif_uint32 *)(keep_y ? &helper->buffer[0] : &helper->buffer[0] + w * (h - 1)),
|
|
0, 0, w, h, fromskew, keep_y ? 0 : -2 * (int)w, pp);
|
|
packTileRGB(helper, x, keep_y ? y : y - h + 1, w, h);
|
|
}
|
|
|
|
static void putSeparate(TIFFRGBAImage *img, tif_uint32 *cp,
|
|
tif_uint32 x, tif_uint32 y, tif_uint32 w, tif_uint32 h,
|
|
tif_int32 fromskew, tif_int32 toskew, byte *r, byte *g, byte *b, byte *a)
|
|
{
|
|
TIFRaster::Data *helper = (TIFRaster::Data *)img;
|
|
Size size = helper->size;
|
|
int wh = w * h;
|
|
if(wh > helper->buffer.GetCount())
|
|
helper->buffer.SetCount(wh);
|
|
int iw = toskew + w;
|
|
bool keep_y = (iw >= 0);
|
|
helper->separate(img, (tif_uint32 *)(keep_y ? &helper->buffer[0] : &helper->buffer[0] + w * (h - 1)),
|
|
0, 0, w, h, fromskew, keep_y ? 0 : -2 * (int)w, r, g, b, a);
|
|
packTileRGB(helper, x, keep_y ? y : y - h + 1, w, h);
|
|
}
|
|
|
|
byte *TIFRaster::Data::MapUp(int x, int y, int count, bool read)
|
|
{
|
|
return MapDown(x, size.cy - 1 - y, count, read);
|
|
}
|
|
|
|
byte *TIFRaster::Data::MapDown(int x, int y, int count, bool read)
|
|
{
|
|
if(!imagebuf.IsEmpty())
|
|
return &imagebuf[row_bytes * y] + x;
|
|
else {
|
|
ASSERT(filebuffer.IsOpen());
|
|
Row& row = rows[y];
|
|
if(row.size >= count && row.x <= x && row.x + row.size >= x + count)
|
|
return &row.mapping[x - row.x];
|
|
if(cache_size + count >= MAX_CACHE_SIZE)
|
|
Flush();
|
|
row.mapping.Alloc(count);
|
|
row.x = x;
|
|
row.size = count;
|
|
cache_size += count;
|
|
if(read) {
|
|
filebuffer.Seek(row_bytes * y + x);
|
|
filebuffer.GetAll(row.mapping, count);
|
|
}
|
|
return row.mapping;
|
|
}
|
|
}
|
|
|
|
void TIFRaster::Data::Flush()
|
|
{
|
|
LLOG("Flush, cache size = " << cache_size);
|
|
for(int y = 0; y < size.cy; y++)
|
|
Flush(y);
|
|
ASSERT(cache_size == 0);
|
|
}
|
|
|
|
void TIFRaster::Data::Flush(int y)
|
|
{
|
|
Row& row = rows[y];
|
|
if(filebuffer.IsOpen() && row.size > 0) {
|
|
int64 fpos = row_bytes * y + row.x;
|
|
// RLOG("writing row " << y << " from " << fpos << " + " << row.size << " = " << (fpos + row.size));
|
|
filebuffer.Seek(fpos);
|
|
filebuffer.Put(row.mapping, row.size);
|
|
cache_size -= row.size;
|
|
row.size = 0;
|
|
row.mapping.Clear();
|
|
}
|
|
}
|
|
|
|
void TIFRaster::Data::Warning(const char *fn, const char *fmt, va_list ap)
|
|
{
|
|
if(!memcmp(fn, "tiff@", 5) && IsDigit(fn[5])) {
|
|
int addr = stou(fn + 5);
|
|
if(addr != -1 && addr != 0) {
|
|
TIFRaster::Data& wrapper = *reinterpret_cast<TIFRaster::Data *>(addr);
|
|
LLOG("TIF warning: " << VFormat(fmt, ap));
|
|
// RLOG("TiffWrapper::Warning: " << wrapper.errors);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TIFRaster::Data::Error(const char *fn, const char *fmt, va_list ap)
|
|
{
|
|
if(!memcmp(fn, "tiff@", 5) && IsDigit(fn[5])) {
|
|
int addr = stou(fn + 5);
|
|
if(addr != -1 && addr != 0) {
|
|
Data& wrapper = *reinterpret_cast<Data *>(addr);
|
|
LLOG("TIF error: " << VFormat(fmt, ap));
|
|
// RLOG("TiffWrapper::Error: " << wrapper.errors);
|
|
}
|
|
}
|
|
}
|
|
|
|
tsize_t TIFRaster::Data::ReadStream(thandle_t fd, tdata_t buf, tsize_t size)
|
|
{
|
|
Data& wrapper = *reinterpret_cast<Data *>(fd);
|
|
ASSERT(wrapper.stream.IsOpen());
|
|
// RLOG("TiffStream::TIFRaster::Data & " << (int)wrapper.stream.GetPos() << ", count = " << size
|
|
// << ", end = " << (int)(wrapper.stream.GetPos() + size));
|
|
return wrapper.stream.Get(buf, size);
|
|
}
|
|
|
|
tsize_t TIFRaster::Data::WriteStream(thandle_t fd, tdata_t buf, tsize_t size)
|
|
{
|
|
NEVER();
|
|
return 0;
|
|
}
|
|
|
|
toff_t TIFRaster::Data::SeekStream(thandle_t fd, toff_t off, int whence)
|
|
{
|
|
Data& wrapper = *reinterpret_cast<Data *>(fd);
|
|
ASSERT(wrapper.stream.IsOpen());
|
|
toff_t size = (toff_t)wrapper.stream.GetSize();
|
|
toff_t destpos = (toff_t)(off + (whence == 1 ? wrapper.stream.GetPos() : whence == 2 ? size : 0));
|
|
wrapper.stream.Seek(destpos);
|
|
// RLOG("TIFRaster::Data::SeekStream -> " << (int)off << ", whence = " << whence << " -> pos = " << (int)wrapper.stream.GetPos());
|
|
return (toff_t)wrapper.stream.GetPos();
|
|
}
|
|
|
|
int TIFRaster::Data::CloseStream(thandle_t fd)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
toff_t TIFRaster::Data::SizeStream(thandle_t fd)
|
|
{
|
|
Data& wrapper = *reinterpret_cast<Data *>(fd);
|
|
ASSERT(wrapper.stream.IsOpen());
|
|
// RLOG("TIFRaster::Data::SizeStream -> " << (int)wrapper.stream.GetSize());
|
|
return (toff_t)wrapper.stream.GetSize();
|
|
}
|
|
|
|
int TIFRaster::Data::MapStream(thandle_t fd, tdata_t *pbase, toff_t *psize)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void TIFRaster::Data::UnmapStream(thandle_t fd, tdata_t base, toff_t size)
|
|
{
|
|
}
|
|
|
|
TIFRaster::Data::Data(Stream& stream)
|
|
: stream(stream)
|
|
{
|
|
tiff = NULL;
|
|
page_index = 0;
|
|
}
|
|
|
|
TIFRaster::Data::~Data()
|
|
{
|
|
if(tiff)
|
|
TIFFClose(tiff);
|
|
}
|
|
|
|
bool TIFRaster::Data::Create()
|
|
{
|
|
tiff = TIFFClientOpen("tiff@" + Format64((intptr_t)this), "r", reinterpret_cast<thandle_t>(this),
|
|
ReadStream, WriteStream, SeekStream, CloseStream, SizeStream, MapStream, UnmapStream);
|
|
if(!tiff)
|
|
return false;
|
|
|
|
int count = TIFFNumberOfDirectories(tiff);
|
|
if(count <= 0)
|
|
return false;
|
|
for(int i = 0; i < count; i++) {
|
|
Page& page = pages.Add();
|
|
TIFFSetDirectory(tiff, i);
|
|
TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &page.width);
|
|
TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &page.height);
|
|
float xres, yres;
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_XRESOLUTION, &xres);
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_YRESOLUTION, &yres);
|
|
uint16 resunit;
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_RESOLUTIONUNIT, &resunit);
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &page.bits_per_sample);
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &page.samples_per_pixel);
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_PHOTOMETRIC, &page.photometric);
|
|
double dots_per_unit = (resunit == RESUNIT_INCH ? 600.0 : resunit == RESUNIT_CENTIMETER
|
|
? 600.0 / 2.54 : 0);
|
|
page.dot_size.cx = (xres ? fround(page.width * dots_per_unit / xres) : 0);
|
|
page.dot_size.cy = (yres ? fround(page.height * dots_per_unit / yres) : 0);
|
|
page.alpha = false;
|
|
uint16 extrasamples, *sampletypes;
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_EXTRASAMPLES, &extrasamples, &sampletypes);
|
|
for(int e = 0; e < extrasamples; e++)
|
|
if(sampletypes[e] == EXTRASAMPLE_ASSOCALPHA) {
|
|
page.alpha = true;
|
|
break;
|
|
}
|
|
}
|
|
return SeekPage(0);
|
|
}
|
|
|
|
bool TIFRaster::Data::SeekPage(int page)
|
|
{
|
|
TIFFSetDirectory(tiff, page_index = page);
|
|
char emsg[1024];
|
|
if(!TIFFRGBAImageBegin(this, tiff, 0, emsg)) {
|
|
TIFFError(TIFFFileName(tiff), emsg);
|
|
return false;
|
|
}
|
|
|
|
if(isContig) {
|
|
contig = put.contig;
|
|
put.contig = putContigRGB;
|
|
}
|
|
else {
|
|
separate = put.separate;
|
|
put.separate = putSeparate;
|
|
}
|
|
if(alpha = pages[page_index].alpha) {
|
|
format.Set32le(0xFF << 16, 0xFF << 8, 0xFF, 0xFF << 24);
|
|
bpp = 32;
|
|
}
|
|
else {
|
|
format.Set24le(0xFF << 16, 0xFF << 8, 0xFF);
|
|
bpp = 24;
|
|
}
|
|
palette_count = 0;
|
|
if(isContig && (photometric == PHOTOMETRIC_PALETTE
|
|
|| photometric == PHOTOMETRIC_MINISWHITE || photometric == PHOTOMETRIC_MINISBLACK)
|
|
&& (bitspersample == 1 || bitspersample == 2 || bitspersample == 4 || bitspersample == 8)) {
|
|
bpp = 8;
|
|
tif_uint32 **ppal = (photometric == PHOTOMETRIC_PALETTE ? PALmap : BWmap);
|
|
ASSERT(ppal);
|
|
// byte rshift = 8 - img.bitspersample;
|
|
palette_count = 1 << min<int>(bitspersample, 8);
|
|
byte mask = (1 << bitspersample) - 1;
|
|
int part_last = 8 / bitspersample - 1;
|
|
int i;
|
|
for(i = 0; i <= mask; i++) {
|
|
uint32 rgba = ppal[i][part_last];
|
|
palette[i].r = (byte)TIFFGetR(rgba);
|
|
palette[i].g = (byte)TIFFGetG(rgba);
|
|
palette[i].b = (byte)TIFFGetB(rgba);
|
|
palette[i].a = 255;
|
|
}
|
|
put.contig = putContig8;
|
|
switch(bitspersample) {
|
|
case 1: bpp = 1; put.contig = putContig1; format.Set1mf(); break;
|
|
case 2: bpp = 4; put.contig = putContig4; format.Set4mf(); break;
|
|
case 4: bpp = 4; put.contig = putContig4; format.Set4mf(); break;
|
|
case 8: format.Set8(); break;
|
|
default: NEVER();
|
|
}
|
|
skewfac = 8 / bitspersample;
|
|
}
|
|
|
|
size = Size(width, height);
|
|
row_bytes = (bpp * width + 31) >> 5 << 2;
|
|
int64 bytes = row_bytes * (int64)height;
|
|
String tmpfile;
|
|
if(bytes >= 100000000) {
|
|
tmpfile = GetTempFileName();
|
|
if(!filebuffer.Open(tmpfile, FileStream::CREATE))
|
|
return false;
|
|
filebuffer.SetSize(bytes);
|
|
if(filebuffer.IsError()) {
|
|
filebuffer.Close();
|
|
FileDelete(tmpfile);
|
|
return false;
|
|
}
|
|
rows.Alloc(size.cy);
|
|
}
|
|
else
|
|
imagebuf.SetCount(size.cy * row_bytes, 0);
|
|
|
|
// RTIMING("TiffWrapper::GetArray/RGBAImageGet");
|
|
|
|
bool res = TIFFRGBAImageGet(this, 0, width, height);
|
|
TIFFRGBAImageEnd(this);
|
|
|
|
if(filebuffer.IsOpen()) {
|
|
Flush();
|
|
if(filebuffer.IsError() || !res) {
|
|
filebuffer.Close();
|
|
FileDelete(tmpfile);
|
|
return false;
|
|
}
|
|
imagebuf.SetCount(size.cy * row_bytes);
|
|
filebuffer.Seek(0);
|
|
filebuffer.GetAll(imagebuf.Begin(), imagebuf.GetCount());
|
|
filebuffer.Close();
|
|
FileDelete(tmpfile);
|
|
}
|
|
// imagebuf.SetDotSize(pages[page_index].dot_size);
|
|
// dest_image.palette = palette;
|
|
// return dest_image;
|
|
return true;
|
|
}
|
|
|
|
Raster::Info TIFRaster::Data::GetInfo()
|
|
{
|
|
const Page& page = pages[page_index];
|
|
Raster::Info out;
|
|
out.kind = (page.alpha ? IMAGE_ALPHA : IMAGE_OPAQUE);
|
|
out.bpp = bpp;
|
|
out.colors = 0;
|
|
out.dots = page.dot_size;
|
|
out.hotspot = Null;
|
|
return out;
|
|
}
|
|
|
|
TIFRaster::TIFRaster()
|
|
{
|
|
}
|
|
|
|
TIFRaster::~TIFRaster()
|
|
{
|
|
}
|
|
|
|
bool TIFRaster::Create()
|
|
{
|
|
data = new Data(GetStream());
|
|
return data->Create();
|
|
}
|
|
|
|
Size TIFRaster::GetSize()
|
|
{
|
|
return data->size;
|
|
}
|
|
|
|
Raster::Info TIFRaster::GetInfo()
|
|
{
|
|
return data->GetInfo();
|
|
}
|
|
|
|
Raster::Line TIFRaster::GetLine(int line)
|
|
{
|
|
return Raster::Line(&data->imagebuf[data->row_bytes * line], this, false);
|
|
}
|
|
|
|
int TIFRaster::GetPaletteCount()
|
|
{
|
|
return data->palette_count;
|
|
}
|
|
|
|
const RGBA *TIFRaster::GetPalette()
|
|
{
|
|
return data->palette;
|
|
}
|
|
|
|
const RasterFormat *TIFRaster::GetFormat()
|
|
{
|
|
return &data->format;
|
|
}
|
|
|
|
int TIFRaster::GetPageCount()
|
|
{
|
|
return data->pages.GetCount();
|
|
}
|
|
|
|
void TIFRaster::SeekPage(int n)
|
|
{
|
|
data->SeekPage(n);
|
|
}
|
|
|
|
class TIFEncoder::Data {
|
|
public:
|
|
Data(Stream& stream, RasterFormat& format);
|
|
~Data();
|
|
|
|
void Start(Size size, int bpp, const RGBA *palette);
|
|
void WriteLineRaw(const byte *line);
|
|
|
|
private:
|
|
Stream& stream;
|
|
TIFF *tiff;
|
|
Size size;
|
|
int bpp;
|
|
const RGBA *palette;
|
|
Vector<byte> rowbuf;
|
|
int linebytes;
|
|
RasterFormat& format;
|
|
int line;
|
|
|
|
static tsize_t ReadStream(thandle_t fd, tdata_t buf, tsize_t size);
|
|
static tsize_t WriteStream(thandle_t fd, tdata_t buf, tsize_t size);
|
|
static toff_t SeekStream(thandle_t fd, toff_t off, int whence);
|
|
static int CloseStream(thandle_t fd);
|
|
static toff_t SizeStream(thandle_t fd);
|
|
static int MapStream(thandle_t fd, tdata_t *pbase, toff_t *psize);
|
|
static void UnmapStream(thandle_t fd, tdata_t base, toff_t size);
|
|
};
|
|
|
|
TIFEncoder::Data::Data(Stream& stream, RasterFormat& format)
|
|
: stream(stream), format(format)
|
|
{
|
|
tiff = NULL;
|
|
}
|
|
|
|
TIFEncoder::Data::~Data()
|
|
{
|
|
if(tiff) TIFFClose(tiff);
|
|
}
|
|
|
|
tsize_t TIFEncoder::Data::ReadStream(thandle_t fd, tdata_t buf, tsize_t size)
|
|
{
|
|
Data& wrapper = *reinterpret_cast<Data *>(fd);
|
|
ASSERT(wrapper.stream.IsOpen());
|
|
// RLOG("TiffStream::ReadStream & " << (int)wrapper.stream.GetPos() << ", count = " << size
|
|
// << ", end = " << (int)(wrapper.stream.GetPos() + size));
|
|
return wrapper.stream.Get(buf, size);
|
|
return 0;
|
|
}
|
|
|
|
tsize_t TIFEncoder::Data::WriteStream(thandle_t fd, tdata_t buf, tsize_t size)
|
|
{
|
|
Data& wrapper = *reinterpret_cast<Data *>(fd);
|
|
ASSERT(wrapper.stream.IsOpen());
|
|
// RLOG("TIFRaster::Data::WriteStream & " << (int)wrapper.stream.GetPos() << ", count = " << (int)size
|
|
// << ", end = " << (int)(wrapper.stream.GetPos() + size));
|
|
wrapper.stream.Put(buf, size);
|
|
return size;
|
|
}
|
|
|
|
toff_t TIFEncoder::Data::SeekStream(thandle_t fd, toff_t off, int whence)
|
|
{
|
|
Data& wrapper = *reinterpret_cast<Data *>(fd);
|
|
ASSERT(wrapper.stream.IsOpen());
|
|
toff_t size = (toff_t)wrapper.stream.GetSize();
|
|
toff_t destpos = (toff_t)(off + (whence == 1 ? wrapper.stream.GetPos() : whence == 2 ? size : 0));
|
|
if(destpos > size) {
|
|
wrapper.stream.Seek(size);
|
|
wrapper.stream.Put((int)0, (int)(destpos - size));
|
|
}
|
|
else
|
|
wrapper.stream.Seek(destpos);
|
|
// RLOG("TIFRaster::Data::SeekStream -> " << (int)off << ", whence = " << whence << " -> pos = " << (int)wrapper.stream.GetPos());
|
|
return (toff_t)wrapper.stream.GetPos();
|
|
}
|
|
|
|
int TIFEncoder::Data::CloseStream(thandle_t fd)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
toff_t TIFEncoder::Data::SizeStream(thandle_t fd)
|
|
{
|
|
Data& wrapper = *reinterpret_cast<Data *>(fd);
|
|
ASSERT(wrapper.stream.IsOpen());
|
|
// RLOG("TIFRaster::Data::SizeStream -> " << (int)wrapper.stream.GetSize());
|
|
return (toff_t)wrapper.stream.GetSize();
|
|
}
|
|
|
|
int TIFEncoder::Data::MapStream(thandle_t fd, tdata_t *pbase, toff_t *psize)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void TIFEncoder::Data::UnmapStream(thandle_t fd, tdata_t base, toff_t size)
|
|
{
|
|
}
|
|
|
|
void TIFEncoder::Data::Start(Size sz, int bpp_, const RGBA *palette)
|
|
{
|
|
size = sz;
|
|
bpp = bpp_;
|
|
line = 0;
|
|
|
|
tiff = TIFFClientOpen("tiff@" + Format64((intptr_t)this), "w", reinterpret_cast<thandle_t>(this),
|
|
ReadStream, WriteStream, SeekStream, CloseStream, SizeStream, MapStream, UnmapStream);
|
|
|
|
TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, size.cx);
|
|
TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, size.cy);
|
|
TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, min<int>(bpp, 8));
|
|
TIFFSetField(tiff, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
|
|
TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, bpp <= 8 ? PHOTOMETRIC_PALETTE : PHOTOMETRIC_RGB);
|
|
TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, bpp <= 8 ? 1 : bpp != 32 ? 3 : 4);
|
|
TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, 1);
|
|
TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
|
|
if(bpp == 32) {
|
|
uint16 es = EXTRASAMPLE_ASSOCALPHA;
|
|
TIFFSetField(tiff, TIFFTAG_EXTRASAMPLES, 1, &es);
|
|
}
|
|
// TIFFSetField(tiff, TIFFTAG_REFERENCEBLACKWHITE, refblackwhite);
|
|
// TIFFSetField(tiff, TIFFTAG_TRANSFERFUNCTION, gray);
|
|
// TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
|
|
switch(bpp) {
|
|
case 1: format.Set1mf(); break;
|
|
case 2: format.Set2mf(); break;
|
|
case 4: format.Set4mf(); break;
|
|
case 8: format.Set8(); break;
|
|
default: NEVER();
|
|
case 24: format.Set24le(0xFF, 0xFF << 8, 0xFF << 16); break;
|
|
case 32: format.Set32le(0xFF, 0xFF << 8, 0xFF << 16, 0xFF << 24); break;
|
|
}
|
|
if(bpp <= 8) {
|
|
uint16 rpal[256], gpal[256], bpal[256];
|
|
int c = 1 << bpp;
|
|
memset(rpal, 0, sizeof(uint16) * c);
|
|
memset(gpal, 0, sizeof(uint16) * c);
|
|
memset(bpal, 0, sizeof(uint16) * c);
|
|
for(int i = 0; i < c; i++) {
|
|
rpal[i] = palette[i].r << 8;
|
|
gpal[i] = palette[i].g << 8;
|
|
bpal[i] = palette[i].b << 8;
|
|
}
|
|
TIFFSetField(tiff, TIFFTAG_COLORMAP, rpal, gpal, bpal);
|
|
}
|
|
int rowbytes = (bpp * size.cx + 31) >> 5 << 2;
|
|
rowbuf.SetCount(rowbytes);
|
|
linebytes = format.GetByteCount(size.cx);
|
|
}
|
|
|
|
void TIFEncoder::Data::WriteLineRaw(const byte *s)
|
|
{
|
|
memcpy(rowbuf.Begin(), s, linebytes);
|
|
TIFFWriteScanline(tiff, rowbuf.Begin(), line, 0);
|
|
if(++line >= size.cy) {
|
|
TIFFClose(tiff);
|
|
tiff = NULL;
|
|
}
|
|
}
|
|
|
|
TIFEncoder::TIFEncoder(int bpp)
|
|
: bpp(bpp)
|
|
{
|
|
}
|
|
|
|
TIFEncoder::~TIFEncoder()
|
|
{
|
|
}
|
|
|
|
int TIFEncoder::GetPaletteCount()
|
|
{
|
|
return (bpp > 8 ? 0 : 1 << bpp);
|
|
}
|
|
|
|
void TIFEncoder::Start(Size sz)
|
|
{
|
|
data = new Data(GetStream(), format);
|
|
data->Start(sz, bpp, bpp <= 8 ? GetPalette() : NULL);
|
|
}
|
|
|
|
void TIFEncoder::WriteLineRaw(const byte *s)
|
|
{
|
|
data->WriteLineRaw(s);
|
|
}
|
|
|
|
END_UPP_NAMESPACE
|