mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 22:02:49 -06:00
1466 lines
No EOL
39 KiB
C++
1466 lines
No EOL
39 KiB
C++
#include <Draw/Draw.h>
|
|
#include "tif.h"
|
|
#include <Painter/Painter.h>
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
#define tif_int32 long
|
|
#define tif_uint32 unsigned long
|
|
#else
|
|
#define tif_int32 int
|
|
#define tif_uint32 unsigned int
|
|
#endif
|
|
|
|
#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 + 16];
|
|
Poke32le(p, s);
|
|
#if DBGALLOC
|
|
alloc_calls++;
|
|
dbgAddAlloc(p, s);
|
|
#endif
|
|
return (tdata_t)(p + 16);
|
|
}
|
|
|
|
extern "C" void* _TIFFcalloc(tmsize_t nmemb, tmsize_t siz)
|
|
{
|
|
if( nmemb == 0 || siz == 0 )
|
|
return ((void *) NULL);
|
|
|
|
return _TIFFmalloc(nmemb * siz);
|
|
}
|
|
|
|
extern "C" void _TIFFfree(tdata_t p)
|
|
{
|
|
if(p) {
|
|
byte *rawp = (byte *)p - 16;
|
|
#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 - 16) : 0);
|
|
byte *newptr = new byte[s + 16];
|
|
#if DBGALLOC
|
|
alloc_calls++;
|
|
dbgAddAlloc(newptr, s);
|
|
#endif
|
|
if(oldsize) {
|
|
memcpy(newptr + 16, p, min<int>(oldsize, s));
|
|
#if DBGALLOC
|
|
free_calls++;
|
|
dbgAddFree(newptr, oldsize);
|
|
#endif
|
|
delete[] ((byte *)p - 16);
|
|
}
|
|
Poke32le(newptr, s);
|
|
return (tdata_t)(newptr + 16);
|
|
}
|
|
|
|
extern "C" void _TIFFmemset(void* p, int v, tmsize_t c) { memset(p, v, c); }
|
|
extern "C" void _TIFFmemcpy(void* d, const void *s, tmsize_t c) { memcpy(d, s, c); }
|
|
extern "C" int _TIFFmemcmp(const void *p1, const void *p2, tmsize_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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// add support for 2 bpp tif - Massimo Del Fedele
|
|
// un-optimized way....
|
|
// bit_shift should be shift on destination, NOT source
|
|
static void BltPack22(byte *dest, const byte *src, byte bit_shift, unsigned count)
|
|
{
|
|
unsigned c2 = count >> 2;
|
|
count &= 0x03;
|
|
byte shift1, shift2;
|
|
byte sMask1, sMask2;
|
|
byte dMask1, dMask2;
|
|
|
|
if(!bit_shift) // fast path
|
|
{
|
|
if(c2)
|
|
{
|
|
memcpy(dest, src, c2);
|
|
dest += c2;
|
|
src += c2;
|
|
}
|
|
switch(count)
|
|
{
|
|
default:
|
|
case 0:
|
|
break;
|
|
|
|
case 1:
|
|
*dest = (*dest & 0x3f) | (*src & 0x3f);
|
|
break;
|
|
|
|
case 2:
|
|
*dest = (*dest & 0x0f) | (*src & 0x0f);
|
|
break;
|
|
|
|
case 3:
|
|
*dest = (*dest & 0x03) | (*src & 0x03);
|
|
break;
|
|
} // switch(count)
|
|
}
|
|
else // slow path
|
|
{
|
|
bit_shift <<= 1;
|
|
shift1 = bit_shift;
|
|
shift2 = (8 - bit_shift);
|
|
sMask1 = 0xff << shift1;
|
|
dMask1 = 0xff << shift2;
|
|
sMask2 = 0xff >> shift2;
|
|
dMask2 = 0xff >> shift1;
|
|
while(c2--)
|
|
{
|
|
*dest = (*dest & dMask1) | ((*src & sMask1) >> shift1);
|
|
dest++;
|
|
*dest = (*dest & dMask2) | ((*src & sMask2) << shift2);
|
|
src++;
|
|
}
|
|
switch(count)
|
|
{
|
|
case 0:
|
|
default:
|
|
break;
|
|
|
|
case 1:
|
|
*dest = (*dest & ~(0xc0 >> bit_shift)) | ((*src & 0xc0) >> bit_shift);
|
|
break;
|
|
|
|
case 2:
|
|
if(bit_shift <= 4)
|
|
{
|
|
*dest = (*dest & ~(0xf0 >> bit_shift)) | ((*src & 0xf0) >> bit_shift);
|
|
}
|
|
else
|
|
{
|
|
*dest = (*dest & ~(0xc0 >> bit_shift)) | ((*src & 0xc0) >> bit_shift);
|
|
dest++;
|
|
*dest = (*dest & 0x3f) | ((*src & 0x30) << 2);
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
if(bit_shift <= 2)
|
|
{
|
|
*dest = (*dest & ~(0xfc >> bit_shift)) | ((*src & 0xfc) >> bit_shift);
|
|
}
|
|
else if(bit_shift <= 4)
|
|
{
|
|
*dest = (*dest & ~(0xf0 >> bit_shift)) | ((*src & 0xf0) >> bit_shift);
|
|
dest++;
|
|
*dest = (*dest & 0x3f) | ((*src & 0x0c) << 4);
|
|
}
|
|
else
|
|
{
|
|
*dest = (*dest & 0xfc) | ((*src & 0xc0) >> 6);
|
|
dest++;
|
|
*dest = (*dest & 0x0f) | ((*src & 0x3c) << 2);
|
|
}
|
|
break;
|
|
} // switch(count)
|
|
} // end slow path
|
|
|
|
}
|
|
|
|
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];
|
|
}
|
|
*/
|
|
static tsize_t ReadStream(thandle_t fd, tdata_t buf, tsize_t size)
|
|
{
|
|
Stream *stream = reinterpret_cast<Stream *>(fd);
|
|
if(!stream->IsOpen())
|
|
return 0;
|
|
// RLOG("TiffStream::TIFRaster::Data & " << (int)wrapper.stream.GetPos() << ", count = " << size
|
|
// << ", end = " << (int)(wrapper.stream.GetPos() + size));
|
|
return stream->Get(buf, size);
|
|
}
|
|
|
|
static tsize_t WriteStream(thandle_t fd, tdata_t buf, tsize_t size)
|
|
{
|
|
Stream *stream = reinterpret_cast<Stream *>(fd);
|
|
ASSERT(stream->IsOpen());
|
|
stream->Put(buf, size);
|
|
return stream->IsError() ? 0 : size;
|
|
}
|
|
|
|
static toff_t SeekStream(thandle_t fd, toff_t off, int whence)
|
|
{
|
|
Stream *stream = reinterpret_cast<Stream *>(fd);
|
|
ASSERT(stream->IsOpen());
|
|
toff_t size = (toff_t)stream->GetSize();
|
|
toff_t destpos = (toff_t)(off + (whence == 1 ? stream->GetPos() : whence == 2 ? size : 0));
|
|
stream->Seek(destpos);
|
|
// RLOG("TIFRaster::Data::SeekStream -> " << (int)off << ", whence = " << whence << " -> pos = " << (int)wrapper.stream.GetPos());
|
|
return (toff_t)stream->GetPos();
|
|
}
|
|
|
|
static int CloseStream(thandle_t fd)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int CloseOwnedStream(thandle_t fd)
|
|
{
|
|
delete reinterpret_cast<Stream *>(fd);
|
|
return 0;
|
|
}
|
|
|
|
static toff_t SizeStream(thandle_t fd)
|
|
{
|
|
Stream *stream = reinterpret_cast<Stream *>(fd);
|
|
ASSERT(stream->IsOpen());
|
|
// RLOG("TIFRaster::Data::SizeStream -> " << (int)wrapper.stream.GetSize());
|
|
return (toff_t)stream->GetSize();
|
|
}
|
|
|
|
static int MapStream(thandle_t fd, tdata_t *pbase, toff_t *psize)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static void UnmapStream(thandle_t fd, tdata_t base, toff_t size)
|
|
{
|
|
}
|
|
|
|
struct ::tiff *TIFFFileStreamOpen(const char *filename, const char *mode)
|
|
{
|
|
One<FileStream> fs = new FileStream;
|
|
int m = _TIFFgetMode(mode, "TIFFOpen");
|
|
dword fmode = FileStream::READ;
|
|
|
|
switch(m) {
|
|
case O_RDONLY: {
|
|
fmode = FileStream::READ;
|
|
break;
|
|
}
|
|
case O_RDWR: {
|
|
fmode = FileStream::READWRITE;
|
|
break;
|
|
}
|
|
case O_RDWR|O_CREAT:
|
|
case O_RDWR|O_TRUNC:
|
|
case O_RDWR|O_CREAT|O_TRUNC: {
|
|
fmode = FileStream::CREATE;
|
|
break;
|
|
}
|
|
}
|
|
if(!fs->Open(filename, fmode))
|
|
return NULL;
|
|
return TIFFStreamOpen(filename, mode, fs.Detach(), true);
|
|
}
|
|
/*
|
|
|
|
struct ::tiff* TIFFWrapOpen(const char *filename, const char *mode){
|
|
return ::TIFFOpen(filename, mode);
|
|
}
|
|
|
|
int TIFFWrapGetField(::tiff* tif_data, uint32 tag, ...){
|
|
va_list ap;
|
|
return ::TIFFGetField(tif_data, tag, ap);
|
|
}
|
|
*/
|
|
|
|
struct ::tiff *TIFFStreamOpen(const char *filename, const char *mode, Stream *stream, bool destruct)
|
|
{
|
|
return TIFFClientOpen(filename, mode, reinterpret_cast<thandle_t>(stream),
|
|
&ReadStream, &WriteStream,
|
|
&SeekStream,
|
|
destruct ? &CloseOwnedStream : &CloseStream,
|
|
&SizeStream,
|
|
&MapStream, &UnmapStream);
|
|
}
|
|
|
|
|
|
struct TIFRaster::Data : public TIFFRGBAImage {
|
|
Data(Stream& stream);
|
|
~Data();
|
|
|
|
bool Create();
|
|
Raster::Info GetInfo();
|
|
Raster::Line GetLine(int i, Raster *owner);
|
|
bool SeekPage(int page);
|
|
bool FetchPage();
|
|
|
|
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;
|
|
|
|
struct Page {
|
|
uint32 width, height;
|
|
uint16 bits_per_sample;
|
|
uint16 samples_per_pixel;
|
|
uint16 photometric;
|
|
uint16 orientation;
|
|
Size dot_size;
|
|
bool alpha;
|
|
};
|
|
Array<Page> pages;
|
|
int page_index;
|
|
|
|
VectorMap<String, Value> attr;
|
|
|
|
byte *MapDown(int x, int y, int count);
|
|
byte *MapUp(int x, int y, int count);
|
|
|
|
Size size;
|
|
int bpp;
|
|
int row_bytes;
|
|
int cache_size;
|
|
bool alpha;
|
|
bool page_open;
|
|
bool page_fetched;
|
|
bool page_error;
|
|
Vector<byte> imagebuf;
|
|
struct Row {
|
|
Row() {}
|
|
|
|
Buffer<byte> mapping;
|
|
};
|
|
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), *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), *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), src, (byte)(x & 7), w);
|
|
}
|
|
|
|
static void putContig2(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 x4 = x >> 2;
|
|
int w4 = ((x + w + 3) >> 2) - x4;
|
|
bool read = !!((x | w) & 3) && (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)
|
|
BltPack22(helper->MapUp(x4, y, w4), src, (byte)(x & 3), 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), 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), 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)
|
|
{
|
|
return MapDown(x, size.cy - 1 - y, count);
|
|
}
|
|
|
|
byte *TIFRaster::Data::MapDown(int x, int y, int count)
|
|
{
|
|
return &imagebuf[row_bytes * y] + x;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
TIFRaster::Data::Data(Stream& stream)
|
|
: stream(stream)
|
|
{
|
|
tiff = NULL;
|
|
page_index = 0;
|
|
cache_size = 0;
|
|
page_open = false;
|
|
page_fetched = false;
|
|
page_error = false;
|
|
}
|
|
|
|
TIFRaster::Data::~Data()
|
|
{
|
|
if(tiff) {
|
|
if(page_open)
|
|
TIFFRGBAImageEnd(this);
|
|
TIFFClose(tiff);
|
|
}
|
|
}
|
|
|
|
bool TIFRaster::Data::Create()
|
|
{
|
|
TIFFSetErrorHandler(NULL);
|
|
TIFFSetErrorHandlerExt(NULL);
|
|
TIFFSetWarningHandler(NULL);
|
|
TIFFSetWarningHandlerExt(NULL);
|
|
|
|
tiff = TIFFStreamOpen("tiff@" + Format64((intptr_t)this), "r", &stream);
|
|
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);
|
|
TIFFGetFieldDefaulted(tiff, TIFFTAG_ORIENTATION, &page.orientation);
|
|
attr.GetAdd("tiff_orientation") = Value((int)page.orientation);
|
|
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;
|
|
}
|
|
|
|
if(i == 0) {
|
|
const word TIFFTAG_GEOPIXELSCALE = 33550,
|
|
TIFFTAG_GEOTIEPOINTS = 33922,
|
|
TIFFTAG_GEOTRANSMATRIX = 34264,
|
|
TIFFTAG_GEOKEYDIRECTORY = 34735,
|
|
TIFFTAG_GEODOUBLEPARAMS = 34736,
|
|
TIFFTAG_GEOASCIIPARAMS = 34737;
|
|
|
|
word count = 0;
|
|
word *geokeys = nullptr;
|
|
bool pixel_is_area = true; // Default is true, usually only DTM/DEM use 'RasterPixelIsPoint' and generally include the GeoKey in such case
|
|
|
|
int dpc = 0;
|
|
double *doubleparams = 0;
|
|
|
|
int apc = 0;
|
|
char *asciiparams = 0;
|
|
|
|
TIFFGetField(tiff, TIFFTAG_GEODOUBLEPARAMS, &dpc, &doubleparams);
|
|
TIFFGetField(tiff, TIFFTAG_GEOASCIIPARAMS, &apc, &asciiparams);
|
|
|
|
if(TIFFGetField(tiff, TIFFTAG_GEOKEYDIRECTORY, &count, &geokeys) &&
|
|
count >= 4 && (count % 4) == 0 && geokeys[0] == 1)
|
|
for(int i = 4; i + 3 < count; i += 4) {
|
|
const word GTRasterTypeGeoKey = 1025,
|
|
RasterPixelIsArea = 1,
|
|
GeographicTypeGeoKey = 2048,
|
|
ProjectedCSTypeGeoKey = 3072;
|
|
|
|
word value = geokeys[i + 3];
|
|
|
|
switch(geokeys[i]) {
|
|
case GTRasterTypeGeoKey:
|
|
pixel_is_area = value == RasterPixelIsArea;
|
|
break;
|
|
case GeographicTypeGeoKey:
|
|
case ProjectedCSTypeGeoKey:
|
|
attr.GetAdd("epsg") = value;
|
|
break;
|
|
}
|
|
|
|
switch(geokeys[i+1]){ // by type
|
|
case TIFFTAG_GEOASCIIPARAMS:
|
|
if(geokeys[i+3]<=apc) attr.GetAdd(Format("GK%d",geokeys[i])) = Value(String(&asciiparams[geokeys[i+3]],geokeys[i+2]));
|
|
break;
|
|
case TIFFTAG_GEODOUBLEPARAMS:
|
|
if(geokeys[i+3]<=dpc) attr.GetAdd(Format("GK%d",geokeys[i])) = Value(doubleparams[geokeys[i+3]]);
|
|
break;
|
|
default:
|
|
attr.GetAdd(Format("GK%d",geokeys[i])) = Value(geokeys[i+3]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
double geomatrix[6];
|
|
geomatrix[0] = 0.0;
|
|
geomatrix[1] = 1.0;
|
|
geomatrix[2] = 0.0;
|
|
geomatrix[3] = 0.0;
|
|
geomatrix[4] = 0.0;
|
|
geomatrix[5] = 1.0;
|
|
|
|
auto AdjustMatrixOrientation = [&](double dx, double dy) {
|
|
geomatrix[0] += (geomatrix[1] * dx + geomatrix[2] * dy);
|
|
geomatrix[3] += (geomatrix[4] * dx + geomatrix[5] * dy);
|
|
if(dx!=0){
|
|
geomatrix[1] = -geomatrix[1];
|
|
geomatrix[4] = -geomatrix[4];
|
|
}
|
|
if(dy!=0){
|
|
geomatrix[2] = -geomatrix[2];
|
|
geomatrix[5] = -geomatrix[5];
|
|
}
|
|
};
|
|
|
|
auto NormalizeOrientation = [&] {
|
|
if(!pixel_is_area) {
|
|
geomatrix[0] -= (geomatrix[1] * 0.5 + geomatrix[2] * 0.5);
|
|
geomatrix[3] -= (geomatrix[4] * 0.5 + geomatrix[5] * 0.5);
|
|
}
|
|
if(page.orientation>=5){
|
|
Swap(geomatrix[1],geomatrix[2]);
|
|
Swap(geomatrix[4],geomatrix[5]);
|
|
}
|
|
switch(page.orientation){
|
|
case 2:
|
|
AdjustMatrixOrientation(page.width, 0);
|
|
break;
|
|
case 3:
|
|
AdjustMatrixOrientation(page.width, page.height);
|
|
break;
|
|
case 4:
|
|
AdjustMatrixOrientation(0, page.height);
|
|
break;
|
|
case 6:
|
|
AdjustMatrixOrientation(page.height, 0);
|
|
break;
|
|
case 7:
|
|
AdjustMatrixOrientation(page.height, page.width);
|
|
break;
|
|
case 8:
|
|
AdjustMatrixOrientation(0, page.width);
|
|
break;
|
|
}
|
|
// TIFF orientations:
|
|
// 1 = FLIP_MIRROR_VERT,
|
|
// 2 = FLIP_ROTATE_180,
|
|
// 3 = FLIP_MIRROR_HORZ,
|
|
// 4 = FLIP_NONE,
|
|
// 5 = FLIP_ROTATE_CLOCKWISE,
|
|
// 6 = FLIP_TRANSPOSE,
|
|
// 7 = FLIP_ROTATE_ANTICLOCKWISE,
|
|
// 8 = FLIP_TRANSVERSE,
|
|
};
|
|
|
|
double *data;
|
|
auto FinishMatrix = [&] {
|
|
ValueArray va;
|
|
for(int i = 0; i < 6; i++)
|
|
va << geomatrix[i];
|
|
attr.GetAdd("geo_matrix") = va;
|
|
};
|
|
|
|
if(TIFFGetField(tiff, TIFFTAG_GEOTRANSMATRIX, &count, &data) && count == 16) {
|
|
geomatrix[0] = data[3];
|
|
geomatrix[1] = data[0];
|
|
geomatrix[2] = data[1];
|
|
geomatrix[3] = data[7];
|
|
geomatrix[4] = data[4];
|
|
geomatrix[5] = data[5];
|
|
|
|
NormalizeOrientation();
|
|
FinishMatrix();
|
|
}
|
|
else
|
|
if(TIFFGetField(tiff, TIFFTAG_GEOTIEPOINTS, &count, &data) && count >= 6) {
|
|
if(count>=18){ // 3 tiepoints needed for Xform2D::Map()
|
|
for(int t=0; t<count; t+=6){
|
|
if(page.orientation>=5) Swap(data[t + 0], data[t + 1]);
|
|
switch(page.orientation){
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
data[t + 0] = page.width - data[t + 0];
|
|
break;
|
|
case 3:
|
|
data[t + 0] = page.width - data[t + 0];
|
|
data[t + 1] = page.height - data[t + 1];
|
|
break;
|
|
case 4:
|
|
data[t + 1] = page.height - data[t + 1];
|
|
break;
|
|
case 5:
|
|
break;
|
|
case 6:
|
|
data[t + 0] = page.height - data[t + 0];
|
|
break;
|
|
case 7:
|
|
data[t + 0] = page.height - data[t + 0];
|
|
data[t + 1] = page.width - data[t + 1];
|
|
break;
|
|
case 8:
|
|
data[t + 1] = page.width - data[t + 1];
|
|
break;
|
|
}
|
|
}
|
|
|
|
Xform2D xf = Xform2D::Map(Pointf(data[0],data[1]),Pointf(data[6+0],data[6+1]),Pointf(data[12+0],data[12+1]),
|
|
Pointf(data[3],data[4]),Pointf(data[6+3],data[6+4]),Pointf(data[12+3],data[12+4]));
|
|
|
|
geomatrix[0] = xf.t.x;
|
|
geomatrix[1] = xf.x.x;
|
|
geomatrix[2] = xf.y.x;
|
|
geomatrix[3] = xf.t.y;
|
|
geomatrix[4] = xf.x.y;
|
|
geomatrix[5] = xf.y.y;
|
|
|
|
if(!pixel_is_area) {
|
|
geomatrix[0] -= (geomatrix[1] * 0.5 + geomatrix[2] * 0.5);
|
|
geomatrix[3] -= (geomatrix[4] * 0.5 + geomatrix[5] * 0.5);
|
|
}
|
|
FinishMatrix();
|
|
}
|
|
else{
|
|
double x = data[0];
|
|
double y = data[1];
|
|
double e = data[3];
|
|
double n = data[4];
|
|
|
|
if(TIFFGetField(tiff, TIFFTAG_GEOPIXELSCALE, &count, &data) && count >= 2 && data[0] && data[1]) {
|
|
double dx = data[0];
|
|
double dy = -abs(data[1]);
|
|
|
|
geomatrix[0] = e - x * dx;
|
|
geomatrix[1] = dx;
|
|
geomatrix[2] = 0;
|
|
geomatrix[3] = n - y * dy;
|
|
geomatrix[4] = 0;
|
|
geomatrix[5] = dy;
|
|
|
|
NormalizeOrientation();
|
|
FinishMatrix();
|
|
}
|
|
}
|
|
}
|
|
|
|
if(TIFFGetField(tiff, 42113, &count, &data) && // TIFFTAG_GDAL_NODATA = 42113
|
|
count >= 1){
|
|
attr.GetAdd("nodata") = String((const char*)data, count);
|
|
}
|
|
|
|
}
|
|
}
|
|
return SeekPage(0);
|
|
}
|
|
|
|
bool TIFRaster::Data::SeekPage(int pgx)
|
|
{
|
|
if(page_open) {
|
|
TIFFRGBAImageEnd(this);
|
|
page_open = false;
|
|
}
|
|
|
|
ASSERT(pgx >= 0 && pgx < pages.GetCount());
|
|
page_index = pgx;
|
|
page_error = false;
|
|
TIFFSetDirectory(tiff, page_index);
|
|
|
|
char emsg[1024];
|
|
if(!TIFFRGBAImageBegin(this, tiff, 0, emsg)) {
|
|
TIFFError(TIFFFileName(tiff), "%s", emsg);
|
|
page_error = true;
|
|
return false;
|
|
}
|
|
|
|
page_open = true;
|
|
const Page& page = pages[page_index];
|
|
|
|
size = Size(page.width, page.height);
|
|
if(isContig) {
|
|
contig = put.contig;
|
|
put.contig = putContigRGB;
|
|
}
|
|
else {
|
|
separate = put.separate;
|
|
put.separate = putSeparate;
|
|
}
|
|
alpha = pages[page_index].alpha;
|
|
if(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 = 2; put.contig = putContig2; format.Set2mf(); break;
|
|
case 4: bpp = 4; put.contig = putContig4; format.Set4mf(); break;
|
|
case 8: format.Set8(); break;
|
|
default: NEVER();
|
|
}
|
|
skewfac = 8 / bitspersample;
|
|
}
|
|
row_bytes = (bpp * width + 31) >> 5 << 2;
|
|
|
|
page_fetched = false;
|
|
return true;
|
|
}
|
|
|
|
bool TIFRaster::Data::FetchPage()
|
|
{
|
|
if(page_error)
|
|
return false;
|
|
if(page_fetched)
|
|
return true;
|
|
|
|
cache_size = 0;
|
|
rows.Clear();
|
|
int64 bytes = row_bytes * (int64)height;
|
|
imagebuf.SetCount(size.cy * row_bytes, 0);
|
|
req_orientation = pages[page_index].orientation;
|
|
|
|
bool res = TIFFRGBAImageGet(this, 0, width, height);
|
|
TIFFRGBAImageEnd(this);
|
|
page_open = false;
|
|
|
|
page_fetched = true;
|
|
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;
|
|
const int info_orientation[] = { FLIP_NONE, FLIP_MIRROR_VERT, FLIP_ROTATE_180, FLIP_MIRROR_HORZ, FLIP_NONE, FLIP_ROTATE_CLOCKWISE, FLIP_TRANSPOSE, FLIP_ROTATE_ANTICLOCKWISE, FLIP_TRANSVERSE, FLIP_NONE}; // Map from TIFFTAG_ORIENTATION to U++ orientation
|
|
out.orientation = info_orientation[min((int)page.orientation,9)];
|
|
return out;
|
|
}
|
|
|
|
Raster::Line TIFRaster::Data::GetLine(int line, Raster *raster)
|
|
{
|
|
if(!page_error && !page_fetched)
|
|
FetchPage();
|
|
if(page_error) {
|
|
byte *tmp = new byte[row_bytes];
|
|
memset(tmp, 0, row_bytes);
|
|
return Raster::Line(tmp, raster, true);
|
|
}
|
|
if(!imagebuf.IsEmpty())
|
|
return Raster::Line(&imagebuf[row_bytes * line], raster, false);
|
|
const byte *data = MapDown(0, line, row_bytes);
|
|
byte *tmp = new byte[row_bytes];
|
|
memcpy(tmp, data, row_bytes);
|
|
return Raster::Line(tmp, raster, true);
|
|
}
|
|
|
|
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 data->GetLine(line, this);
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
int TIFRaster::GetActivePage() const
|
|
{
|
|
return data->page_index;
|
|
}
|
|
|
|
void TIFRaster::SeekPage(int n)
|
|
{
|
|
data->SeekPage(n);
|
|
}
|
|
|
|
Value TIFRaster::GetMetaData(String id)
|
|
{
|
|
return data->attr.Get(id, Value());
|
|
}
|
|
|
|
void TIFRaster::EnumMetaData(Vector<String>& id_list)
|
|
{
|
|
id_list = clone(data->attr.GetKeys());
|
|
}
|
|
|
|
class TIFEncoder::Data {
|
|
public:
|
|
Data(Stream& stream, RasterFormat& format);
|
|
~Data();
|
|
|
|
void Start(Size size, Size dots, 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, Size dots, 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_LZW);
|
|
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);
|
|
if(dots.cx && dots.cy) {
|
|
TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, (uint16)RESUNIT_INCH);
|
|
float xres = float(sz.cx * 600.0 / dots.cx);
|
|
TIFFSetField(tiff, TIFFTAG_XRESOLUTION, xres);
|
|
float yres = float(sz.cy * 600.0 / dots.cy);
|
|
TIFFSetField(tiff, TIFFTAG_YRESOLUTION, yres);
|
|
}
|
|
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, GetDots(), bpp, bpp <= 8 ? GetPalette() : NULL);
|
|
}
|
|
|
|
void TIFEncoder::WriteLineRaw(const byte *s)
|
|
{
|
|
data->WriteLineRaw(s);
|
|
}
|
|
|
|
|
|
INITIALIZER(TIFRaster) {
|
|
StreamRaster::Register<TIFRaster>();
|
|
}
|
|
|
|
} |