506 lines
14 KiB
C
506 lines
14 KiB
C
|
// Copyright (c) 2005-2008 ASCLEPIOS Project, INRIA Sophia-Antipolis (France)
|
|||
|
// All rights reserved.
|
|||
|
//
|
|||
|
// This file is part of the ImageIO Library, and as been adapted for CGAL (www.cgal.org).
|
|||
|
//
|
|||
|
// $URL: https://github.com/CGAL/cgal/blob/v5.1/CGAL_ImageIO/include/CGAL/ImageIO/inr_impl.h $
|
|||
|
// $Id: inr_impl.h 0779373 2020-03-26T13:31:46+01:00 Sébastien Loriot
|
|||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|||
|
//
|
|||
|
//
|
|||
|
// Author(s) : ASCLEPIOS Project (INRIA Sophia-Antipolis), Laurent Rineau
|
|||
|
|
|||
|
#ifdef CGAL_HEADER_ONLY
|
|||
|
#define CGAL_INLINE_FUNCTION inline
|
|||
|
#else
|
|||
|
#define CGAL_INLINE_FUNCTION
|
|||
|
#endif
|
|||
|
|
|||
|
#include <CGAL/ImageIO/fgetns.h>
|
|||
|
|
|||
|
#include <string>
|
|||
|
#include <sstream>
|
|||
|
#include <string.h>
|
|||
|
|
|||
|
/* Magic header for inrimages v4 */
|
|||
|
#define INR4_MAGIC "#INRIMAGE-4#{"
|
|||
|
|
|||
|
|
|||
|
/** Magic header for inrimages */
|
|||
|
#define INR_MAGIC "#INR"
|
|||
|
|
|||
|
typedef struct stringListElementStruct {
|
|||
|
char *string;
|
|||
|
struct stringListElementStruct *next;
|
|||
|
} stringListElement;
|
|||
|
/* list element with a pointer on a string */
|
|||
|
|
|||
|
typedef struct {
|
|||
|
stringListElement *begin, *end;
|
|||
|
} stringListHead;
|
|||
|
/* string list descriptor */
|
|||
|
|
|||
|
#include <clocale>
|
|||
|
class Set_numeric_locale {
|
|||
|
const char * old_locale;
|
|||
|
public:
|
|||
|
Set_numeric_locale(const char* locale)
|
|||
|
: old_locale(std::setlocale(LC_NUMERIC, locale))
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
~Set_numeric_locale() {
|
|||
|
std::setlocale(LC_NUMERIC, old_locale);
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
static void addStringElement(stringListHead *strhead,
|
|||
|
const char *str);
|
|||
|
/* add a string element at the tail of given list */
|
|||
|
|
|||
|
static void concatStringElement(const stringListHead *strhead,
|
|||
|
const char *str);
|
|||
|
/* concat given string at the last element of given list */
|
|||
|
|
|||
|
|
|||
|
/* Writes the given inrimage header in an already opened file.*/
|
|||
|
CGAL_INLINE_FUNCTION
|
|||
|
int _writeInrimageHeader(const _image *im, ENDIANNESS end) {
|
|||
|
std::size_t pos, i;
|
|||
|
char type[30], endianness[5], buf[257], scale[20];
|
|||
|
std::ostringstream oss;
|
|||
|
|
|||
|
Set_numeric_locale num_locale("C");
|
|||
|
|
|||
|
if(im->openMode != OM_CLOSE) {
|
|||
|
/* fix word kind */
|
|||
|
switch(im->wordKind) {
|
|||
|
|
|||
|
case WK_FLOAT:
|
|||
|
sprintf(type, "float");
|
|||
|
scale[0] = '\0';
|
|||
|
break;
|
|||
|
|
|||
|
case WK_FIXED:
|
|||
|
switch(im->sign) {
|
|||
|
case SGN_SIGNED:
|
|||
|
sprintf(type, "signed fixed");
|
|||
|
break;
|
|||
|
case SGN_UNSIGNED:
|
|||
|
sprintf(type, "unsigned fixed");
|
|||
|
break;
|
|||
|
default:
|
|||
|
return -1;
|
|||
|
}
|
|||
|
sprintf(scale, "SCALE=2**0\n");
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
switch(end) {
|
|||
|
case END_LITTLE:
|
|||
|
sprintf(endianness, "decm");
|
|||
|
break;
|
|||
|
case END_BIG:
|
|||
|
sprintf(endianness, "sun");
|
|||
|
break;
|
|||
|
default:
|
|||
|
/* fix architecture endianness */
|
|||
|
if( _getEndianness() == END_LITTLE)
|
|||
|
sprintf(endianness, "decm");
|
|||
|
else
|
|||
|
sprintf(endianness, "sun");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
/* write header information */
|
|||
|
oss << INR4_MAGIC << "\n";
|
|||
|
oss << "XDIM=" << im->xdim << "\n";
|
|||
|
oss << "YDIM=" << im->ydim << "\n";
|
|||
|
oss << "ZDIM=" << im->zdim << "\n";
|
|||
|
oss << "VDIM=" << im->vdim << "\n";
|
|||
|
oss << "TYPE=" << type << "\n";
|
|||
|
oss << "PIXSIZE=" << im->wdim*8 <<" bits\n";
|
|||
|
oss << scale << "CPU=" << endianness << "\n";
|
|||
|
oss << "VX=" << im->vx << "\n";
|
|||
|
oss << "VY=" << im->vy << "\n";
|
|||
|
oss << "VZ=" << im->vz << "\n";
|
|||
|
|
|||
|
if ( im->cx != 0 )
|
|||
|
oss << "XO="<< im->cx << "\n";
|
|||
|
if ( im->cy != 0 )
|
|||
|
oss << "YO="<< im->cy << "\n";
|
|||
|
if ( im->cz != 0 )
|
|||
|
oss << "ZO="<< im->cz << "\n";
|
|||
|
if ( im->tx != 0.0 )
|
|||
|
oss << "TX="<< im->tx << "\n";
|
|||
|
if ( im->ty != 0.0 )
|
|||
|
oss << "TY="<< im->ty << "\n";
|
|||
|
if ( im->tz != 0.0 )
|
|||
|
oss << "TZ="<< im->tz << "\n";
|
|||
|
if ( im->rx != 0.0 )
|
|||
|
oss << "RX="<< im->rx <<"\n";
|
|||
|
if ( im->ry != 0.0 )
|
|||
|
oss << "RY="<< im->ry << "\n";
|
|||
|
if ( im->rz != 0.0 )
|
|||
|
oss << "RZ=" << im->rz <<"\n";
|
|||
|
|
|||
|
pos = oss.str().length();
|
|||
|
|
|||
|
if(ImageIO_write(im, oss.str().data(), oss.str().length()) == 0)
|
|||
|
return -1;
|
|||
|
|
|||
|
|
|||
|
/* write user strings */
|
|||
|
if ( im->user != nullptr ) {
|
|||
|
for(i = 0; i < im->nuser; i++) {
|
|||
|
if ( im->user[i] == nullptr ) continue;
|
|||
|
pos += strlen(im->user[i]) + 2;
|
|||
|
if(ImageIO_write(im, "#", 1) == 0) return -1;
|
|||
|
if(ImageIO_write(im, im->user[i], strlen(im->user[i])) == 0) return -1;
|
|||
|
if(ImageIO_write(im, "\n", 1) == 0) return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
/* write end of header */
|
|||
|
pos = pos % 256;
|
|||
|
if(pos > 252) {
|
|||
|
for(i = pos; i < 256; i++)
|
|||
|
if(ImageIO_write(im, "\n", 1) != 1) return -1;
|
|||
|
pos = 0;
|
|||
|
}
|
|||
|
buf[0] = '\0';
|
|||
|
for(i = pos; i < 252; i++) strcat(buf, "\n");
|
|||
|
strcat(buf, "##}\n");
|
|||
|
|
|||
|
if(ImageIO_write(im, buf, strlen(buf)) == 0) return -1;
|
|||
|
else return 1;
|
|||
|
}
|
|||
|
|
|||
|
else return -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* Writes the given image body in an already opened file.*/
|
|||
|
CGAL_INLINE_FUNCTION
|
|||
|
bool _writeInrimageData(const _image *im) {
|
|||
|
std::size_t size, nbv, nwrt, i, v;
|
|||
|
unsigned char **vp;
|
|||
|
|
|||
|
if(im->openMode != OM_CLOSE) {
|
|||
|
|
|||
|
/* scalar or interlaced vectors */
|
|||
|
if(im->vectMode != VM_NON_INTERLACED) {
|
|||
|
size = im->xdim * im->ydim * im->zdim * im->vdim * im->wdim;
|
|||
|
nwrt = ImageIO_write(im, im->data, size);
|
|||
|
return nwrt == size;
|
|||
|
}
|
|||
|
|
|||
|
/* non interlaced vectors: interlace for saving */
|
|||
|
else {
|
|||
|
nbv = im->xdim * im->ydim * im->zdim;
|
|||
|
size = im->xdim * im->ydim * im->zdim * im->wdim;
|
|||
|
vp = (unsigned char **) ImageIO_alloc(im->vdim * sizeof(unsigned char *));
|
|||
|
for(v = 0; v < im->vdim; v++)
|
|||
|
vp[v] = (unsigned char *) im->data + v * size;
|
|||
|
for(i = 0; i < nbv; i++)
|
|||
|
for(v = 0; v < im->vdim; v++) {
|
|||
|
if(ImageIO_write(im, (const void *) vp[v], im->wdim) != im->wdim)
|
|||
|
return false;
|
|||
|
vp[v] += im->wdim;
|
|||
|
}
|
|||
|
ImageIO_free(vp);
|
|||
|
return true;
|
|||
|
}
|
|||
|
}
|
|||
|
else return false;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* read header of an opened inrimage */
|
|||
|
CGAL_INLINE_FUNCTION
|
|||
|
int readInrimageHeader(const char *,_image *im) {
|
|||
|
char str[257];
|
|||
|
int n, nusr;
|
|||
|
stringListHead strl = { nullptr, nullptr };
|
|||
|
stringListElement *oel, *el;
|
|||
|
|
|||
|
Set_numeric_locale num_locale("C");
|
|||
|
|
|||
|
if(im->openMode != OM_CLOSE) {
|
|||
|
/* read image magic number */
|
|||
|
if(!fgetns(str, 257, im )) return -1;
|
|||
|
if(strcmp(str, INR4_MAGIC)) return -1;
|
|||
|
|
|||
|
|
|||
|
/* while read line does not begin with '#' or '\n', read line
|
|||
|
and decode field */
|
|||
|
if(!fgetns(str, 257, im)) return -1;
|
|||
|
|
|||
|
while(str[0] != '#' && str[0] != '\0') {
|
|||
|
|
|||
|
if(!strncmp(str, "XDIM=", 5)) {
|
|||
|
std::istringstream iss(str+5);
|
|||
|
if(!(iss >> im->xdim)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "YDIM=", 5)) {
|
|||
|
std::istringstream iss(str+5);
|
|||
|
if(!(iss >> im->ydim)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "ZDIM=", 5)) {
|
|||
|
std::istringstream iss(str+5);
|
|||
|
if(!(iss >> im->zdim)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "VDIM=", 5)) {
|
|||
|
std::istringstream iss(str+5);
|
|||
|
if(!(iss >> im->vdim)) return -1;
|
|||
|
if(im->vdim == 1) im->vectMode = VM_SCALAR;
|
|||
|
else im->vectMode = VM_INTERLACED;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "VX=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->vx)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "VY=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->vy)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "VZ=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->vz)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "TYPE=", 5)) {
|
|||
|
if(!strncmp(str+5, "float", 5)) im->wordKind = WK_FLOAT;
|
|||
|
else {
|
|||
|
if(!strncmp(str+5, "signed fixed", 12)) {
|
|||
|
im->wordKind = WK_FIXED;
|
|||
|
im->sign = SGN_SIGNED;
|
|||
|
}
|
|||
|
else if(!strncmp(str+5, "unsigned fixed", 14)) {
|
|||
|
im->wordKind = WK_FIXED;
|
|||
|
im->sign = SGN_UNSIGNED;
|
|||
|
}
|
|||
|
else return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
/* before "sscanf(str+8, "%i %n", &im->wdim, &n) != 1"
|
|||
|
was used.
|
|||
|
However the man said
|
|||
|
...
|
|||
|
n Nothing is expected; instead, the number of charac
|
|||
|
ters consumed thus far from the input is stored
|
|||
|
through the next pointer, which must be a pointer
|
|||
|
to int. This is not a conversion, although it can
|
|||
|
be suppressed with the * flag. The C standard
|
|||
|
says: `Execution of a %n directive does not incre
|
|||
|
ment the assignment count returned at the comple
|
|||
|
tion of execution' but the Corrigendum seems to
|
|||
|
contradict this. Probably it is wise not to make
|
|||
|
any assumptions on the effect of %n conversions on
|
|||
|
the return value.
|
|||
|
...
|
|||
|
Thus I change it. It was yielding a RETURN_FAILURE with
|
|||
|
insight (GM).
|
|||
|
*/
|
|||
|
else if(!strncmp(str, "PIXSIZE=", 8)) {
|
|||
|
std::istringstream iss(str+8);
|
|||
|
if(!(iss >> im->wdim)) return -1;
|
|||
|
if(im->wdim != 8 && im->wdim != 16 && im->wdim != 32 &&
|
|||
|
im->wdim != 64) return -1;
|
|||
|
|
|||
|
if ( im->wdim <= 9 ) {
|
|||
|
if(strncmp(str+8+1, " bits", 5)) return -1;
|
|||
|
}
|
|||
|
else if ( im->wdim <= 99 ) {
|
|||
|
if(strncmp(str+8+2, " bits", 5)) return -1;
|
|||
|
}
|
|||
|
else {
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
im->wdim >>= 3;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "SCALE=", 6)) ;
|
|||
|
else if(!strncmp(str, "CPU=", 4)) {
|
|||
|
if(!strncmp(str+4, "decm", 4)) im->endianness = END_LITTLE;
|
|||
|
else if(!strncmp(str+4, "alpha", 5)) im->endianness = END_LITTLE;
|
|||
|
else if(!strncmp(str+4, "pc", 2)) im->endianness = END_LITTLE;
|
|||
|
else if(!strncmp(str+4, "sun", 3)) im->endianness = END_BIG;
|
|||
|
else if(!strncmp(str+4, "sgi", 3)) im->endianness = END_BIG;
|
|||
|
else return -1;
|
|||
|
}
|
|||
|
|
|||
|
else if(!strncmp(str, "XO=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->cx)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "YO=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->cy)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "ZO=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->cz)) return -1;
|
|||
|
}
|
|||
|
|
|||
|
else if(!strncmp(str, "TX=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->tx)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "TY=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->ty)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "TZ=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->tz)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "RX=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->rx)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "RY=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->ry)) return -1;
|
|||
|
}
|
|||
|
else if(!strncmp(str, "RZ=", 3)) {
|
|||
|
std::istringstream iss(str+3);
|
|||
|
if(!(iss >> im->rz)) return -1;
|
|||
|
}
|
|||
|
|
|||
|
if(!fgetns(str, 257, im)) return -1;
|
|||
|
}
|
|||
|
|
|||
|
/* parse user strings */
|
|||
|
im->nuser = nusr = 0;
|
|||
|
while(str[0] == '#' && strncmp(str, "##}", 3)) {
|
|||
|
addStringElement(&strl, str + 1);
|
|||
|
while(strlen(str) == 256) {
|
|||
|
if(!fgetns(str, 257, im)) return -1;
|
|||
|
concatStringElement(&strl, str);
|
|||
|
}
|
|||
|
nusr++;
|
|||
|
if(!fgetns(str, 257, im)) return -1;
|
|||
|
}
|
|||
|
|
|||
|
/* go to end of header */
|
|||
|
while(strncmp(str, "##}", 3)) {
|
|||
|
if(!fgetns(str, 257, im)) return -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* check header validity */
|
|||
|
if(im->xdim > 0 && im->ydim > 0 && im->zdim > 0 && im->vdim > 0 &&
|
|||
|
im->vx > 0.0 && im->vy > 0.0 && im->vz > 0.0 &&
|
|||
|
(im->wordKind == WK_FLOAT || (im->wordKind == WK_FIXED &&
|
|||
|
im->sign != SGN_UNKNOWN)) &&
|
|||
|
im->endianness != END_UNKNOWN) {
|
|||
|
if(nusr > 0) {
|
|||
|
im->nuser = nusr;
|
|||
|
im->user = (char **) ImageIO_alloc(im->nuser * sizeof(char *));
|
|||
|
oel = nullptr;
|
|||
|
for(el = strl.begin, n = 0; el != nullptr; el = oel, n++) {
|
|||
|
im->user[n] = el->string;
|
|||
|
oel = el->next;
|
|||
|
ImageIO_free(el);
|
|||
|
}
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
else return -1;
|
|||
|
|
|||
|
}
|
|||
|
else return -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/* add a string element at the tail of given list */
|
|||
|
static void addStringElement(stringListHead *strhead, const char *str) {
|
|||
|
stringListElement *el;
|
|||
|
|
|||
|
el = (stringListElement *) ImageIO_alloc(sizeof(stringListElement));
|
|||
|
/* was strdup(str); */
|
|||
|
el->string = (char*)ImageIO_alloc( strlen(str)+1);
|
|||
|
memcpy(el->string, str, strlen(str)+1);
|
|||
|
el->next = nullptr;
|
|||
|
if(strhead->begin == nullptr)
|
|||
|
strhead->begin = strhead->end = el;
|
|||
|
else {
|
|||
|
strhead->end->next = el;
|
|||
|
strhead->end = el;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/* concat given string at the last element of given list */
|
|||
|
static void concatStringElement(const stringListHead *strhead,
|
|||
|
const char *str) {
|
|||
|
stringListElement *el;
|
|||
|
|
|||
|
el = strhead->end;
|
|||
|
el->string = (char *) realloc(el->string,
|
|||
|
strlen(el->string) + strlen(str) + 1);
|
|||
|
strcat(el->string, str);
|
|||
|
}
|
|||
|
|
|||
|
CGAL_INLINE_FUNCTION
|
|||
|
int testInrimageHeader(char *magic,const char *) {
|
|||
|
if (!strcmp(magic, INR_MAGIC))
|
|||
|
return 0;
|
|||
|
else
|
|||
|
return -1;
|
|||
|
}
|
|||
|
CGAL_INLINE_FUNCTION
|
|||
|
int writeInrimage(char *name,_image *im) {
|
|||
|
int res;
|
|||
|
|
|||
|
_openWriteImage( im, name );
|
|||
|
|
|||
|
if(!im->fd) {
|
|||
|
fprintf(stderr, "writeInrimage: error: unable to open file \'%s\'\n", name );
|
|||
|
return ImageIO_OPENING;
|
|||
|
}
|
|||
|
|
|||
|
res = _writeInrimageHeader(im, END_UNKNOWN);
|
|||
|
if (res < 0) {
|
|||
|
fprintf(stderr, "writeInrimage: error: unable to write header of \'%s\'\n",
|
|||
|
name);
|
|||
|
ImageIO_close( im );
|
|||
|
im->fd = nullptr;
|
|||
|
im->openMode = OM_CLOSE;
|
|||
|
return( res );
|
|||
|
}
|
|||
|
|
|||
|
if (!_writeInrimageData(im)) {
|
|||
|
fprintf(stderr, "writeInrimage: error: unable to write data of \'%s\'\n",
|
|||
|
name);
|
|||
|
ImageIO_close( im );
|
|||
|
im->fd = nullptr;
|
|||
|
im->openMode = OM_CLOSE;
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
ImageIO_close( im );
|
|||
|
im->fd = nullptr;
|
|||
|
im->openMode = OM_CLOSE;
|
|||
|
|
|||
|
return ( res );
|
|||
|
}
|
|||
|
|
|||
|
CGAL_INLINE_FUNCTION
|
|||
|
PTRIMAGE_FORMAT createInrimageFormat() {
|
|||
|
PTRIMAGE_FORMAT f=(PTRIMAGE_FORMAT) ImageIO_alloc(sizeof(IMAGE_FORMAT));
|
|||
|
|
|||
|
f->testImageFormat=&testInrimageHeader;
|
|||
|
f->readImageHeader=&readInrimageHeader;
|
|||
|
f->writeImage=&writeInrimage;
|
|||
|
strcpy(f->fileExtension,".inr,.inr.gz,.gradient,.gradient.gz,.gradient_direction,.gradient_direction.gz");
|
|||
|
strcpy(f->realName,"Inrimage");
|
|||
|
return f;
|
|||
|
}
|