TIFF support

This commit is contained in:
Thomas Bernard 2019-01-02 18:47:57 +01:00
parent 36bd67f197
commit 7db03f7aab
5 changed files with 480 additions and 4 deletions

View File

@ -461,10 +461,10 @@ endif
LOPT += -lSDLmain -lSDL $(shell $(CROSS_SDLCONFIG) --libs) -lSDL_image LOPT += -lSDLmain -lSDL $(shell $(CROSS_SDLCONFIG) --libs) -lSDL_image
endif endif
ifeq (,$(CROSS_PKGCONFIG)) ifeq (,$(CROSS_PKGCONFIG))
COPT += -D__no_pnglib__ COPT += -D__no_pnglib__ -D__no_tifflib__
else else
COPT += $(shell PKG_CONFIG_LIBDIR=$(CROSS_PKG_CONFIG_PATH) $(CROSS_PKGCONFIG) --cflags libpng || echo "-D__no_pnglib__" ) COPT += $(shell PKG_CONFIG_LIBDIR=$(CROSS_PKG_CONFIG_PATH) $(CROSS_PKGCONFIG) --cflags libpng || echo "-D__no_pnglib__" )
LOPT += $(shell PKG_CONFIG_LIBDIR=$(CROSS_PKG_CONFIG_PATH) $(CROSS_PKGCONFIG) --libs libpng) LOPT += $(shell PKG_CONFIG_LIBDIR=$(CROSS_PKG_CONFIG_PATH) $(CROSS_PKGCONFIG) --libs libpng libtiff-4)
endif endif
LUALOPT = -llua LUALOPT = -llua
COPT += $(LUACOPT) COPT += $(LUACOPT)
@ -576,7 +576,7 @@ endif
COPT += -DNO_X11 COPT += -DNO_X11
endif endif
LOPT += $(TTFLOPT) LOPT += $(TTFLOPT)
LOPT += $(shell pkg-config --libs libpng) LOPT += $(shell pkg-config --libs libpng libtiff-4)
LOPT += $(LUALOPT) LOPT += $(LUALOPT)
OBJDIR = ../obj/unix OBJDIR = ../obj/unix
FCLOPT = -lfontconfig FCLOPT = -lfontconfig
@ -705,7 +705,7 @@ OBJS = main.o init.o graph.o $(APIOBJ) misc.o special.o \
fileformats.o miscfileformats.o libraw2crtc.o \ fileformats.o miscfileformats.o libraw2crtc.o \
brush_ops.o buttons_effects.o layers.o \ brush_ops.o buttons_effects.o layers.o \
oldies.o tiles.o colorred.o unicode.o gfx2surface.o \ oldies.o tiles.o colorred.o unicode.o gfx2surface.o \
gfx2log.o gfx2log.o tifformat.o
ifndef NORECOIL ifndef NORECOIL
OBJS += loadrecoil.o recoil.o OBJS += loadrecoil.o recoil.o
endif endif

View File

@ -153,6 +153,7 @@ enum FILE_FORMATS
FORMAT_FLI, ///< Autodesk Animator FLI/FLC FORMAT_FLI, ///< Autodesk Animator FLI/FLC
FORMAT_MOTO, ///< Thomson MO/TO computers pictures FORMAT_MOTO, ///< Thomson MO/TO computers pictures
FORMAT_HGR, ///< Apple II HGR and DHGR FORMAT_HGR, ///< Apple II HGR and DHGR
FORMAT_TIFF, ///< Tagged Image File Format
FORMAT_MISC, ///< Must be last of enum: others formats recognized by SDL_image (or recoil) FORMAT_MISC, ///< Must be last of enum: others formats recognized by SDL_image (or recoil)
FORMAT_CLIPBOARD ///< To load/save from/to Clipboard FORMAT_CLIPBOARD ///< To load/save from/to Clipboard
}; };

View File

@ -162,5 +162,12 @@ void Test_HGR(T_IO_Context *, FILE *);
void Load_HGR(T_IO_Context *); void Load_HGR(T_IO_Context *);
void Save_HGR(T_IO_Context *); void Save_HGR(T_IO_Context *);
// -- TIFF ------------------------------------------------------------------
#ifndef __no_tifflib__
void Test_TIFF(T_IO_Context *, FILE *);
void Load_TIFF(T_IO_Context *);
void Save_TIFF(T_IO_Context *);
#endif
/// @} /// @}
#endif #endif

View File

@ -2,6 +2,7 @@
*/ */
/* Grafx2 - The Ultimate 256-color bitmap paint program /* Grafx2 - The Ultimate 256-color bitmap paint program
Copyright 2018 Thomas Bernard
Copyright 2011 Pawel Góralski Copyright 2011 Pawel Góralski
Copyright 2010 Alexander Filyanov Copyright 2010 Alexander Filyanov
Copyright 2009 Petter Lindquist Copyright 2009 Petter Lindquist
@ -143,6 +144,9 @@ const T_Format File_formats[] = {
{FORMAT_FLI, " flc", Test_FLI, Load_FLI, NULL, 0, 0, 0, "flc", "flc;fli;dat"}, {FORMAT_FLI, " flc", Test_FLI, Load_FLI, NULL, 0, 0, 0, "flc", "flc;fli;dat"},
{FORMAT_MOTO," moto",Test_MOTO,Load_MOTO,Save_MOTO,0, 1, 0, "bin", "bin;map"}, {FORMAT_MOTO," moto",Test_MOTO,Load_MOTO,Save_MOTO,0, 1, 0, "bin", "bin;map"},
{FORMAT_HGR, " hgr", Test_HGR, Load_HGR, Save_HGR, 0, 0, 1, "hgr", "hgr;dhgr;bin"}, {FORMAT_HGR, " hgr", Test_HGR, Load_HGR, Save_HGR, 0, 0, 1, "hgr", "hgr;dhgr;bin"},
#ifndef __no_tifflib__
{FORMAT_TIFF," tiff",Test_TIFF,Load_TIFF,Save_TIFF,0, 1, 1, "tif", "tif;tiff"},
#endif
{FORMAT_MISC,"misc.",NULL, NULL, NULL, 0, 0, 0, "", "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff"}, {FORMAT_MISC,"misc.",NULL, NULL, NULL, 0, 0, 0, "", "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff"},
}; };

464
src/tifformat.c Normal file
View File

@ -0,0 +1,464 @@
/* vim:expandtab:ts=2 sw=2:
*/
/* Grafx2 - The Ultimate 256-color bitmap paint program
Copyright 2018 Thomas Bernard
Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
Grafx2 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.
Grafx2 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grafx2; if not, see <http://www.gnu.org/licenses/>
*/
///@file tifformat.c
/// Support of TIFF
///
/// @todo use TIFFSetErrorHandler() and TIFFSetWarningHandler() to
/// redirect warning/error output to our own functions
#ifndef __no_tifflib__
#include <tiffio.h>
#include "global.h"
#include "io.h"
#include "loadsave.h"
#include "gfx2log.h"
extern char Program_version[]; // generated in pversion.c
extern const char SVN_revision[]; // generated in version.c
/// test for a valid TIFF
void Test_TIFF(T_IO_Context * context, FILE * file)
{
char buffer[4];
File_error = 1;
if (!Read_bytes(file, buffer, 4))
return;
if (0 == memcmp(buffer, "MM\0*", 4) || 0 == memcmp(buffer, "II*\0", 4))
File_error = 0;
}
/// Load current image in TIFF
static void Load_TIFF_image(T_IO_Context * context, TIFF * tif, word spp, word bps)
{
tsize_t size;
int x, y;
unsigned int i, j;
if (TIFFIsTiled(tif))
{
// Tiled image
dword tile_width, tile_height;
if (!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) || !TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height))
{
File_error = 2;
return;
}
if (spp > 1 || bps > 8)
{
dword * buffer;
dword x2, y2;
buffer = malloc(sizeof(dword) * tile_width * tile_height);
for (y = 0; y < context->Height; y += tile_height)
{
for (x = 0; x < context->Width; x += tile_width)
{
if (!TIFFReadRGBATile(tif, x, y, buffer))
{
free(buffer);
File_error = 2;
return;
}
j = 0;
// Note that the raster is assume to be organized such that the pixel at
// location (x,y) is raster[y*width+x]; with the raster origin in the
// lower-left hand corner of the tile. That is bottom to top organization.
// Edge tiles which partly fall off the image will be filled out with
// appropriate zeroed areas.
for (y2 = 0; y2 < tile_height; y2++)
{
int y_pos = y + tile_height - 1 - y2;
for (x2 = 0; x2 < tile_width ; x2++)
{
Set_pixel_24b(context, x + x2, y_pos, TIFFGetR(buffer[j]), TIFFGetG(buffer[j]), TIFFGetB(buffer[j]));
j++;
}
}
}
}
free(buffer);
}
else
{
byte * buffer;
//ttile_t tile;
size = TIFFTileSize(tif);
buffer = malloc(size);
for (y = 0; y < context->Height; y += tile_height)
{
for (x = 0; x < context->Width; x += tile_width)
{
dword x2, y2;
if (TIFFReadTile(tif, buffer, x, y, 0, 0) == -1)
{
free(buffer);
File_error = 2;
return;
}
j = 0;
for (y2 = 0; y2 < tile_height; y2++)
{
for (x2 = 0; x2 < tile_width ; x2++)
{
Set_pixel(context, x + x2, y + y2, buffer[j]);
j++;
}
}
}
}
free(buffer);
}
}
else
{
dword rows_per_strip = 0;
tstrip_t strip, strip_count;
// "Striped" image
TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip);
if (rows_per_strip == 0)
{
File_error = 2;
return;
}
if (spp > 1 || bps > 8)
{
// if not 8bit with colormap, use TIFFReadRGBAStrip
dword * buffer;
strip_count = (context->Height + rows_per_strip - 1) / rows_per_strip;
buffer = malloc(sizeof(dword) * rows_per_strip * context->Width);
for (strip = 0, y = 0; strip < strip_count; strip++)
{
if (!TIFFReadRGBAStrip(tif, strip * rows_per_strip, buffer))
{
free(buffer);
File_error = 2;
return;
}
// Note that the raster is assume to be organized such that the
// pixel at location (x,y) is raster[y*width+x]; with the raster
// origin in the lower-left hand corner of the strip. That is bottom
// to top organization. When reading a partial last strip in the
// file the last line of the image will begin at the beginning of
// the buffer.
y = (strip + 1) * rows_per_strip - 1;
if (y >= context->Height)
y = context->Height - 1;
for (i = 0, j = 0;
i < rows_per_strip && y >= (int)(strip * rows_per_strip);
i++, y--)
{
for (x = 0; x < context->Width; x++)
{
Set_pixel_24b(context, x, y, TIFFGetR(buffer[j]), TIFFGetG(buffer[j]), TIFFGetB(buffer[j]));
j++;
}
}
}
free(buffer);
return; // ignore next image for now...
}
else
{
byte * buffer = NULL;
strip_count = TIFFNumberOfStrips(tif);
size = TIFFStripSize(tif);
GFX2_Log(GFX2_DEBUG, "TIFF %u strips of %u bytes\n", strip_count, size);
buffer = malloc(size);
for (strip = 0, y = 0; strip < strip_count; strip++)
{
tsize_t r = TIFFReadEncodedStrip(tif, strip, buffer, size);
if (r == -1)
{
free(buffer);
File_error = 2;
return;
}
for (i = 0, j = 0; i < rows_per_strip && y < context->Height; i++, y++)
{
for (x = 0; x < context->Width; x++)
{
switch (bps)
{
case 8:
Set_pixel(context, x, y, buffer[j++]);
break;
case 6: // 3 bytes => 4 pixels
Set_pixel(context, x++, y, buffer[j] >> 2);
if (x < context->Width)
{
Set_pixel(context, x++, y, (buffer[j] & 3) << 4 | (buffer[j+1] & 0xf0) >> 4);
j++;
if (x < context->Width)
{
Set_pixel(context, x++, y, (buffer[j] & 0x0f) << 2 | (buffer[j+1] & 0xc0) >> 6);
j++;
Set_pixel(context, x, y, buffer[j] & 0x3f);
}
}
j++;
break;
case 4:
Set_pixel(context, x++, y, buffer[j] >> 4);
Set_pixel(context, x, y, buffer[j++] & 0x0f);
break;
case 2:
Set_pixel(context, x++, y, buffer[j] >> 6);
Set_pixel(context, x++, y, (buffer[j] >> 4) & 3);
Set_pixel(context, x++, y, (buffer[j] >> 2) & 3);
Set_pixel(context, x, y, buffer[j++] & 3);
break;
case 1:
Set_pixel(context, x++, y, (buffer[j] >> 7) & 1);
Set_pixel(context, x++, y, (buffer[j] >> 6) & 1);
Set_pixel(context, x++, y, (buffer[j] >> 5) & 1);
Set_pixel(context, x++, y, (buffer[j] >> 4) & 1);
Set_pixel(context, x++, y, (buffer[j] >> 3) & 1);
Set_pixel(context, x++, y, (buffer[j] >> 2) & 1);
Set_pixel(context, x++, y, (buffer[j] >> 1) & 1);
Set_pixel(context, x, y, buffer[j++] & 1);
break;
default:
File_error = 2;
GFX2_Log(GFX2_ERROR, "TIFF : %u bps unsupported\n", bps);
free(buffer);
return;
}
}
}
}
free(buffer);
}
}
}
/// Load TIFF
void Load_TIFF_Sub(T_IO_Context * context, TIFF * tif, unsigned long file_size)
{
int layer = 0;
enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
dword width, height;
word bps, spp;
word photometric = PHOTOMETRIC_RGB;
char * desc;
TIFFPrintDirectory(tif, stdout, TIFFPRINT_NONE);
if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) || !TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))
return;
if (!TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps) || !TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &spp))
return;
GFX2_Log(GFX2_DEBUG, "TIFF #0 : %ux%u %ux%ubps\n", width, height, spp, bps);
if (TIFFGetField(tif, TIFFTAG_IMAGEDESCRIPTION, &desc))
{
size_t len = strlen(desc);
if (len <= COMMENT_SIZE)
memcpy(context->Comment, desc, len + 1);
else
{
memcpy(context->Comment, desc, COMMENT_SIZE);
context->Comment[COMMENT_SIZE] = '\0';
}
}
TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
File_error = 0;
Pre_load(context, width, height, file_size, FORMAT_TIFF, ratio, bps * spp);
if (File_error != 0)
return;
if (spp == 1)
{
struct {
word * r;
word * g;
word * b;
} colormap;
unsigned i, count;
count = (bps <= 8) ? 1 << bps : 256;
if (TIFFGetField(tif, TIFFTAG_COLORMAP, &colormap.r, &colormap.g, &colormap.b))
{
for (i = 0; i < count; i++)
{
context->Palette[i].R = colormap.r[i] >> 8;
context->Palette[i].G = colormap.g[i] >> 8;
context->Palette[i].B = colormap.b[i] >> 8;
}
}
else
{
// Grayscale palette
for (i = 0; i < count; i++)
{
unsigned int value = 255
* (photometric == PHOTOMETRIC_MINISWHITE ? (count - 1 - i) : i)
/ (count - 1);
context->Palette[i].R = value;
context->Palette[i].G = value;
context->Palette[i].B = value;
}
}
}
for (;;)
{
Load_TIFF_image(context, tif, spp, bps);
if (File_error != 0)
return;
if (context->Type != CONTEXT_MAIN_IMAGE)
return;
if (!TIFFReadDirectory(tif))
return;
if (!TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width) || !TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height))
return;
if (!TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bps) || !TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &spp))
return;
layer++;
GFX2_Log(GFX2_DEBUG, "TIFF #%d : %ux%u %ux%ubps\n", layer, width, height, spp, bps);
if ((int)width != context->Width || (int)height != context->Height)
return;
if (context->bpp != (spp * bps))
return;
Set_loading_layer(context, layer);
TIFFPrintDirectory(tif, stdout, TIFFPRINT_NONE);
}
}
/// Load TIFF from file
void Load_TIFF(T_IO_Context * context)
{
FILE * file;
TIFF * tif;
File_error = 1;
file = Open_file_read(context);
if (file != NULL)
{
tif = TIFFFdOpen(fileno(file), context->File_name, "r");
if (tif != NULL)
{
Load_TIFF_Sub(context, tif, File_length_file(file));
TIFFClose(tif);
}
fclose(file);
}
}
void Save_TIFF_Sub(T_IO_Context * context, TIFF * tif)
{
char version[64];
int i;
int layer = 0;
dword width, height;
dword y;
tstrip_t strip;
struct {
word r[256];
word g[256];
word b[256];
} colormap;
const word bps = 8;
const word spp = 1;
const dword rowsperstrip = 64;
snprintf(version, sizeof(version), "GrafX2 %s.%s", Program_version, SVN_revision);
width = context->Width;
height = context->Height;
for (i = 0; i < 256; i++)
{
colormap.r[i] = 0x101 * context->Palette[i].R;
colormap.g[i] = 0x101 * context->Palette[i].G;
colormap.b[i] = 0x101 * context->Palette[i].B;
}
for (;;)
{
GFX2_Log(GFX2_DEBUG, "TIFF save layer #%d\n", layer);
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bps);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, spp);
TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_LZW);
//TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); // not relevant if SAMPLESPERPIXEL == 1
TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsperstrip);
if (context->Comment[0] != '\0')
TIFFSetField(tif, TIFFTAG_IMAGEDESCRIPTION, context->Comment);
TIFFSetField(tif, TIFFTAG_SOFTWARE, version);
TIFFSetField(tif, TIFFTAG_COLORMAP, colormap.r, colormap.g, colormap.b);
#if 0
for (y = 0; y < height; y++)
{
if (TIFFWriteScanline(tif, context->Target_address + y*context->Pitch, y, 0) < 0)
return;
}
#else
for (y = 0, strip = 0; y < height; y += rowsperstrip, strip++)
{
if (TIFFWriteEncodedStrip(tif, strip, context->Target_address + y*context->Pitch, rowsperstrip * context->Pitch) < 0)
return;
}
#endif
layer++;
if (layer >= context->Nb_layers)
{
TIFFFlushData(tif);
File_error = 0; // everything was fine
return;
}
Set_saving_layer(context, layer);
if (!TIFFWriteDirectory(tif))
return;
TIFFFlushData(tif);
}
}
/// Save TIFF
void Save_TIFF(T_IO_Context * context)
{
TIFF * tif;
File_error = 1;
#if defined(WIN32)
if (context->File_name_unicode != NULL && context->File_name_unicode[0] != 0)
tif = TIFFOpenW(context->File_name_unicode, "w");
else
#endif
tif = TIFFOpen(context->File_name, "w");
if (tif != NULL)
{
Save_TIFF_Sub(context, tif);
TIFFClose(tif);
}
}
#endif