openexr read becomes a new-style class
This commit is contained in:
parent
5bc26b0b65
commit
5789ca9421
20
TODO
20
TODO
@ -1,3 +1,23 @@
|
||||
- the way were handling the format/ compat thing is broken
|
||||
|
||||
if we build without-tiff, tiff compat will break
|
||||
|
||||
we need very loose coupling: tiff compat must just use the foreign
|
||||
interfaceo
|
||||
|
||||
we have vips__istiff() declared in foreign/tiff.h and internal.h
|
||||
|
||||
|
||||
im_tiff2vips.c needs this stuff:
|
||||
|
||||
->read()/->header() ... just use vips_tiffload()
|
||||
|
||||
->is_a() ...
|
||||
|
||||
->flags() ...
|
||||
|
||||
|
||||
|
||||
- "header fred.png" does not work, since header uses im_open() which uses
|
||||
VipsForeign
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
noinst_LTLIBRARIES = libforeign.la
|
||||
|
||||
libforeign_la_SOURCES = \
|
||||
openexr2vips.h \
|
||||
openexr2vips.c \
|
||||
openexrload.c \
|
||||
openslide2vips.h \
|
||||
openslide2vips.c \
|
||||
openslideload.c \
|
||||
|
@ -1100,6 +1100,7 @@ vips_foreign_write_options( VipsImage *in, const char *filename )
|
||||
void
|
||||
vips_foreign_operation_init( void )
|
||||
{
|
||||
extern GType vips_foreign_load_openexr_get_type( void );
|
||||
extern GType vips_foreign_load_openslide_get_type( void );
|
||||
extern GType vips_foreign_load_jpeg_file_get_type( void );
|
||||
extern GType vips_foreign_load_jpeg_buffer_get_type( void );
|
||||
@ -1128,6 +1129,10 @@ vips_foreign_operation_init( void )
|
||||
vips_foreign_load_openslide_get_type();
|
||||
#endif /*HAVE_OPENSLIDE*/
|
||||
|
||||
#ifdef HAVE_OPENEXR
|
||||
vips_foreign_load_openexr_get_type();
|
||||
#endif /*HAVE_OPENEXR*/
|
||||
|
||||
vips_foreign_load_vips_get_type();
|
||||
vips_foreign_save_vips_get_type();
|
||||
}
|
||||
|
408
libvips/foreign/openexr2vips.c
Normal file
408
libvips/foreign/openexr2vips.c
Normal file
@ -0,0 +1,408 @@
|
||||
/* Convert OpenEXR to VIPS
|
||||
*
|
||||
* 1/5/06
|
||||
* - from im_png2vips.c
|
||||
* 17/5/06
|
||||
* - oops, buffer calcs were wrong
|
||||
* 19/5/06
|
||||
* - added tiled read, with a separate cache
|
||||
* - removed *255 we had before, better to do something clever with
|
||||
* chromaticities
|
||||
* 4/2/10
|
||||
* - gtkdoc
|
||||
* 12/12/11
|
||||
* - redo as a set of fns ready for wrapping in a new-style class
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
TODO
|
||||
|
||||
- colour management
|
||||
- attributes
|
||||
- more of OpenEXR's pixel formats
|
||||
- more than just RGBA channels
|
||||
|
||||
the openexr C API is very limited ... it seems RGBA half pixels is
|
||||
all you can do
|
||||
|
||||
openexr lets you have different formats in different channels :-(
|
||||
|
||||
there's no API to read the "chromaticities" attribute :-(
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/thread.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include <ImfCRgbaFile.h>
|
||||
|
||||
#include "openexr2vips.h"
|
||||
|
||||
/* What we track during a OpenEXR read.
|
||||
*/
|
||||
typedef struct {
|
||||
char *filename;
|
||||
VipsImage *out;
|
||||
|
||||
ImfTiledInputFile *tiles;
|
||||
ImfInputFile *lines;
|
||||
const ImfHeader *header;
|
||||
VipsRect window;
|
||||
int tile_width;
|
||||
int tile_height;
|
||||
} Read;
|
||||
|
||||
gboolean
|
||||
vips__openexr_isexr( const char *filename )
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if( vips__get_bytes( filename, buf, 4 ) )
|
||||
if( buf[0] == 0x76 && buf[1] == 0x2f &&
|
||||
buf[2] == 0x31 && buf[3] == 0x01 )
|
||||
return( TRUE );
|
||||
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
static void
|
||||
get_imf_error( void )
|
||||
{
|
||||
vips_error( "exr2vips", _( "EXR error: %s" ), ImfErrorMessage() );
|
||||
}
|
||||
|
||||
static void
|
||||
read_destroy( VipsImage *out, Read *read )
|
||||
{
|
||||
VIPS_FREE( read->filename );
|
||||
|
||||
VIPS_FREEF( ImfCloseTiledInputFile, read->tiles );
|
||||
VIPS_FREEF( ImfCloseInputFile, read->lines );
|
||||
|
||||
vips_free( read );
|
||||
}
|
||||
|
||||
static Read *
|
||||
read_new( const char *filename, VipsImage *out )
|
||||
{
|
||||
Read *read;
|
||||
int xmin, ymin;
|
||||
int xmax, ymax;
|
||||
|
||||
if( !(read = VIPS_NEW( NULL, Read )) )
|
||||
return( NULL );
|
||||
read->filename = vips_strdup( NULL, filename );
|
||||
read->out = out;
|
||||
read->tiles = NULL;
|
||||
read->lines = NULL;
|
||||
if( out )
|
||||
g_signal_connect( out, "close",
|
||||
G_CALLBACK( read_destroy ), read );
|
||||
|
||||
/* Try to open tiled first ... if that fails, fall back to scanlines.
|
||||
|
||||
FIXME ... seems a bit ugly, but how else can you spot a tiled
|
||||
EXR image?
|
||||
|
||||
*/
|
||||
if( !(read->tiles = ImfOpenTiledInputFile( read->filename )) ) {
|
||||
if( !(read->lines = ImfOpenInputFile( read->filename )) ) {
|
||||
get_imf_error();
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if( read->tiles )
|
||||
printf( "exr2vips: opening in tiled mode\n" );
|
||||
else
|
||||
printf( "exr2vips: opening in scanline mode\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( read->tiles ) {
|
||||
read->header = ImfTiledInputHeader( read->tiles );
|
||||
read->tile_width = ImfTiledInputTileXSize( read->tiles );
|
||||
read->tile_height = ImfTiledInputTileYSize( read->tiles );
|
||||
}
|
||||
else
|
||||
read->header = ImfInputHeader( read->lines );
|
||||
|
||||
ImfHeaderDataWindow( read->header, &xmin, &ymin, &xmax, &ymax );
|
||||
read->window.left = xmin;
|
||||
read->window.top = ymin;
|
||||
read->window.width = xmax - xmin + 1;
|
||||
read->window.height = ymax - ymin + 1;
|
||||
|
||||
return( read );
|
||||
}
|
||||
|
||||
gboolean
|
||||
vips__openexr_istiled( const char *filename )
|
||||
{
|
||||
Read *read;
|
||||
gboolean tiled;
|
||||
|
||||
if( !(read = read_new( filename, NULL )) )
|
||||
return( FALSE );
|
||||
tiled = read->tiles != NULL;
|
||||
read_destroy( NULL, read );
|
||||
|
||||
return( tiled );
|
||||
}
|
||||
|
||||
/* Read a OpenEXR file (header) into a VIPS (header).
|
||||
*/
|
||||
static void
|
||||
read_header( Read *read, VipsImage *out )
|
||||
{
|
||||
/*
|
||||
|
||||
FIXME ... not really sRGB. I think EXR is actually linear (no
|
||||
gamma). We ought to read the chromaticities from the header, put
|
||||
through a 3x3 matrix and output as XYZ
|
||||
|
||||
*/
|
||||
vips_image_init_fields( out,
|
||||
read->window.width, read->window.height, 4,
|
||||
VIPS_FORMAT_FLOAT,
|
||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 );
|
||||
if( read->tiles )
|
||||
vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
|
||||
else
|
||||
vips_demand_hint( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
|
||||
}
|
||||
|
||||
int
|
||||
vips__openexr_read_header( const char *filename, VipsImage *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( filename, out )) )
|
||||
return( -1 );
|
||||
read_header( read, out );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Allocate a tile buffer.
|
||||
*/
|
||||
static void *
|
||||
vips__openexr_start( VipsImage *out, void *a, void *b )
|
||||
{
|
||||
Read *read = (Read *) a;
|
||||
ImfRgba *imf_buffer;
|
||||
|
||||
if( !(imf_buffer = VIPS_ARRAY( out,
|
||||
read->tile_width * read->tile_height, ImfRgba )) )
|
||||
return( NULL );
|
||||
|
||||
return( imf_buffer );
|
||||
}
|
||||
|
||||
static int
|
||||
vips__openexr_generate( VipsRegion *out,
|
||||
void *seq, void *a, void *b, gboolean *top )
|
||||
{
|
||||
ImfRgba *imf_buffer = (ImfRgba *) seq;
|
||||
Read *read = (Read *) a;
|
||||
VipsRect *r = &out->valid;
|
||||
|
||||
const int tw = read->tile_width;
|
||||
const int th = read->tile_height;
|
||||
|
||||
/* Find top left of tiles we need.
|
||||
*/
|
||||
const int xs = (r->left / tw) * tw;
|
||||
const int ys = (r->top / th) * th;
|
||||
|
||||
int x, y, z;
|
||||
VipsRect image;
|
||||
|
||||
/* Area of image.
|
||||
*/
|
||||
image.left = 0;
|
||||
image.top = 0;
|
||||
image.width = read->out->Xsize;
|
||||
image.height = read->out->Ysize;
|
||||
|
||||
for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += th )
|
||||
for( x = xs; x < VIPS_RECT_RIGHT( r ); x += tw ) {
|
||||
VipsRect tile;
|
||||
VipsRect hit;
|
||||
int result;
|
||||
|
||||
if( !ImfTiledInputSetFrameBuffer( read->tiles,
|
||||
imf_buffer -
|
||||
(read->window.left + x) -
|
||||
(read->window.top + y) * tw,
|
||||
1, tw ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "exr2vips: requesting tile %d x %d\n",
|
||||
x / tw, y / th );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
result = ImfTiledInputReadTile( read->tiles,
|
||||
x / tw, y / th, 0, 0 );
|
||||
|
||||
if( !result ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* The tile in the file, in VIPS coordinates.
|
||||
*/
|
||||
tile.left = x;
|
||||
tile.top = y;
|
||||
tile.width = tw;
|
||||
tile.height = th;
|
||||
vips_rect_intersectrect( &tile, &image, &tile );
|
||||
|
||||
/* The part of this tile that hits the region.
|
||||
*/
|
||||
vips_rect_intersectrect( &tile, r, &hit );
|
||||
|
||||
/* Convert to float and write to the region.
|
||||
*/
|
||||
for( z = 0; z < hit.height; z++ ) {
|
||||
ImfRgba *p = imf_buffer +
|
||||
(hit.left - tile.left) +
|
||||
(hit.top - tile.top + z) * tw;
|
||||
float *q = (float *) VIPS_REGION_ADDR( out,
|
||||
hit.left, hit.top + z );
|
||||
|
||||
ImfHalfToFloatArray( 4 * hit.width,
|
||||
(ImfHalf *) p, q );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
vips__openexr_read( const char *filename, VipsImage *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( filename, out )) )
|
||||
return( -1 );
|
||||
|
||||
if( read->tiles ) {
|
||||
VipsImage *raw;
|
||||
VipsImage *t;
|
||||
|
||||
/* Tile cache: keep enough for two complete rows of tiles.
|
||||
*/
|
||||
raw = vips_image_new();
|
||||
vips_object_local( out, raw );
|
||||
|
||||
read_header( read, raw );
|
||||
|
||||
if( vips_image_generate( raw,
|
||||
vips__openexr_start, vips__openexr_generate, NULL,
|
||||
read, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
/* Copy to out, adding a cache. Enough tiles for a complete
|
||||
* row, plus 50%.
|
||||
*/
|
||||
if( vips_tilecache( raw, &t,
|
||||
"tile_width", read->tile_width,
|
||||
"tile_height", read->tile_height,
|
||||
"max_tiles", (int)
|
||||
(1.5 * (1 + raw->Xsize / read->tile_width)),
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write( t, out ) ) {
|
||||
g_object_unref( t );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t );
|
||||
}
|
||||
else {
|
||||
const int left = read->window.left;
|
||||
const int top = read->window.top;
|
||||
const int width = read->window.width;
|
||||
const int height = read->window.height;
|
||||
|
||||
ImfRgba *imf_buffer;
|
||||
float *vips_buffer;
|
||||
int y;
|
||||
|
||||
if( !(imf_buffer = VIPS_ARRAY( out, width, ImfRgba )) ||
|
||||
!(vips_buffer = VIPS_ARRAY( out, 4 * width, float )) )
|
||||
return( -1 );
|
||||
|
||||
read_header( read, out );
|
||||
if( vips_image_wio_output( out ) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < height; y++ ) {
|
||||
if( !ImfInputSetFrameBuffer( read->lines,
|
||||
imf_buffer - left - (top + y) * width,
|
||||
1, width ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
if( !ImfInputReadPixels( read->lines,
|
||||
top + y, top + y ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
ImfHalfToFloatArray( 4 * width,
|
||||
(ImfHalf *) imf_buffer, vips_buffer );
|
||||
|
||||
if( vips_image_write_line( out, y,
|
||||
(PEL *) vips_buffer ) )
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
46
libvips/foreign/openexr2vips.h
Normal file
46
libvips/foreign/openexr2vips.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* common defs for jpeg read/write
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
Copyright (C) 1991-2005 The National Gallery
|
||||
|
||||
This program 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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
#ifndef VIPS_OPENEXR2VIPS_H
|
||||
#define VIPS_OPENEXR2VIPS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
int vips__openexr_isexr( const char *filename );
|
||||
gboolean vips__openexr_istiled( const char *filename );
|
||||
int vips__openexr_read_header( const char *filename, VipsImage *out );
|
||||
int vips__openexr_read( const char *filename, VipsImage *out );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
#endif /*VIPS_OPENEXR2VIPS_H*/
|
167
libvips/foreign/openexrload.c
Normal file
167
libvips/foreign/openexrload.c
Normal file
@ -0,0 +1,167 @@
|
||||
/* load openexr from a file
|
||||
*
|
||||
* 5/12/11
|
||||
* - from openslideload.c
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file is part of VIPS.
|
||||
|
||||
VIPS is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
#define DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include "openexr2vips.h"
|
||||
|
||||
typedef struct _VipsForeignLoadOpenexr {
|
||||
VipsForeignLoad parent_object;
|
||||
|
||||
/* Filename for load.
|
||||
*/
|
||||
char *filename;
|
||||
|
||||
} VipsForeignLoadOpenexr;
|
||||
|
||||
typedef VipsForeignLoadClass VipsForeignLoadOpenexrClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsForeignLoadOpenexr, vips_foreign_load_openexr,
|
||||
VIPS_TYPE_FOREIGN_LOAD );
|
||||
|
||||
static VipsForeignFlags
|
||||
vips_foreign_load_openexr_get_flags( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadOpenexr *openexr = (VipsForeignLoadOpenexr *) load;
|
||||
VipsForeignFlags flags;
|
||||
|
||||
flags = 0;
|
||||
if( vips__openexr_istiled( openexr->filename ) )
|
||||
flags |= VIPS_FOREIGN_PARTIAL;
|
||||
|
||||
return( flags );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_openexr_header( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadOpenexr *openexr = (VipsForeignLoadOpenexr *) load;
|
||||
|
||||
if( vips__openexr_read_header( openexr->filename, load->out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips_foreign_load_openexr_load( VipsForeignLoad *load )
|
||||
{
|
||||
VipsForeignLoadOpenexr *openexr = (VipsForeignLoadOpenexr *) load;
|
||||
|
||||
if( vips__openexr_read( openexr->filename, load->real ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static const char *vips_foreign_openexr_suffs[] = { ".exr", NULL };
|
||||
|
||||
static void
|
||||
vips_foreign_load_openexr_class_init( VipsForeignLoadOpenexrClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
|
||||
VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "openexrload";
|
||||
object_class->description = _( "load an OpenEXR image" );
|
||||
|
||||
foreign_class->suffs = vips_foreign_openexr_suffs;
|
||||
|
||||
load_class->is_a = vips__openexr_isexr;
|
||||
load_class->get_flags = vips_foreign_load_openexr_get_flags;
|
||||
load_class->header = vips_foreign_load_openexr_header;
|
||||
load_class->load = vips_foreign_load_openexr_load;
|
||||
|
||||
VIPS_ARG_STRING( class, "filename", 1,
|
||||
_( "Filename" ),
|
||||
_( "Filename to load from" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignLoadOpenexr, filename ),
|
||||
NULL );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_foreign_load_openexr_init( VipsForeignLoadOpenexr *openexr )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_openexrload:
|
||||
* @filename: file to load
|
||||
* @out: decompressed image
|
||||
* @...: %NULL-terminated list of optional named arguments
|
||||
*
|
||||
* Read a OpenEXR file into a VIPS image.
|
||||
*
|
||||
* The reader can handle scanline and tiled OpenEXR images. It can't handle
|
||||
* OpenEXR colour management, image attributes, many pixel formats, anything
|
||||
* other than RGBA.
|
||||
*
|
||||
* This reader uses the rather limited OpenEXR C API. It should really be
|
||||
* redone in C++.
|
||||
*
|
||||
* See also: vips_image_new_from_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
vips_openexrload( const char *filename, VipsImage **out, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, out );
|
||||
result = vips_call_split( "openexrload", ap, filename, out );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
@ -85,381 +85,20 @@ im_exr2vips( const char *name, IMAGE *out )
|
||||
#include <vips/thread.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#include <ImfCRgbaFile.h>
|
||||
#include "../foreign/openexr2vips.h"
|
||||
|
||||
/* What we track during a OpenEXR read.
|
||||
*/
|
||||
typedef struct {
|
||||
char *name;
|
||||
IMAGE *out;
|
||||
|
||||
ImfTiledInputFile *tiles;
|
||||
ImfInputFile *lines;
|
||||
const ImfHeader *header;
|
||||
Rect window;
|
||||
int tile_width;
|
||||
int tile_height;
|
||||
|
||||
/* Need to single-thread calls to ReadTile.
|
||||
*/
|
||||
GMutex *lock;
|
||||
} Read;
|
||||
|
||||
static void
|
||||
get_imf_error( void )
|
||||
{
|
||||
im_error( "im_exr2vips", _( "EXR error: %s" ), ImfErrorMessage() );
|
||||
}
|
||||
|
||||
static void
|
||||
read_destroy( Read *read )
|
||||
{
|
||||
IM_FREE( read->name );
|
||||
|
||||
IM_FREEF( ImfCloseTiledInputFile, read->tiles );
|
||||
IM_FREEF( ImfCloseInputFile, read->lines );
|
||||
|
||||
IM_FREEF( g_mutex_free, read->lock );
|
||||
|
||||
im_free( read );
|
||||
}
|
||||
|
||||
static Read *
|
||||
read_new( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
int xmin, ymin;
|
||||
int xmax, ymax;
|
||||
|
||||
if( !(read = IM_NEW( NULL, Read )) )
|
||||
return( NULL );
|
||||
|
||||
read->name = im_strdup( NULL, name );
|
||||
read->out = out;
|
||||
read->tiles = NULL;
|
||||
read->lines = NULL;
|
||||
read->lock = NULL;
|
||||
|
||||
if( im_add_close_callback( out,
|
||||
(im_callback_fn) read_destroy, read, NULL ) ) {
|
||||
read_destroy( read );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Try to open tiled first ... if that fails, fall back to scanlines.
|
||||
|
||||
FIXME ... seems a bit ugly, but how else can you spot a tiled
|
||||
EXR image?
|
||||
|
||||
*/
|
||||
if( !(read->tiles = ImfOpenTiledInputFile( read->name )) ) {
|
||||
if( !(read->lines = ImfOpenInputFile( read->name )) ) {
|
||||
get_imf_error();
|
||||
return( NULL );
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
if( read->tiles )
|
||||
printf( "im_exr2vips: opening in tiled mode\n" );
|
||||
else
|
||||
printf( "im_exr2vips: opening in scanline mode\n" );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( read->tiles ) {
|
||||
read->header = ImfTiledInputHeader( read->tiles );
|
||||
read->lock = g_mutex_new();
|
||||
read->tile_width = ImfTiledInputTileXSize( read->tiles );
|
||||
read->tile_height = ImfTiledInputTileYSize( read->tiles );
|
||||
}
|
||||
else
|
||||
read->header = ImfInputHeader( read->lines );
|
||||
|
||||
ImfHeaderDataWindow( read->header, &xmin, &ymin, &xmax, &ymax );
|
||||
read->window.left = xmin;
|
||||
read->window.top = ymin;
|
||||
read->window.width = xmax - xmin + 1;
|
||||
read->window.height = ymax - ymin + 1;
|
||||
|
||||
return( read );
|
||||
}
|
||||
|
||||
/* Read a OpenEXR file (header) into a VIPS (header).
|
||||
*/
|
||||
static int
|
||||
read_header( Read *read, IMAGE *out )
|
||||
{
|
||||
/*
|
||||
|
||||
FIXME ... not really sRGB. I think EXR is actually linear (no
|
||||
gamma). We ought to read the chromaticities from the header, put
|
||||
through a 3x3 matrix and output as XYZ
|
||||
|
||||
*/
|
||||
im_initdesc( out,
|
||||
read->window.width, read->window.height, 4,
|
||||
IM_BBITS_FLOAT, IM_BANDFMT_FLOAT,
|
||||
IM_CODING_NONE, IM_TYPE_sRGB, 1.0, 1.0, 0, 0 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read a OpenEXR file header into a VIPS header.
|
||||
*/
|
||||
static int
|
||||
exr2vips_header( const char *name, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
|
||||
if( !(read = read_new( name, out )) ||
|
||||
read_header( read, out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Test for tiled EXR.
|
||||
*/
|
||||
static int
|
||||
isexrtiled( const char *name )
|
||||
{
|
||||
Read *read;
|
||||
int tiled;
|
||||
|
||||
if( !(read = read_new( name, NULL )) )
|
||||
return( -1 );
|
||||
tiled = read->tiles != NULL;
|
||||
read_destroy( read );
|
||||
|
||||
return( tiled );
|
||||
}
|
||||
|
||||
static int
|
||||
fill_region( REGION *out, void *seq, void *a, void *b )
|
||||
{
|
||||
ImfRgba *imf_buffer = (ImfRgba *) seq;
|
||||
Read *read = (Read *) a;
|
||||
Rect *r = &out->valid;
|
||||
|
||||
const int tw = read->tile_width;
|
||||
const int th = read->tile_height;
|
||||
|
||||
/* Find top left of tiles we need.
|
||||
*/
|
||||
const int xs = (r->left / tw) * tw;
|
||||
const int ys = (r->top / th) * th;
|
||||
|
||||
int x, y, z;
|
||||
Rect image;
|
||||
|
||||
/* Area of image.
|
||||
*/
|
||||
image.left = 0;
|
||||
image.top = 0;
|
||||
image.width = read->out->Xsize;
|
||||
image.height = read->out->Ysize;
|
||||
|
||||
for( y = ys; y < IM_RECT_BOTTOM( r ); y += th )
|
||||
for( x = xs; x < IM_RECT_RIGHT( r ); x += tw ) {
|
||||
Rect tile;
|
||||
Rect hit;
|
||||
int result;
|
||||
|
||||
if( !ImfTiledInputSetFrameBuffer( read->tiles,
|
||||
imf_buffer -
|
||||
(read->window.left + x) -
|
||||
(read->window.top + y) * tw,
|
||||
1, tw ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_exr2vips: requesting tile %d x %d\n",
|
||||
x / tw, y / th );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
g_mutex_lock( read->lock );
|
||||
result = ImfTiledInputReadTile( read->tiles,
|
||||
x / tw, y / th, 0, 0 );
|
||||
g_mutex_unlock( read->lock );
|
||||
|
||||
if( !result ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* The tile in the file, in VIPS coordinates.
|
||||
*/
|
||||
tile.left = x;
|
||||
tile.top = y;
|
||||
tile.width = tw;
|
||||
tile.height = th;
|
||||
im_rect_intersectrect( &tile, &image, &tile );
|
||||
|
||||
/* The part of this tile that hits the region.
|
||||
*/
|
||||
im_rect_intersectrect( &tile, r, &hit );
|
||||
|
||||
/* Convert to float and write to the region.
|
||||
*/
|
||||
for( z = 0; z < hit.height; z++ ) {
|
||||
ImfRgba *p = imf_buffer +
|
||||
(hit.left - tile.left) +
|
||||
(hit.top - tile.top + z) * tw;
|
||||
float *q = (float *) IM_REGION_ADDR( out,
|
||||
hit.left, hit.top + z );
|
||||
|
||||
ImfHalfToFloatArray( 4 * hit.width,
|
||||
(ImfHalf *) p, q );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Allocate a tile buffer.
|
||||
*/
|
||||
static void *
|
||||
seq_start( IMAGE *out, void *a, void *b )
|
||||
{
|
||||
Read *read = (Read *) a;
|
||||
ImfRgba *imf_buffer;
|
||||
|
||||
if( !(imf_buffer = IM_ARRAY( out,
|
||||
read->tile_width * read->tile_height, ImfRgba )) )
|
||||
return( NULL );
|
||||
|
||||
return( imf_buffer );
|
||||
}
|
||||
|
||||
/* Read tilewise.
|
||||
*/
|
||||
static int
|
||||
exr2vips_tiles( Read *read, IMAGE *out )
|
||||
{
|
||||
if( read_header( read, out ) ||
|
||||
im_poutcheck( out ) ||
|
||||
im_demand_hint( out, IM_SMALLTILE, NULL ) ||
|
||||
im_generate( out, seq_start, fill_region, NULL, read, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Read scanlinewise.
|
||||
*/
|
||||
static int
|
||||
exr2vips_lines( Read *read, IMAGE *out )
|
||||
{
|
||||
const int left = read->window.left;
|
||||
const int top = read->window.top;
|
||||
const int width = read->window.width;
|
||||
const int height = read->window.height;
|
||||
|
||||
ImfRgba *imf_buffer;
|
||||
float *vips_buffer;
|
||||
int y;
|
||||
|
||||
if( !(imf_buffer = IM_ARRAY( out, width, ImfRgba )) ||
|
||||
!(vips_buffer = IM_ARRAY( out, 4 * width, float )) ||
|
||||
read_header( read, out ) ||
|
||||
im_outcheck( out ) ||
|
||||
im_setupout( out ) )
|
||||
return( -1 );
|
||||
|
||||
for( y = 0; y < height; y++ ) {
|
||||
if( !ImfInputSetFrameBuffer( read->lines,
|
||||
imf_buffer - left - (top + y) * width,
|
||||
1, width ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
if( !ImfInputReadPixels( read->lines, top + y, top + y ) ) {
|
||||
get_imf_error();
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
ImfHalfToFloatArray( 4 * width,
|
||||
(ImfHalf *) imf_buffer, vips_buffer );
|
||||
|
||||
if( im_writeline( y, out, (PEL *) vips_buffer ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
exr2vips( Read *read )
|
||||
{
|
||||
if( read->tiles ) {
|
||||
IMAGE *raw;
|
||||
|
||||
/* Tile cache: keep enough for two complete rows of tiles.
|
||||
* This lets us do (smallish) area ops, like im_conv(), while
|
||||
* still only hitting each OpenEXR tile once.
|
||||
*/
|
||||
if( !(raw = im_open_local( read->out, "cache", "p" )) )
|
||||
return( -1 );
|
||||
if( exr2vips_tiles( read, raw ) )
|
||||
return( -1 );
|
||||
if( im_tile_cache( raw, read->out,
|
||||
read->tile_width, read->tile_height,
|
||||
2 * (1 + raw->Xsize / read->tile_width) ) )
|
||||
return( -1 );
|
||||
}
|
||||
else {
|
||||
if( exr2vips_lines( read, read->out ) )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* im_exr2vips:
|
||||
* @filename: file to load
|
||||
* @out: image to write to
|
||||
*
|
||||
* Read a OpenEXR file into a VIPS image.
|
||||
*
|
||||
* The reader can handle scanline and tiled OpenEXR images. It can't handle
|
||||
* OpenEXR colour management, image attributes, many pixel formats, anything
|
||||
* other than RGBA.
|
||||
*
|
||||
* This reader uses the rather limited OpenEXR C API. It should really be
|
||||
* redone in C++.
|
||||
*
|
||||
* See also: #VipsFormat.
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
im_exr2vips( const char *filename, IMAGE *out )
|
||||
{
|
||||
Read *read;
|
||||
VipsImage *t;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "im_exr2vips: reading \"%s\"\n", filename );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
if( !(read = read_new( filename, out )) ||
|
||||
exr2vips( read ) )
|
||||
if( vips_openexrload( filename, &t, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
isexr( const char *filename )
|
||||
{
|
||||
unsigned char buf[4];
|
||||
|
||||
if( im__get_bytes( filename, buf, 4 ) )
|
||||
if( buf[0] == 0x76 && buf[1] == 0x2f &&
|
||||
buf[2] == 0x31 && buf[3] == 0x01 )
|
||||
return( 1 );
|
||||
if( vips_image_write( t, out ) ) {
|
||||
g_object_unref( t );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -472,7 +111,7 @@ exr_flags( const char *filename )
|
||||
VipsFormatFlags flags;
|
||||
|
||||
flags = 0;
|
||||
if( isexrtiled( filename ) )
|
||||
if( vips__openexr_istiled( filename ) )
|
||||
flags |= VIPS_FORMAT_PARTIAL;
|
||||
|
||||
return( flags );
|
||||
@ -492,8 +131,8 @@ vips_format_exr_class_init( VipsFormatExrClass *class )
|
||||
object_class->nickname = "exr";
|
||||
object_class->description = _( "OpenEXR" );
|
||||
|
||||
format_class->is_a = isexr;
|
||||
format_class->header = exr2vips_header;
|
||||
format_class->is_a = vips__openexr_isexr;
|
||||
format_class->header = im_exr2vips;
|
||||
format_class->load = im_exr2vips;
|
||||
format_class->get_flags = exr_flags;
|
||||
format_class->suffs = exr_suffs;
|
||||
|
@ -312,6 +312,9 @@ int vips_tiffload( const char *filename, VipsImage **out, ... )
|
||||
int vips_tiffsave( VipsImage *in, const char *filename, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
int vips_openexrload( const char *filename, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /*__cplusplus*/
|
||||
|
@ -1168,8 +1168,8 @@ vips_popenf( const char *fmt, const char *mode, ... )
|
||||
|
||||
/* Break a command-line argument into tokens separated by whitespace. Strings
|
||||
* can't be adjacent, so "hello world" (without quotes) is a single string.
|
||||
* Strings are written (with \" escaped) into string, which must be size
|
||||
* characters large.
|
||||
* Strings are written (with \" escaped) into @string, which must be @size
|
||||
* characters large. NULL for end of tokens.
|
||||
*/
|
||||
const char *
|
||||
vips__token_get( const char *p, VipsToken *token, char *string, int size )
|
||||
|
Loading…
x
Reference in New Issue
Block a user