Files
jdsouza90 cf68600c4f v0.8.1.0 Release
v0.8.1.0 Release
2022-09-20 09:59:34 -07:00

2379 lines
87 KiB
C++

/*###############################################################################
#
# Copyright 2019-2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#include "FaceIO.h"
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stack>
#include <string>
#include <vector>
#ifdef _MSC_VER
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#endif /* _MSC_VER */
#define BAIL_IF_ERR(err) do { if ((err) != 0) { goto bail; } } while(0)
#define BAIL_IF_ZERO(x, err, code) do { if ((x) == 0) { err = code; goto bail; } } while(0)
#define BAIL_IF_NONZERO(x, err, code) do { if ((x) != 0) { err = code; goto bail; } } while(0)
#define BAIL_IF_FALSE(x, err, code) do { if (!(x)) { err = code; goto bail; } } while(0)
#define BAIL_IF_TRUE(x, err, code) do { if ((x)) { err = code; goto bail; } } while(0)
#define BAIL_IF_NULL(x, err, code) do { if ((void*)(x) == NULL) { err = code; goto bail; } } while(0)
#define BAIL_IF_NEGATIVE(x, err, code) do { if ((x) < 0) { err = code; goto bail; } } while(0)
#define BAIL_IF_NONPOSITIVE(x, err, code) do { if (!((x) > 0)) { err = code; goto bail; } } while(0)
#define BAIL(err, code) do { err = code; goto bail; } while(0)
#ifndef __BYTE_ORDER__ /* How bytes are packed into a 32 bit word */
#define __ORDER_LITTLE_ENDIAN__ 3210 /* First byte in the least significant position */
#define __ORDER_BIG_ENDIAN__ 0123 /* First byte in the most significant position */
#ifdef _MSC_VER
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#endif /* _MSC_VER */
#endif /* __BYTE_ORDER__ */
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define IOFOURCC(w, o, r, d) (((w) << 24) | ((o) << 16) | ((r) << 8) | ((d) << 0))
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define IOFOURCC(w, o, r, d) (((d) << 24) | ((r) << 16) | ((o) << 8) | ((w) << 0))
#endif /* __BYTE_ORDER__ */
/********************************************************************************
********************************************************************************
********************************************************************************
***** Utilities *****
********************************************************************************
********************************************************************************
********************************************************************************/
/********************************************************************************
* HasSuffix
********************************************************************************/
static bool HasSuffix(const char *str, const char *suf) {
size_t strSize = strlen(str),
sufSize = strlen(suf);
if (strSize < sufSize)
return false;
return (0 == strcasecmp(suf, str + strSize - sufSize));
}
/********************************************************************************
* CopyDoubleToSingleVector
* This works in-place.
********************************************************************************/
static void CopyDoubleToSingleVector(const double *d, float *s, uint32_t n) {
for (float *send = s + n; s != send;) *s++ = (float)(*d++);
}
/********************************************************************************
* CopyUInt32to16Vector
* This works in-place.
********************************************************************************/
static void CopyUInt32to16Vector(const uint32_t *fr, uint16_t *to, uint32_t n) {
for (uint16_t *toEnd = to + n; to != toEnd;) *to++ = (uint16_t)(*fr++);
}
/********************************************************************************
* FaceIOErrorStringFromCode
********************************************************************************/
const char* FaceIOErrorStringFromCode(FaceIOErr err) {
struct ErrStr { FaceIOErr err; const char *str; };
static const ErrStr lut[] = {
{ kIOErrNone, "no error" },
{ kIOErrFileNotFound, "The file was not found" },
{ kIOErrFileOpen, "The file could not be opened" },
{ kIOErrEOF, "A premature end-of-file was encountered" },
{ kIOErrRead, "An error occurred while reading a file" },
{ kIOErrWrite, "An error occurred while writing a file" },
{ kIOErrSyntax, "A parsing syntax error has been encountered" },
{ kIOErrFormat, "The file has an unknown format" },
{ kIOErrParameter, "The parameter has an invalid value" },
};
for (const ErrStr *p = lut; p != &lut[sizeof(lut)/sizeof(lut[0])]; ++p)
if (p->err == err)
return p->str;
static char msg[16];
snprintf(msg, sizeof(msg), "ERR#%d", (int)err);
return msg;
}
/********************************************************************************
* PrintIOError
********************************************************************************/
static void PrintIOError(const char *file, FaceIOErr err) {
if (kIOErrNone == err) return;
printf("\"%s\": %s\n", file, FaceIOErrorStringFromCode(err));
}
/********************************************************************************
* PrintUnknownFormatMessage
********************************************************************************/
static void PrintUnknownFormatMessage(const char *file, const char *msg = nullptr) {
if (!msg) msg = "";
printf("\"%s\": Unknown format %s\n", file, msg);
}
/********************************************************************************
* ReadFileIntoString
********************************************************************************/
static FaceIOErr ReadFileIntoString(const char *file, bool isText, std::string &str) {
FaceIOErr err = kIOErrNone;
FILE *fd = NULL;
long z;
size_t y;
#ifndef _MSC_VER
fd = fopen(file, (isText ? "r" : "rb"));
#else /* _MSC_VER */
BAIL_IF_NONZERO(fopen_s(&fd, file, (isText ? "r" : "rb")), err, kIOErrFileNotFound);
#endif /* _MSC_VER */
BAIL_IF_NULL(fd, err, kIOErrFileNotFound);
fseek(fd, 0L, SEEK_END);
z = ftell(fd);
BAIL_IF_NEGATIVE(z, err, kIOErrRead); /* If there was a seek error, return an appropriate error code */
fseek(fd, 0L, SEEK_SET);
str.resize((unsigned long)z);
y = fread(&str[0], 1, z, fd); /* y differs from z when reading text on Windows, .. */
str.resize(y); /* ... because CRLF --> NL */
bail:
if (fd) fclose(fd);
return err;
}
typedef struct IbugSfmMapper {
unsigned numLandmarks;
uint16_t landmarkMap[50][2];
uint16_t rightContour[8];
uint16_t leftContour[8];
}IbugSfmMapper;
const IbugSfmMapper ibugMapping = {
68,
// Landmarks Mapping for 68 points
{
// 1 to 8 are the right contour landmarks
{9, 33}, // chin bottom
// 10 to 17 are the left contour landmarks
{18, 225}, // right eyebrow outer-corner (18)
{19, 229}, // right eyebrow between middle and outer corner
{20, 233}, // right eyebrow middle, vertical middle (20)
{21, 2086}, // right eyebrow between middle and inner corner
{22, 157}, // right eyebrow inner-corner (19)
{23, 590}, // left eyebrow inner-corner (23)
{24, 2091}, // left eyebrow between inner corner and middle
{25, 666}, // left eyebrow middle (24)
{26, 662}, // left eyebrow between middle and outer corner
{27, 658}, // left eyebrow outer-corner (22)
{28, 2842}, // bridge of the nose (parallel to upper eye lids)
{29, 379}, // middle of the nose, a bit below the lower eye lids
{30, 272}, // above nose-tip (1cm or so)
{31, 114}, // nose-tip (3)
{32, 100}, // right nostril, below nose, nose-lip junction
{33, 2794}, // nose-lip junction
{34, 270}, // nose-lip junction (28)
{35, 2797}, // nose-lip junction
{36, 537}, // left nostril, below nose, nose-lip junction
{37, 177}, // right eye outer-corner (1)
{38, 172}, // right eye pupil top right (from subject's perspective)
{39, 191}, // right eye pupil top left
{40, 181}, // right eye inner-corner (5)
{41, 173}, // right eye pupil bottom left
{42, 174}, // right eye pupil bottom right
{43, 614}, // left eye inner-corner (8)
{44, 624}, // left eye pupil top right
{45, 605}, // left eye pupil top left
{46, 610}, // left eye outer-corner (2)
{47, 607}, // left eye pupil bottom left
{48, 606}, // left eye pupil bottom right
{49, 398}, // right mouth corner (12)
{50, 315}, // upper lip right top outer
{51, 413}, // upper lip middle top right
{52, 329}, // upper lip middle top (14)
{53, 825}, // upper lip middle top left
{54, 736}, // upper lip left top outer
{55, 812}, // left mouth corner (13)
{56, 841}, // lower lip left bottom outer
{57, 693}, // lower lip middle bottom left
{58, 411}, // lower lip middle bottom (17)
{59, 264}, // lower lip middle bottom right
{60, 431}, // lower lip right bottom outer
// 61 not defined - would be right inner corner of the mouth
{62, 416}, // upper lip right bottom outer
{63, 423}, // upper lip middle bottom
{64, 828}, // upper lip left bottom outer
// 65 not defined - would be left inner corner of the mouth
{66, 817}, // lower lip left top outer
{67, 442}, // lower lip middle top
{68, 404}, // lower lip right top outer
},
{1, 2, 3, 4, 5, 6, 7, 8}, // ibug right contour points
{10, 11, 12, 13, 14, 15, 16, 17} // ibug left contour points
};
/********************************************************************************
********************************************************************************
* NVF Encapsulated object definitions
********************************************************************************
********************************************************************************/
/********************************************************************************
* Object encapsulation. The type is usually a FOURCC.
********************************************************************************/
struct EOTypeSize {
uint32_t type, size;
};
/********************************************************************************
* FOURCC types
********************************************************************************/
#define FOURCC_ADJACENT_FACES IOFOURCC('A', 'J', 'F', 'C')
#define FOURCC_ADJACENT_VERTICES IOFOURCC('A', 'J', 'V', 'X')
#define FOURCC_BASIS IOFOURCC('B', 'S', 'I', 'S')
#define FOURCC_BLEND_SHAPES IOFOURCC('B', 'L', 'N', 'D')
#define FOURCC_COLOR IOFOURCC('C', 'O', 'L', 'R')
#define FOURCC_EIGENVALUES IOFOURCC('E', 'I', 'V', 'L')
#define FOURCC_FILE_TYPE IOFOURCC('N', 'F', 'A', 'C')
#define FOURCC_IBUG IOFOURCC('I', 'B', 'U', 'G')
#define FOURCC_LANDMARK_MAP IOFOURCC('L', 'M', 'R', 'K')
#define FOURCC_LEFT_CONTOUR IOFOURCC('L', 'C', 'T', 'R')
#define FOURCC_MEAN IOFOURCC('M', 'E', 'A', 'N')
#define FOURCC_MODEL IOFOURCC('M', 'O', 'D', 'L')
#define FOURCC_MODEL_CONTOUR IOFOURCC('M', 'C', 'T', 'R')
#define FOURCC_NAME IOFOURCC('N', 'A', 'M', 'E')
#define FOURCC_RIGHT_CONTOUR IOFOURCC('R', 'C', 'T', 'R')
#define FOURCC_SHAPE IOFOURCC('S', 'H', 'A', 'P')
#define FOURCC_TEXTURE_COORDS IOFOURCC('T', 'X', 'C', 'O')
#define FOURCC_EOTOC IOFOURCC('T', 'O', 'C', '0')
#define FOURCC_TOPOLOGY IOFOURCC('T', 'O', 'P', 'O')
#define FOURCC_TRIANGLE_LIST IOFOURCC('T', 'R', 'N', 'G')
#define FOURCC_NVLM IOFOURCC('N', 'V', 'L', 'M')
#define FOURCC_PARTITIONS IOFOURCC('P', 'R', 'T', 'S')
#define FOURCC_PART IOFOURCC('P', 'A', 'R', 'T')
#define FOURCC_MATERIAL IOFOURCC('M', 'T', 'R', 'L')
/********************************************************************************
* NVFFileHeader
********************************************************************************/
struct NVFFileHeader {
enum { // Illustrate 2 bit packets packed into a byte by the specified scheme
LITTLE_ENDIAN_CODE = 0xE4, // = 11 10 01 00 // or 0x10 = 0001 0000 -- 4 bit packets
BIG_ENDIAN_CODE = 0x1B // = 00 01 10 11 // or 0x01 = 0000 0001
};
unsigned type;
unsigned size;
unsigned char endian, sizeBits, indexBits, zero;
unsigned tocLoc;
NVFFileHeader() {
type = FOURCC_FILE_TYPE;
size = sizeof(*this) - sizeof(type) - sizeof(size);
endian = LITTLE_ENDIAN_CODE;
sizeBits = 32; // 32 bits for the object size
indexBits = 16; // 16 bits for indices
zero = 0;
tocLoc = 0;
}
void clear() { memset(this, 0, sizeof(*this)); }
static unsigned EOTOCOffset() {
return (char *)(&((NVFFileHeader *)nullptr)->tocLoc) - (char *)(&((NVFFileHeader *)nullptr)->type);
}
};
namespace { // anonymous
/********************************************************************************
********************************************************************************
* EOWriter - Encapsulated Object Writer
********************************************************************************
********************************************************************************/
class EOWriter
{
public:
EOWriter();
~EOWriter();
/// Open a file for writing.
/// @param[in] fileName the name of the file to be written to.
/// @return kIOErrNone if the file was opened successfully.
/// @return kIOErrWrite if there were problems opening the file for writing.
FaceIOErr open(const char *fileName);
/// Close the file without writing the table of contents, and flush the output.
FaceIOErr close();
/// Write the table of contents and close the file.
/// @param[in] tocRefOffset the offset from the beginning of the file where the location of the TOC is stored.
/// @return kIOErrNone if the operation was completed successfully.
FaceIOErr writeTocAndClose(unsigned tocRefOffset);
/// Get the file descriptor.
/// @return the file descriptor, or NULL if there is no open file.
FILE* fileDescriptor();
/// Write the object encapsulation header.
/// @param[in] type a FOURCC 4-byte code.
/// @param[in] size a 4-byte size for the remainder of the object.
/// @param[in] tag a tag for the TOC. 0 makes no entry into the TOC.
/// Usually the outer objects are in the TOC. But one may also include
/// subobjects in the TOC, or no objects at all.
/// @return kIOErrNone if the operation was completed successfully.
FaceIOErr writeEncapsulationHeader(unsigned type, unsigned size, unsigned tag = 0);
/// Write an encapsulated opaque object.
/// @param[in] type a FOURCC 4-byte code.
/// @param[in] size the size in bytes of the data to be written.
/// @param[in] data the data to be written.
/// @param[in] tag a tag for the TOC. 0 makes no entry into the TOC.
/// Usually the outer objects are in the TOC. But one may also include
/// subobjects in the TOC, or no objects at all.
/// @return kIOErrNone if the operation was completed successfully.
FaceIOErr writeOpaqueObject(unsigned type, unsigned size, const void *data, unsigned tag = 0);
FaceIOErr writeOpaqueObject(unsigned type, size_t size, const void *data, unsigned tag = 0)
{ return writeOpaqueObject(type, (unsigned)size, data, tag); }
/// Write opaque data
/// @param[in] size the size in bytes of the data to be written.
/// @param[in] data the data to be written.
/// @return kIOErrNone if the operation was completed successfully.
FaceIOErr writeData(unsigned size, const void *data);
FaceIOErr writeData(size_t size, const void *data) { return writeData((unsigned)size, data); }
private:
struct Impl;
Impl *pimpl;
};
struct EOTOC {
unsigned _tag;
unsigned _offset;
EOTOC(unsigned tag, unsigned offset) {
_tag = tag;
_offset = offset;
}
};
struct EOEncapsulation {
unsigned _type, _size;
EOEncapsulation(unsigned type, unsigned size) {
_type = type;
_size = size;
}
};
struct EOWriter::Impl {
FILE *fd;
std::vector<EOTOC> toc;
void addToEOTOC(unsigned tag) {
if (tag) toc.emplace_back(tag, (unsigned)ftell(fd));
}
};
EOWriter::EOWriter() {
pimpl = new EOWriter::Impl;
pimpl->fd = nullptr;
}
EOWriter::~EOWriter() {
if (pimpl->fd) fclose(pimpl->fd);
delete pimpl;
}
FILE *EOWriter::fileDescriptor() { return pimpl->fd; }
FaceIOErr EOWriter::open(const char *fileName) {
pimpl->fd = nullptr;
#ifndef _MSC_VER
pimpl->fd = fopen(fileName, "wb");
#else /* _MSC_VER */
if (0 != fopen_s(&pimpl->fd, fileName, "wb"))
pimpl->fd = nullptr;
#endif /* _MSC_VER */
return pimpl->fd ? kIOErrNone : kIOErrWrite;
}
FaceIOErr EOWriter::close() {
fclose(pimpl->fd);
pimpl->fd = nullptr;
return kIOErrNone;
}
FaceIOErr EOWriter::writeEncapsulationHeader(unsigned type, unsigned size, unsigned tag) {
EOEncapsulation eo(type, size);
pimpl->addToEOTOC(tag);
size_t z = fwrite(&eo, sizeof(eo._type), sizeof(eo) / sizeof(eo._type), pimpl->fd);
return (z == (sizeof(eo) / sizeof(eo._type))) ? kIOErrNone : kIOErrWrite;
}
FaceIOErr EOWriter::writeData(unsigned size, const void *data) {
return (1 == fwrite(data, size, 1, pimpl->fd)) ? kIOErrNone : kIOErrWrite;
}
FaceIOErr EOWriter::writeOpaqueObject(unsigned type, unsigned size, const void *data, unsigned tag) {
FaceIOErr err = writeEncapsulationHeader(type, size, tag);
if (!err) err = writeData(size, data);
return err;
}
FaceIOErr EOWriter::writeTocAndClose(unsigned tocRefOffset) {
FaceIOErr err = kIOErrNone;
unsigned numRecords = (unsigned)pimpl->toc.size();
if (numRecords) {
unsigned offset = ftell(pimpl->fd),
size = sizeof(unsigned) + numRecords * sizeof(EOTOC);
err = writeEncapsulationHeader(FOURCC_EOTOC, size, 0);
if (kIOErrNone == err) {
unsigned recordSize = sizeof(EOTOC);
BAIL_IF_ERR(
err = (1 == fwrite(&recordSize, sizeof(recordSize), 1, pimpl->fd))
? kIOErrNone
: kIOErrWrite);
BAIL_IF_ERR(err = (numRecords == fwrite(pimpl->toc.data(), sizeof(EOTOC),
numRecords, pimpl->fd))
? kIOErrNone
: kIOErrWrite);
BAIL_IF_NONZERO(fseek(pimpl->fd, tocRefOffset, SEEK_SET), err,
kIOErrWrite);
BAIL_IF_ERR(err = (1 == fwrite(&offset, sizeof(offset), 1, pimpl->fd))
? kIOErrNone
: kIOErrWrite);
}
}
fclose(pimpl->fd);
pimpl->fd = nullptr;
bail:
return err;
}
/********************************************************************************
********************************************************************************
* JSON reader and writer
********************************************************************************
********************************************************************************/
enum JSONNodeType {
kJSONObject,
kJSONArray,
kJSONString,
kJSONNumber,
kJSONMember,
kJSONBoolean,
kJSONNull
};
struct JSONInfo {
void *userData;
JSONNodeType type;
const char *value;
double number;
};
typedef FaceIOErr (*JSONOpenNodeProc)(JSONInfo *info);
typedef FaceIOErr (*JSONCloseNodeProc)(JSONInfo *info);
class JSONReader {
public:
JSONReader(JSONOpenNodeProc openNode, JSONCloseNodeProc closeNode);
~JSONReader() {}
/// Parse from a string.
/// @param[in] str the string to be parsed.
/// @param[in] len the length of the string to be parsed; if 0, it is
/// assumed to be NULL-terminated.
/// @param[in,out] state the parser state.
/// @return kIOErrNone if the operation was completed successfully.
/// @return kIOErrSyntax if a syntax error occurred during parsing.
/// @return kIOErrEOF if the file ended sooner than expected.
FaceIOErr parse(const char *str, size_t len, void *state);
/// Parse from a file.
/// @param[in] fileName the file to be parsed.
/// @param[in,out] state the parser state.
/// @return kIOErrNone if the operation was completed successfully.
/// @return kIOErrSyntax if a syntax error occurred during parsing.
/// @return kIOErrEOF if the file ended sooner than expected.
FaceIOErr parse(const char *fileName, void *state);
private:
JSONOpenNodeProc _openNode;
JSONCloseNodeProc _closeNode;
const char *_jsonStr;
size_t _jsonLen;
JSONInfo _infoBack;
void skipWhiteSpace();
FaceIOErr readNumber();
FaceIOErr readString();
FaceIOErr readValue();
FaceIOErr readArray();
FaceIOErr readObject();
FaceIOErr getString(std::string *str);
};
class JSONWriter {
public:
JSONWriter();
~JSONWriter();
FaceIOErr open(const char *file);
FaceIOErr close();
void openObject(const char *tag = nullptr);
void closeObject();
void openArray(const char *tag = nullptr);
void closeArray();
void writeNumericArray(unsigned n, const float *v, unsigned maxRow = 0, const char *tag = nullptr);
void writeNumericArray(unsigned n, const double *v, unsigned maxRow = 0, const char *tag = nullptr);
void writeNumericArray(unsigned n, const int *v, unsigned maxRow = 0, const char *tag = nullptr);
void writeNumericArray(unsigned n, const unsigned short *v, unsigned maxRow = 0, const char *tag = nullptr);
void writeNumericArray(unsigned n, const unsigned char *v, unsigned maxRow = 0, const char *tag = nullptr);
void writeNumber(double number, const char *tag = nullptr);
void writeString(const char *str, const char *tag = nullptr);
void writeBool(bool value, const char *tag = nullptr);
void writeNull(const char *tag = nullptr);
private:
class Impl;
Impl *pimpl;
};
JSONReader::JSONReader(JSONOpenNodeProc openNode, JSONCloseNodeProc closeNode) {
_openNode = openNode;
_closeNode = closeNode;
_jsonStr = nullptr;
_jsonLen = 0;
_infoBack.userData = nullptr;
_infoBack.type = kJSONNull;
_infoBack.value = nullptr;
_infoBack.number = NAN;
}
void JSONReader::skipWhiteSpace() {
for (; _jsonLen && isspace(*_jsonStr); ++_jsonStr, --_jsonLen) {}
}
FaceIOErr JSONReader::getString(std::string *str) {
FaceIOErr err = kIOErrNone;
const char *s;
char c, quote;
size_t n;
str->clear();
BAIL_IF_FALSE(_jsonLen > 2, err, kIOErrEOF); /* not enough characters */
n = _jsonLen;
c = *(s = _jsonStr);
BAIL_IF_FALSE(('"' == c || '\'' == c), err, kIOErrSyntax); /* missing quotes */
quote = c;
for (--n, ++s; n--; s++)
if (quote == *s) /* TODO: check for escapes */
break;
n = s - _jsonStr + 1;
BAIL_IF_FALSE(n >= 2, err, kIOErrEOF); /* not enough characters */
str->assign(_jsonStr + 1, n - 2);
_jsonStr += n;
_jsonLen -= n;
bail:
if (kIOErrSyntax == err) printf("Expecting a string, but found no quotes\n");
return err;
}
FaceIOErr JSONReader::readString() {
FaceIOErr err;
std::string str;
BAIL_IF_ERR(err = getString(&str));
_infoBack.type = kJSONString;
_infoBack.value = str.c_str();
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
err = (*_closeNode)(&_infoBack);
bail:
_infoBack.value = nullptr; /* Make Coverity happy */
return err;
}
FaceIOErr JSONReader::readObject() {
FaceIOErr err;
std::string str;
_infoBack.type = kJSONObject; /* Open an object node */
_infoBack.value = nullptr;
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
BAIL_IF_FALSE('{' == _jsonStr[0], err, kIOErrSyntax); /* This is where we always start */
--_jsonLen;
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
++_jsonStr;
skipWhiteSpace();
if ('}' != _jsonStr[0]) {
do {
skipWhiteSpace();
BAIL_IF_ERR(err = getString(&str)); /* Every member must have a name */
skipWhiteSpace();
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
BAIL_IF_FALSE(':' == _jsonStr[0], err, kIOErrSyntax); /* Followed by a colon */
--_jsonLen;
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
++_jsonStr;
skipWhiteSpace();
_infoBack.type = kJSONMember; /* Start a new member */
_infoBack.value = str.c_str();
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
BAIL_IF_ERR(err = readValue()); /* Get the member */
_infoBack.type = kJSONMember; /* Close the member */
BAIL_IF_ERR(err = (*_closeNode)(&_infoBack));
skipWhiteSpace();
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
if ('}' == _jsonStr[0]) /* Look for ending */
break; /* Normal object exit */
BAIL_IF_FALSE(',' == _jsonStr[0], err, kIOErrNotValue); /* Comma-separated members */
--_jsonLen;
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
++_jsonStr;
} while (1);
}
--_jsonLen; /* Always ('}' == _jsonStr[0]) here */
++_jsonStr;
_infoBack.type = kJSONObject; /* Close the object node */
_infoBack.value = nullptr;
err = (*_closeNode)(&_infoBack);
bail:
_infoBack.value = nullptr; /* Make Coverity happy */
return err;
}
FaceIOErr JSONReader::readArray() {
FaceIOErr err;
_infoBack.type = kJSONArray;
_infoBack.value = nullptr;
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
BAIL_IF_FALSE('[' == _jsonStr[0], err, kIOErrSyntax); /* We always enter in this state */
--_jsonLen;
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
++_jsonStr;
skipWhiteSpace();
if (']' != _jsonStr[0]) {
do {
skipWhiteSpace();
BAIL_IF_ERR(err = readValue());
skipWhiteSpace();
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
if (']' == _jsonStr[0]) break;
BAIL_IF_FALSE(',' == _jsonStr[0], err, kIOErrEOF);
--_jsonLen;
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
++_jsonStr;
} while (1);
}
--_jsonLen; /* Always (']' == _jsonStr[0]) here */
++_jsonStr;
_infoBack.type = kJSONArray;
_infoBack.value = nullptr;
err = (*_closeNode)(&_infoBack);
bail:
return err;
}
FaceIOErr JSONReader::readNumber() {
FaceIOErr err = kIOErrNone;
char *endPtr;
size_t n;
std::string str;
_infoBack.number = strtod(_jsonStr, &endPtr); /* Parse the number */
n = endPtr - _jsonStr;
BAIL_IF_FALSE(n <= _jsonLen, err, kIOErrEOF);
BAIL_IF_ZERO(n, err, kIOErrSyntax);
str.assign(_jsonStr, n);
_jsonStr += n;
_infoBack.type = kJSONNumber; /* Open bracket */
_infoBack.value = str.c_str();
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
err = (*_closeNode)(&_infoBack); /* Close bracket */
bail:
_infoBack.value = nullptr; /* Make Coverity happy */
return err;
}
FaceIOErr JSONReader::readValue() {
static const char kTrue[] = "true", kFalse[] = "false", kNull[] = "null";
FaceIOErr err = kIOErrNotValue;
skipWhiteSpace();
BAIL_IF_ZERO(_jsonLen, err, kIOErrEOF);
if ('{' == _jsonStr[0]) // Object
return readObject();
if ('[' == _jsonStr[0]) // Array
return readArray();
if ('"' == _jsonStr[0] || '\'' == _jsonStr[0]) // String
return readString();
if (('0' <= _jsonStr[0] && _jsonStr[0] <= '9') || // Number
'-' == _jsonStr[0] || '+' == _jsonStr[0] || '.' == _jsonStr[0])
return readNumber();
if (_jsonLen >= 4 && !strncasecmp(_jsonStr, kTrue, 4)) // Boolean true
{ // TODO: look out for "true" token prefix
_jsonStr += 4;
_jsonLen -= 4;
_infoBack.type = kJSONBoolean;
_infoBack.value = kTrue;
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
return (*_closeNode)(&_infoBack);
}
if (_jsonLen >= 5 && !strncasecmp(_jsonStr, kFalse, 5)) // Boolean false
{ // TODO: look out for "false" token prefix
_jsonStr += 5;
_jsonLen -= 5;
_infoBack.type = kJSONBoolean;
_infoBack.value = kFalse;
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
return (*_closeNode)(&_infoBack);
}
if (_jsonLen >= 4 && !strncasecmp(_jsonStr, kNull, 4)) // null
{ // TODO: look out for " null" token prefix
_jsonStr += 4;
_jsonLen -= 4;
_infoBack.type = kJSONNull;
_infoBack.value = kNull;
BAIL_IF_ERR(err = (*_openNode)(&_infoBack));
return (*_closeNode)(&_infoBack);
}
bail:
return err;
}
FaceIOErr JSONReader::parse(const char *str, size_t len, void *userState) {
FaceIOErr err;
_jsonStr = str;
_jsonLen = len ? len : strlen(str);
_infoBack.userData = userState;
err = readValue();
if (kIOErrNone != err) printf("Syntax error at \"%.32s...\"\n", _jsonStr);
return err;
}
FaceIOErr JSONReader::parse(const char *fileName, void *userState) {
std::string str;
FaceIOErr err = ReadFileIntoString(fileName, true, str);
if (kIOErrNone == err) err = parse(str.c_str(), str.size(), userState);
return err;
}
class JSONWriter::Impl {
public:
int level;
FILE *fd;
std::stack<int> count;
void doIndent();
void maybeComma();
void maybeTag(const char *tag);
void commaIndentTag(const char *tag);
template<typename T> void writeArray(unsigned n, const T *v, unsigned maxRow, const char *fmt, const char *tag);
void openObject(const char *tag);
void closeObject();
void openArray(const char *tag);
void closeArray();
};
void JSONWriter::Impl::doIndent() {
for (int i = level * 2; i-- > 0;) putc(' ', fd);
}
void JSONWriter::Impl::maybeComma() {
if (count.top())
putc(',', fd);
count.top()++;
}
void JSONWriter::Impl::maybeTag(const char *tag) {
if (!tag) return;
fprintf(fd, "\"%s\": ", tag);
}
void JSONWriter::Impl::commaIndentTag(const char *tag) {
maybeComma();
if (level) putc('\n', fd);
doIndent();
maybeTag(tag);
}
void JSONWriter::Impl::openObject(const char *tag)
{
commaIndentTag(tag);
putc('{', fd);
++(level);
count.push(0);
}
void JSONWriter::Impl::closeObject()
{
--(level);
putc('\n', fd);
doIndent();
putc('}', fd);
count.pop();
}
void JSONWriter::Impl::openArray(const char *tag)
{
commaIndentTag(tag);
putc('[', fd);
++(level);
count.push(0);
}
void JSONWriter::Impl::closeArray()
{
--(level);
putc('\n', fd);
doIndent();
putc(']', fd);
count.pop();
}
JSONWriter::JSONWriter() {
pimpl = new Impl();
pimpl->level = 0;
pimpl->fd = nullptr;
pimpl->count.push(0);
}
JSONWriter::~JSONWriter() {
if (pimpl->fd && pimpl->fd != stdout) fclose(pimpl->fd);
delete pimpl;
}
FaceIOErr JSONWriter::open(const char *file) {
if (pimpl->fd && pimpl->fd != stdout) (void)(this->close());
if (file && file[0]) {
#ifndef _MSC_VER
pimpl->fd = fopen(file, "w");
#else /* _MSC_VER */
if (0 != fopen_s(&pimpl->fd, file, "w"))
pimpl->fd = nullptr;
#endif /* _MSC_VER */
} else {
pimpl->fd = stdout;
}
return pimpl->fd ? kIOErrNone : kIOErrWrite;
}
FaceIOErr JSONWriter::close() {
putc('\n', pimpl->fd);
if (pimpl->fd && pimpl->fd != stdout) (void)fclose(pimpl->fd);
pimpl->fd = nullptr;
return kIOErrNone;
}
void JSONWriter::openObject(const char *tag) {
pimpl->openObject(tag);
}
void JSONWriter::closeObject() {
pimpl->closeObject();
}
void JSONWriter::openArray(const char *tag) {
pimpl->openArray(tag);
}
void JSONWriter::closeArray() {
pimpl->closeArray();
}
template<typename T>
void JSONWriter::Impl::writeArray(unsigned n, const T * v, unsigned maxRow, const char *fmt, const char * tag)
{
openArray(tag);
putc('\n', fd);
if (n) {
for (; n > maxRow; n -= maxRow) {
doIndent();
for (unsigned i = maxRow; i--; ++v) {
fprintf(fd, fmt, *v, (i ? ' ' : '\n'));
putc(',', fd);
putc((i ? ' ' : '\n'), fd);
}
}
doIndent();
for (; n--; ++v) {
fprintf(fd, fmt, *v, (n ? ", " : ""));
if (n)
fprintf(fd, ", ");
}
}
closeArray();
}
void JSONWriter::writeNumericArray(unsigned n, const float *v, unsigned maxRow, const char *tag) {
pimpl->writeArray(n, v, maxRow, "%.8g", tag);
}
void JSONWriter::writeNumericArray(unsigned n, const double *v, unsigned maxRow, const char *tag) {
pimpl->writeArray(n, v, maxRow, "%.17g", tag);
}
void JSONWriter::writeNumericArray(unsigned n, const int *v, unsigned maxRow, const char *tag) {
pimpl->writeArray(n, v, maxRow, "%d", tag);
}
void JSONWriter::writeNumericArray(unsigned n, const unsigned short *v, unsigned maxRow, const char *tag) {
pimpl->writeArray(n, v, maxRow, "%u", tag);
}
void JSONWriter::writeNumericArray(unsigned n, const unsigned char *v, unsigned maxRow, const char *tag) {
pimpl->writeArray(n, v, maxRow, "%u", tag);
}
void JSONWriter::writeNumber(double number, const char *tag) {
pimpl->commaIndentTag(tag);
fprintf(pimpl->fd, "%.17g", number);
}
void JSONWriter::writeString(const char *str, const char *tag) {
pimpl->commaIndentTag(tag);
fprintf(pimpl->fd, "\"%s\"", str);
}
void JSONWriter::writeBool(bool value, const char *tag) {
pimpl->commaIndentTag(tag);
fprintf(pimpl->fd, "%s", value ? "true" : "false");
}
void JSONWriter::writeNull(const char *tag) {
pimpl->commaIndentTag(tag);
fprintf(pimpl->fd, "null");
}
} // namespace anonymous
/*******************************************************************************
********************************************************************************
********************************************************************************
***** NVF Output *****
********************************************************************************
********************************************************************************
********************************************************************************/
namespace /* anonymous */ {
template <typename ElementType>
uint32_t NVFSize(uint32_t n, const ElementType *v) {
return (uint32_t)(n * sizeof(*v));
}
template <typename ElementType>
FaceIOErr NVFWriteOpaqueObject(uint32_t type, uint32_t size, const ElementType *v, EOWriter &wtr, unsigned tag = 0) {
return wtr.writeOpaqueObject(type, size * sizeof(*v), v, tag);
}
} // namespace
static uint32_t NVFSizeShapeModel(const FaceIOAdapter *fac) {
uint32_t size = 8 + NVFSize(fac->getShapeMeanSize(), fac->getShapeMean())
+ 8 + NVFSize(fac->getShapeModesSize(), fac->getShapeModes())
+ sizeof(uint32_t)
+ 8 + NVFSize(fac->getShapeEigenvaluesSize(), fac->getShapeEigenvalues())
+ 8 + NVFSize(fac->getTriangleListSize(), fac->getTriangleList());
return size;
}
static FaceIOErr NVFWriteShapeModel(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err;
uint32_t numModes = fac->getShapeNumModes(), sizeNumMode = sizeof(uint32_t),
sizeMean = NVFSize(fac->getShapeMeanSize(), fac->getShapeMean()),
sizeModes = NVFSize(fac->getShapeModesSize(), fac->getShapeModes()),
sizeEigen = NVFSize(fac->getShapeEigenvaluesSize(), fac->getShapeEigenvalues()),
sizeTriList = NVFSize(fac->getTriangleListSize(), fac->getTriangleList()),
size = 8 + sizeMean + 8 + sizeModes + sizeNumMode + 8 + sizeEigen + 8 + sizeTriList;
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_SHAPE, size, 0));
BAIL_IF_ERR(err = wtr.writeOpaqueObject(FOURCC_MEAN, sizeMean, fac->getShapeMean(), 0));
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_BASIS, sizeModes + sizeNumMode, 0));
BAIL_IF_ERR(err = wtr.writeData(sizeNumMode, &numModes));
BAIL_IF_ERR(err = wtr.writeData(sizeModes, fac->getShapeModes()));
BAIL_IF_ERR(err = wtr.writeOpaqueObject(FOURCC_EIGENVALUES, sizeEigen, fac->getShapeEigenvalues(), 0));
BAIL_IF_ERR(err = wtr.writeOpaqueObject(FOURCC_TRIANGLE_LIST, sizeTriList, fac->getTriangleList(), 0));
bail:
return err;
}
static uint32_t NVFSizeColorModel(const FaceIOAdapter *fac) {
uint32_t sizeMean = NVFSize(fac->getColorMeanSize(), fac->getColorMean()),
sizeModes = NVFSize(fac->getColorModesSize(), fac->getColorModes()),
sizeEigen = NVFSize(fac->getColorEigenvaluesSize(), fac->getColorEigenvalues()),
sizeTriList = 0, // NVFSize(fac->getTriangleListSize(), fac->getTriangleList());
size = sizeMean + sizeModes + sizeEigen + sizeTriList;
if (size) size += 8 + 8 + 8 + 8;
return size;
}
static FaceIOErr NVFWriteColorModel(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err = kIOErrNone;
uint32_t numModes = fac->getColorNumModes(), sizeNumMode = sizeof(uint32_t),
sizeMean = NVFSize(fac->getColorMeanSize(), fac->getColorMean()),
sizeModes = NVFSize(fac->getColorModesSize(), fac->getColorModes()),
sizeEigen = NVFSize(fac->getColorEigenvaluesSize(), fac->getColorEigenvalues()),
sizeTriList = 0, // NVFSize(fac->getTriangleListSize(), fac->getTriangleList());
size = 8 + sizeMean + 8 + sizeModes + sizeNumMode + 8 + sizeEigen + 8 + sizeTriList;
if (sizeMean + sizeModes + sizeEigen != 0) {
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_COLOR, size, 0));
BAIL_IF_ERR(err = wtr.writeOpaqueObject(FOURCC_MEAN, sizeMean, fac->getColorMean(), 0));
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_BASIS, sizeModes + sizeNumMode, 0));
BAIL_IF_ERR(err = wtr.writeData(sizeNumMode, &numModes));
BAIL_IF_ERR(err = wtr.writeData(sizeModes, fac->getColorModes()));
BAIL_IF_ERR(err = wtr.writeOpaqueObject(FOURCC_EIGENVALUES, sizeEigen, fac->getColorEigenvalues(), 0));
BAIL_IF_ERR(err = wtr.writeOpaqueObject(FOURCC_TRIANGLE_LIST, sizeTriList, fac->getTriangleList(), 0));
}
bail:
return err;
}
static uint32_t NVFSizeMorphableModel(const FaceIOAdapter *fac) {
uint32_t size = 8 + NVFSizeShapeModel(fac),
textureSize = NVFSize(fac->getTextureCoordinatesSize(), fac->getTextureCoordinates()),
colorSize = NVFSizeColorModel(fac);
if (textureSize) size += 8 + textureSize;
if (colorSize) size += 8 + colorSize;
return size;
}
static FaceIOErr NVFWriteMorphableModel(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err;
uint32_t size;
/* Write the shape model */
BAIL_IF_ERR(err = NVFWriteShapeModel(fac, wtr));
/* Write the color model */
BAIL_IF_ERR(err = NVFWriteColorModel(fac, wtr));
/* Write the texture coordinates */
if (0 != (size = NVFSize(fac->getTextureCoordinatesSize(), fac->getTextureCoordinates())))
BAIL_IF_ERR(err = wtr.writeOpaqueObject(FOURCC_TEXTURE_COORDS, size, fac->getTextureCoordinates()));
bail:
return err;
}
static uint32_t NVFSizeString(const char *str) {
return (strlen(str) + 3) & ~3; // bump up to multiple of 4
}
static FaceIOErr NVFWriteString(const char *str, EOWriter &wtr) {
uint32_t strSize = (uint32_t)strlen(str);
FaceIOErr err = wtr.writeData(strSize, str);
uint32_t pad = (uint32_t)(NVFSizeString(str) - strSize);
BAIL_IF_ERR(err);
if (pad) err = wtr.writeData(pad, "\0\0\0");
bail:
return err;
}
static uint32_t NVFSizeBlendShapes(const FaceIOAdapter *fac) {
uint32_t size = sizeof(uint32_t), // sizeof(numShapes)
numShapes = fac->getNumBlendShapes(), i;
for (i = 0; i < numShapes; ++i) {
size += 8 + NVFSizeString(fac->getBlendShapeName(i));
size += 8 + NVFSize(fac->getBlendShapeSize(i), fac->getBlendShape(i));
}
return size;
}
static FaceIOErr NVFWriteBlendShapes(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err = kIOErrNone;
uint32_t numShapes = fac->getNumBlendShapes();
uint32_t i;
BAIL_IF_ERR(err = wtr.writeData(sizeof(numShapes), &numShapes));
for (i = 0; i < numShapes; ++i) {
const char *str = fac->getBlendShapeName(i);
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_NAME, NVFSizeString(str)));
BAIL_IF_ERR(err = NVFWriteString(str, wtr));
BAIL_IF_ERR(err = NVFWriteOpaqueObject(FOURCC_SHAPE, fac->getBlendShapeSize(i), fac->getBlendShape(i), wtr));
}
bail:
return err;
}
static uint32_t NVFSizeIbugMappings(const FaceIOAdapter *fac) {
uint32_t size;
size = 8 + NVFSize(fac->getIbugLandmarkMappingsSize(), fac->getIbugLandmarkMappings())
+ 8 + NVFSize(fac->getIbugRightContourSize(), fac->getIbugRightContour())
+ 8 + NVFSize(fac->getIbugLeftContourSize(), fac->getIbugLeftContour());
return size;
}
static FaceIOErr NVFWriteIbugMappings(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err;
BAIL_IF_ERR(err = NVFWriteOpaqueObject(FOURCC_LANDMARK_MAP, fac->getIbugLandmarkMappingsSize(),
fac->getIbugLandmarkMappings(), wtr));
BAIL_IF_ERR(err = NVFWriteOpaqueObject(FOURCC_RIGHT_CONTOUR, fac->getIbugRightContourSize(),
fac->getIbugRightContour(), wtr));
BAIL_IF_ERR(
err = NVFWriteOpaqueObject(FOURCC_LEFT_CONTOUR, fac->getIbugLeftContourSize(), fac->getIbugLeftContour(), wtr));
bail:
return err;
}
static uint32_t NVFSizeModelContours(const FaceIOAdapter *fac) {
uint32_t size = 8 + NVFSize(fac->getModelRightContourSize(), fac->getModelRightContour())
+ 8 + NVFSize(fac->getModelLeftContourSize(), fac->getModelLeftContour());
return size;
}
static FaceIOErr NVFWriteModelContours(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err;
BAIL_IF_ERR(err = NVFWriteOpaqueObject(FOURCC_RIGHT_CONTOUR, fac->getModelRightContourSize(),
fac->getModelRightContour(), wtr));
BAIL_IF_ERR(
err = NVFWriteOpaqueObject(FOURCC_LEFT_CONTOUR, fac->getModelLeftContourSize(), fac->getModelLeftContour(), wtr));
bail:
return err;
}
static uint32_t NVFSizeTopology(const FaceIOAdapter *fac) {
uint32_t size = 8 + NVFSize(fac->getAdjacentFacesSize(), fac->getAdjacentFaces()) + 8 +
NVFSize(fac->getAdjacentVerticesSize(), fac->getAdjacentVertices());
return size;
}
static FaceIOErr NVFWriteTopology(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err;
BAIL_IF_ERR(
err = NVFWriteOpaqueObject(FOURCC_ADJACENT_FACES, fac->getAdjacentFacesSize(), fac->getAdjacentFaces(), wtr));
BAIL_IF_ERR(err = NVFWriteOpaqueObject(FOURCC_ADJACENT_VERTICES, fac->getAdjacentVerticesSize(),
fac->getAdjacentVertices(), wtr));
bail:
return err;
}
static uint32_t NVFSizeNVLM(const FaceIOAdapter *fac) {
uint32_t size = 8 + NVFSize(fac->getNvlmLandmarksSize(), fac->getNvlmLandmarks())
+ 8 + NVFSize(fac->getNvlmRightContourSize(), fac->getNvlmRightContour())
+ 8 + NVFSize(fac->getNvlmLeftContourSize(), fac->getNvlmLeftContour());
return size;
}
static FaceIOErr NVFWriteNVLM(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err;
BAIL_IF_ERR(
err = NVFWriteOpaqueObject(FOURCC_LANDMARK_MAP, fac->getNvlmLandmarksSize(), fac->getNvlmLandmarks(), wtr));
BAIL_IF_ERR(
err = NVFWriteOpaqueObject(FOURCC_RIGHT_CONTOUR, fac->getNvlmRightContourSize(), fac->getNvlmRightContour(), wtr));
BAIL_IF_ERR(
err = NVFWriteOpaqueObject(FOURCC_LEFT_CONTOUR, fac->getNvlmLeftContourSize(), fac->getNvlmLeftContour(), wtr));
bail:
return err;
}
struct TPart {
uint32_t partitionIndex, faceIndex, numFaces, vertexIndex, numVertices;
int32_t smoothingGroup;
};
static uint32_t NVFSizePartitions(const FaceIOAdapter *fac) {
uint32_t numPartitions = fac->getNumPartitions(),
size = sizeof(numPartitions),
i, z;
for (i = 0; i < numPartitions; ++i) {
size += 8; // type, size
size += sizeof(TPart); // partitionIndex, faceIndex, numFaces, vertexIndex, numVertices, smoothingGroup
if (0 != (z = NVFSizeString(fac->getPartitionName(i)))) size += 8 + z;
if (0 != (z = NVFSizeString(fac->getPartitionMaterialName(i)))) size += 8 + z;
}
return size;
}
static FaceIOErr NVFWritePartitions(const FaceIOAdapter *fac, EOWriter &wtr) {
FaceIOErr err = kIOErrNone;
uint32_t numPartitions = fac->getNumPartitions(), i;
EOTypeSize ts;
TPart part;
const char *name, *mtrl;
uint32_t nameSize, mtrlSize;
ts.type = FOURCC_PART;
BAIL_IF_ERR(err = wtr.writeData(sizeof(numPartitions), &numPartitions));
for (i = 0; i < numPartitions; ++i) {
part.partitionIndex = fac->getPartition(
i, &part.faceIndex, &part.numFaces, &part.vertexIndex, &part.numVertices, &part.smoothingGroup);
nameSize = (nullptr != (name = fac->getPartitionName(i)) && name[0]) ? NVFSizeString(name) : 0;
mtrlSize = (nullptr != (mtrl = fac->getPartitionMaterialName(i)) && mtrl[0]) ? NVFSizeString(mtrl) : 0;
ts.size = sizeof(part);
if (nameSize) ts.size += 8 + nameSize;
if (mtrlSize) ts.size += 8 + mtrlSize;
BAIL_IF_ERR(err = wtr.writeData(sizeof(ts), &ts)); // {type,size}
BAIL_IF_ERR(err = wtr.writeData(sizeof(part), &part)); // unencapsulated 6 int16s
if (nameSize) {
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_NAME, nameSize));
BAIL_IF_ERR(err = NVFWriteString(name, wtr));
}
if (mtrlSize) {
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_MATERIAL, mtrlSize));
BAIL_IF_ERR(err = NVFWriteString(mtrl, wtr));
}
}
bail:
return err;
}
/********************************************************************************
* API WriteNVFFaceModel API *
********************************************************************************/
FaceIOErr WriteNVFFaceModel(FaceIOAdapter *fac, const char *fileName) {
FaceIOErr err;
EOWriter wtr;
NVFFileHeader header;
err = wtr.open(fileName);
if (kIOErrNone != err) {
PrintIOError(fileName, err);
goto bail;
}
BAIL_IF_ERR(err = wtr.writeData(sizeof(header), &header));
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_MODEL, NVFSizeMorphableModel(fac), FOURCC_MODEL));
BAIL_IF_ERR(err = NVFWriteMorphableModel(fac, wtr));
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_IBUG, NVFSizeIbugMappings(fac), FOURCC_IBUG));
BAIL_IF_ERR(err = NVFWriteIbugMappings(fac, wtr));
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_BLEND_SHAPES, NVFSizeBlendShapes(fac), FOURCC_BLEND_SHAPES));
BAIL_IF_ERR(err = NVFWriteBlendShapes(fac, wtr));
BAIL_IF_ERR(err =
wtr.writeEncapsulationHeader(FOURCC_MODEL_CONTOUR, NVFSizeModelContours(fac), FOURCC_MODEL_CONTOUR));
BAIL_IF_ERR(err = NVFWriteModelContours(fac, wtr));
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_TOPOLOGY, NVFSizeTopology(fac), FOURCC_TOPOLOGY));
BAIL_IF_ERR(err = NVFWriteTopology(fac, wtr));
if (fac->getNvlmLandmarksSize()) {
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_NVLM, NVFSizeNVLM(fac), FOURCC_NVLM));
BAIL_IF_ERR(err = NVFWriteNVLM(fac, wtr));
}
if (fac->getNumPartitions()) {
BAIL_IF_ERR(err = wtr.writeEncapsulationHeader(FOURCC_PARTITIONS, NVFSizePartitions(fac), FOURCC_PARTITIONS));
BAIL_IF_ERR(err = NVFWritePartitions(fac, wtr));
}
BAIL_IF_ERR(err = wtr.writeTocAndClose(NVFFileHeader::EOTOCOffset()));
bail:
return err;
}
/********************************************************************************
********************************************************************************
********************************************************************************
***** NVF Input *****
********************************************************************************
********************************************************************************
********************************************************************************/
static size_t SafeRead(void *ptr, size_t size, size_t num, FILE *fd) {
if (ptr) return(fread(ptr, size, num, fd));
else return fseek(fd, (long)(size * num), SEEK_CUR) ? 0 : num;
}
static FaceIOErr NVFReadShapeModel(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
uint32_t n, numModes;
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_MEAN:
n = ts.size / sizeof(*fac->getShapeMean());
BAIL_IF_FALSE(1 == SafeRead(fac->getShapeMean(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_BASIS:
BAIL_IF_FALSE((1 == fread(&numModes, sizeof(numModes), 1, fd)), numModes, 0); // The number of modes
ts.size -= sizeof(numModes); // The byte size of all modes
n = ts.size / sizeof(*fac->getShapeModes()); // The elements size of all nodes
BAIL_IF_FALSE(1 == SafeRead(fac->getShapeModes(n / numModes, numModes), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_EIGENVALUES:
n = ts.size / sizeof(*fac->getShapeEigenvalues());
BAIL_IF_FALSE(1 == SafeRead(fac->getShapeEigenvalues(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_TRIANGLE_LIST:
n = ts.size / sizeof(*fac->getTriangleList());
BAIL_IF_FALSE(1 == SafeRead(fac->getTriangleList(n), ts.size, 1, fd), err, kIOErrRead);
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadColorModel(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
uint32_t n, numModes;
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_MEAN:
n = ts.size / sizeof(*fac->getColorMean());
BAIL_IF_FALSE(1 == SafeRead(fac->getColorMean(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_BASIS:
BAIL_IF_FALSE((1 == fread(&numModes, sizeof(numModes), 1, fd)), numModes, 0); // The number of modes
ts.size -= sizeof(numModes); // The byte size of all modes
n = ts.size / sizeof(*fac->getColorModes()); // The elements size of all nodes
BAIL_IF_FALSE(1 == SafeRead(fac->getColorModes(n / numModes, numModes), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_EIGENVALUES:
n = ts.size / sizeof(*fac->getColorEigenvalues());
BAIL_IF_FALSE(1 == SafeRead(fac->getColorEigenvalues(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_TRIANGLE_LIST:
(void)fseek(fd, ts.size, SEEK_CUR); // Skip color triangle list -- it doesn't make sense
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadMorphableModel(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
uint32_t n;
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_SHAPE:
BAIL_IF_ERR(err = NVFReadShapeModel(fac, ts.size, fd));
break;
case FOURCC_COLOR:
BAIL_IF_ERR(err = NVFReadColorModel(fac, ts.size, fd));
break;
case FOURCC_TEXTURE_COORDS:
n = ts.size / sizeof(*fac->getTextureCoordinates());
BAIL_IF_FALSE(1 == SafeRead(fac->getTextureCoordinates(n), ts.size, 1, fd), err, kIOErrRead);
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadString(std::string &str, uint32_t size, FILE *fd) {
str.resize(size);
FaceIOErr err = (size == fread(&str[0], 1, size, fd)) ? kIOErrNone : kIOErrRead;
BAIL_IF_ERR(err);
for (; size; --size)
if (str[size - 1]) break;
str.resize(size); // remove pad
bail:
return err;
}
static FaceIOErr NVFReadBlendShapes(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
uint32_t numShapes;
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
std::string name;
float *shape;
BAIL_IF_FALSE(1 == fread(&numShapes, sizeof(numShapes), 1, fd), err, kIOErrEOF);
fac->setNumBlendShapes(numShapes);
for (uint32_t idxShape = 0, idxName = 0; size >= sizeof(ts);) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_NAME:
BAIL_IF_FALSE(idxName < numShapes, err, kIOErrRead);
BAIL_IF_ERR(err = NVFReadString(name, ts.size, fd));
fac->setBlendShapeName(idxName++, name.c_str());
break;
case FOURCC_SHAPE:
BAIL_IF_FALSE(idxShape < numShapes, err, kIOErrRead);
shape = fac->getBlendShape(idxShape++, ts.size / sizeof(*shape));
BAIL_IF_FALSE(1 == SafeRead(shape, ts.size, 1, fd), err, kIOErrRead);
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadIbugMappings(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
uint32_t n;
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_LANDMARK_MAP:
n = ts.size / sizeof(*fac->getIbugLandmarkMappings());
BAIL_IF_FALSE(1 == SafeRead(fac->getIbugLandmarkMappings(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_RIGHT_CONTOUR:
n = ts.size / sizeof(*fac->getIbugRightContour());
BAIL_IF_FALSE(1 == SafeRead(fac->getIbugRightContour(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_LEFT_CONTOUR:
n = ts.size / sizeof(*fac->getIbugLeftContour());
BAIL_IF_FALSE(1 == SafeRead(fac->getIbugLeftContour(n), ts.size, 1, fd), err, kIOErrRead);
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadModelContours(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
;
EOTypeSize ts;
uint32_t n;
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_RIGHT_CONTOUR:
n = ts.size / sizeof(*fac->getModelRightContour());
BAIL_IF_FALSE(1 == SafeRead(fac->getModelRightContour(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_LEFT_CONTOUR:
n = ts.size / sizeof(*fac->getModelLeftContour());
BAIL_IF_FALSE(1 == SafeRead(fac->getModelLeftContour(n), ts.size, 1, fd), err, kIOErrRead);
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadTopology(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
uint32_t n;
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_ADJACENT_FACES:
n = ts.size / sizeof(*fac->getAdjacentFaces());
BAIL_IF_FALSE(1 == SafeRead(fac->getAdjacentFaces(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_ADJACENT_VERTICES:
n = ts.size / sizeof(*fac->getAdjacentVertices());
BAIL_IF_FALSE(1 == SafeRead(fac->getAdjacentVertices(n), ts.size, 1, fd), err, kIOErrRead);
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadNvlm(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
uint32_t n;
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_LANDMARK_MAP:
n = ts.size / sizeof(*fac->getNvlmLandmarks());
BAIL_IF_FALSE(1 == SafeRead(fac->getNvlmLandmarks(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_RIGHT_CONTOUR:
n = ts.size / sizeof(*fac->getIbugRightContour());
BAIL_IF_FALSE(1 == SafeRead(fac->getNvlmRightContour(n), ts.size, 1, fd), err, kIOErrRead);
break;
case FOURCC_LEFT_CONTOUR:
n = ts.size / sizeof(*fac->getIbugLeftContour());
BAIL_IF_FALSE(1 == SafeRead(fac->getNvlmLeftContour(n), ts.size, 1, fd), err, kIOErrRead);
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
bail:
return err;
}
static FaceIOErr NVFReadPart(uint32_t i, FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
TPart part;
std::string name;
BAIL_IF_FALSE(sizeof(part) <= size, err, kIOErrEOF);
BAIL_IF_FALSE(1 == fread(&part, sizeof(part), 1, fd), err, kIOErrEOF);
size -= sizeof(part);
fac->setPartition(i, part.faceIndex, part.numFaces, part.vertexIndex, part.numVertices, part.smoothingGroup);
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_NAME:
BAIL_IF_ERR(err = NVFReadString(name, ts.size, fd));
fac->setPartitionName(i, name.c_str());
break;
case FOURCC_MATERIAL:
BAIL_IF_ERR(err = NVFReadString(name, ts.size, fd));
fac->setPartitionMaterialName(i, name.c_str());
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
BAIL_IF_NONZERO(size, err, kIOErrRead); // We are out-of-sync if size != 0
bail:
return err;
}
static FaceIOErr NVFReadPartitions(FaceIOAdapter *fac, uint32_t size, FILE *fd) {
FaceIOErr err = kIOErrNone;
EOTypeSize ts;
uint32_t numPartitions, partIx = 0;
BAIL_IF_FALSE(sizeof(numPartitions) <= size, err, kIOErrEOF);
BAIL_IF_FALSE(1 == fread(&numPartitions, sizeof(numPartitions), 1, fd), err, kIOErrEOF);
size -= sizeof(numPartitions);
fac->setNumPartitions(numPartitions);
for (; size >= sizeof(ts); ++partIx) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_PART:
BAIL_IF_ERR(err = NVFReadPart(partIx, fac, ts.size, fd));
break;
default:
BAIL_IF_NONZERO(fseek(fd, ts.size, SEEK_CUR), err, kIOErrEOF); // We skip over objects we don't understand
break;
}
}
BAIL_IF_NONZERO(size, err, kIOErrRead); // We are out-of-sync if size != 0
bail:
return err;
}
/********************************************************************************
* API ReadNVFFaceModel API *
********************************************************************************/
FaceIOErr ReadNVFFaceModel(const char *fileName, FaceIOAdapter *fac) {
FaceIOErr err = kIOErrNone;
FILE *fd = nullptr;
EOTypeSize ts;
NVFFileHeader header;
uint32_t size;
#ifndef _MSC_VER
fd = fopen(fileName, "rb");
#else /* _MSC_VER */
if (0 != fopen_s(&fd, fileName, "rb"))
fd = nullptr;
#endif /* _MSC_VER */
if (!fd) {
PrintIOError(fileName, err = kIOErrFileOpen);
goto bail;
}
/* Find the length of the file */
BAIL_IF_NEGATIVE(fseek(fd, 0L, SEEK_END), err, kIOErrRead);
size = ftell(fd);
BAIL_IF_NEGATIVE(fseek(fd, 0L, SEEK_SET), err, kIOErrRead);
BAIL_IF_FALSE(size >= sizeof(header), err, kIOErrEOF);
/* Validate header */
BAIL_IF_FALSE(1 == fread(&header, sizeof(header), 1, fd), err, kIOErrRead);
BAIL_IF_FALSE(FOURCC_FILE_TYPE == header.type, err, kIOErrFormat);
BAIL_IF_FALSE(8 <= header.size, err, kIOErrFormat); // TODO: robustify
BAIL_IF_FALSE(NVFFileHeader::LITTLE_ENDIAN_CODE == header.endian, err, kIOErrFormat); // We only handle little-endian
BAIL_IF_FALSE(32 == header.sizeBits, err, kIOErrFormat); // We only use 32 bit sizes
BAIL_IF_FALSE(16 == header.indexBits, err, kIOErrFormat); // We only use 16 bit indices
size -= sizeof(header);
if (header.size > 8) { /* Forward compatibility */
uint32_t extra = header.size - 8;
BAIL_IF_FALSE(size >= extra, err, kIOErrEOF);
size -= extra;
BAIL_IF_NEGATIVE(fseek(fd, extra, SEEK_CUR), err, kIOErrRead);
}
while (size >= sizeof(ts)) {
BAIL_IF_FALSE(1 == fread(&ts, sizeof(ts), 1, fd), err, kIOErrEOF);
size -= sizeof(ts);
BAIL_IF_FALSE(ts.size <= size, err, kIOErrEOF);
size -= ts.size;
switch (ts.type) {
case FOURCC_MODEL:
BAIL_IF_ERR(err = NVFReadMorphableModel(fac, ts.size, fd));
break;
case FOURCC_IBUG:
BAIL_IF_ERR(err = NVFReadIbugMappings(fac, ts.size, fd));
break;
case FOURCC_BLEND_SHAPES:
BAIL_IF_ERR(err = NVFReadBlendShapes(fac, ts.size, fd));
break;
case FOURCC_MODEL_CONTOUR:
BAIL_IF_ERR(err = NVFReadModelContours(fac, ts.size, fd));
break;
case FOURCC_TOPOLOGY:
BAIL_IF_ERR(err = NVFReadTopology(fac, ts.size, fd));
break;
case FOURCC_NVLM:
BAIL_IF_ERR(err = NVFReadNvlm(fac, ts.size, fd));
break;
case FOURCC_PARTITIONS:
BAIL_IF_ERR(err = NVFReadPartitions(fac, ts.size, fd));
break;
case FOURCC_EOTOC:
BAIL_IF_NEGATIVE(fseek(fd, ts.size, SEEK_CUR), err, kIOErrRead); // Skip the EOTOC
break;
default:
BAIL(err, kIOErrSyntax); // TODO: We should just skip over objects we don't understand
}
}
bail:
if (fd) fclose(fd);
return err;
}
/********************************************************************************
********************************************************************************
********************************************************************************
***** EOS Input *****
********************************************************************************
********************************************************************************
********************************************************************************/
#define DEBUG_PARSER 0
static FaceIOErr EOSReadModesSize(uint32_t *oneModeSize, uint32_t *numModes, FILE *fd) {
FaceIOErr err = kIOErrNone;
BAIL_IF_FALSE(1 == fread(oneModeSize, sizeof(*oneModeSize), 1, fd), err, kIOErrRead);
BAIL_IF_FALSE(1 == fread(numModes, sizeof(*numModes), 1, fd), err, kIOErrRead);
bail:
return err;
}
static FaceIOErr EOSReadShapeModel(FaceIOAdapter *fac, FILE *fd) {
FaceIOErr err = kIOErrNone;
uint32_t modeSize, numModes;
uint16_t *tri;
float *data;
long long z;
if (DEBUG_PARSER) printf("Reading Shape Model @ %ld (%#lx)\n", ftell(fd), ftell(fd));
if (DEBUG_PARSER) printf("Reading mean @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadModesSize(&modeSize, &numModes, fd)); // Coverity thinks "read" means "taint"
if (0 != (modeSize *= numModes)) {
BAIL_IF_NULL(data = fac->getShapeMean(modeSize), err, kIOErrNullPointer);
BAIL_IF_FALSE(1 == fread(data, modeSize * sizeof(*data), 1, fd), err, kIOErrRead);
}
if (DEBUG_PARSER) printf("Reading pca_basis @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadModesSize(&modeSize, &numModes, fd));
if (0 != (modeSize * numModes)) {
BAIL_IF_NULL(data = fac->getShapeModes(modeSize, numModes), err, kIOErrNullPointer);
BAIL_IF_FALSE(1 == fread(data, modeSize * numModes * sizeof(*data), 1, fd), err, kIOErrRead);
}
if (DEBUG_PARSER) printf("Reading eigenvalues @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadModesSize(&modeSize, &numModes, fd));
if (modeSize *= numModes) {
BAIL_IF_NULL(data = fac->getShapeEigenvalues(modeSize), err, kIOErrNullPointer);
BAIL_IF_FALSE(1 == fread(data, modeSize * sizeof(*data), 1, fd), err, kIOErrRead);
}
if (DEBUG_PARSER) printf("Reading triangle_list(%lld) @ %ld (%#lx)\n", z, ftell(fd), ftell(fd));
BAIL_IF_FALSE(1 == fread(&z, sizeof(z), 1, fd), err, kIOErrRead);
z *= 3;
tri = fac->getTriangleList((uint32_t)z * 2); // Big enough for uint32_t now, we resize to uint16_t later
BAIL_IF_FALSE(z == (long long)fread(tri, sizeof(uint32_t), z, fd), err, kIOErrRead);
CopyUInt32to16Vector((uint32_t *)tri, tri, (uint32_t)z);
fac->setTriangleListSize((uint32_t)z); // Now it is the actual size
bail:
if (err) printf("Error reading Shape Model\n");
return err;
}
static FaceIOErr EOSReadColorModel(FaceIOAdapter *fac, FILE *fd) {
FaceIOErr err = kIOErrNone;
uint32_t modeSize, numModes;
float *data;
long long z;
if (DEBUG_PARSER) printf("Reading Color Model @ %ld (%#lx)\n", ftell(fd), ftell(fd));
if (DEBUG_PARSER) printf("Reading mean @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadModesSize(&modeSize, &numModes, fd));
if (modeSize *= numModes) {
BAIL_IF_NULL(data = fac->getColorMean(modeSize), err, kIOErrNullPointer);
BAIL_IF_FALSE(1 == fread(data, modeSize * sizeof(*data), 1, fd), err, kIOErrRead);
}
if (DEBUG_PARSER) printf("Reading pca_basis @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadModesSize(&modeSize, &numModes, fd));
if (modeSize * numModes) {
BAIL_IF_NULL(data = fac->getColorModes(modeSize, numModes), err, kIOErrNullPointer);
BAIL_IF_FALSE(1 == fread(data, modeSize * numModes * sizeof(*data), 1, fd), err, kIOErrRead);
}
if (DEBUG_PARSER) printf("Reading eigenvalues @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadModesSize(&modeSize, &numModes, fd));
if (modeSize *= numModes) {
BAIL_IF_NULL(data = fac->getColorEigenvalues(modeSize), err, kIOErrNullPointer);
BAIL_IF_FALSE(1 == fread(data, modeSize * sizeof(*data), 1, fd), err, kIOErrRead);
}
if (DEBUG_PARSER) printf("Skipping triangle_list(%lld) @ %ld (%#lx)\n", z, ftell(fd), ftell(fd));
BAIL_IF_FALSE(1 == fread(&z, sizeof(z), 1, fd), err, kIOErrRead);
z *= 3 * sizeof(uint32_t);
(void)fseek(fd, (long)z, SEEK_CUR);
bail:
if (err) printf("Error reading Color Model\n");
return err;
}
static FaceIOErr EOSReadMorphableModel(FaceIOAdapter *fac, FILE *fd) {
FaceIOErr err = kIOErrNone;
int version;
size_t n1;
long long z;
float *tex;
if (DEBUG_PARSER) printf("Reading TmorphableModel @ %ld (%#lx)\n", ftell(fd), ftell(fd));
n1 = fread(&version, sizeof(version), 1, fd);
BAIL_IF_FALSE(n1 == 1, err, kIOErrRead);
if (DEBUG_PARSER) printf("Version(%d)\n", version);
if (DEBUG_PARSER) printf("Reading shape_model @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadShapeModel(fac, fd));
if (DEBUG_PARSER) printf("Reading color_model @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_ERR(err = EOSReadColorModel(fac, fd));
BAIL_IF_FALSE(1 == fread(&z, sizeof(z), 1, fd), err, kIOErrRead);
z *= 2; /* 2 floats per texture coordinate */
tex = fac->getTextureCoordinates((uint32_t)z * 2); /* allocate enough space for double precision */
if (DEBUG_PARSER) printf("Reading texture_coordinates(%lld) @ %ld (%#lx)\n", z, ftell(fd), ftell(fd));
BAIL_IF_FALSE(1 == fread(tex, sizeof(double) * z, 1, fd), err, kIOErrRead);
CopyDoubleToSingleVector((double *)tex, tex, (uint32_t)z);
fac->setTextureCoordinatesSize((uint32_t)z); /* resize for single precision */
bail:
if (err) printf("Error reading TmorphableModel\n");
return err;
}
static FaceIOErr EOSReadString(std::string &str, FILE *fd) {
FaceIOErr err = kIOErrNone;
long long z;
if (DEBUG_PARSER) printf("Reading string @ %ld (%#lx)\n", ftell(fd), ftell(fd));
BAIL_IF_FALSE(1 == fread(&z, sizeof(z), 1, fd), err, kIOErrRead);
str.resize(z);
BAIL_IF_FALSE(z == (long long)fread(&str[0], sizeof(str[0]), z, fd), err, kIOErrRead);
bail:
if (err) printf("Error reading EOS string\n");
return err;
}
static FaceIOErr EOSReadBlendShapes(FaceIOAdapter *fac, FILE *fd) {
FaceIOErr err = kIOErrNone;
size_t numShapes;
uint32_t i, modeSize, numModes;
float *modes;
long long z;
std::string name;
BAIL_IF_FALSE(1 == fread(&z, sizeof(z), 1, fd), err, kIOErrRead);
numShapes = z;
fac->setNumBlendShapes((uint32_t)numShapes);
for (i = 0; i < (uint32_t)numShapes; ++i) {
BAIL_IF_ERR(err = EOSReadString(name, fd));
fac->setBlendShapeName(i, name.c_str());
BAIL_IF_ERR(err = EOSReadModesSize(&modeSize, &numModes, fd));
modes = fac->getBlendShape(i, modeSize *= numModes);
BAIL_IF_FALSE(1 == fread(modes, modeSize * sizeof(*modes), 1, fd), err, kIOErrRead);
}
bail:
if (err) printf("Error reading TblendShapes\n");
return err;
}
struct EOSContoursReaderState {
enum {
STATE_NULL,
STATE_MODEL_CONTOUR,
STATE_RIGHT_CONTOUR,
STATE_LEFT_CONTOUR,
STATE_RIGHT_CONTOUR_ARRAY,
STATE_LEFT_CONTOUR_ARRAY,
STATE_ERROR
};
int state, nest;
FaceIOAdapter *fac;
EOSContoursReaderState() {
state = STATE_NULL;
nest = 0;
fac = nullptr;
};
};
static FaceIOErr EOSContoursOpenNode(JSONInfo *info) {
EOSContoursReaderState *st = (EOSContoursReaderState *)(info->userData);
switch (info->type) {
case kJSONObject:
++(st->nest);
break;
case kJSONArray:
++(st->nest);
switch (st->state) {
case EOSContoursReaderState::STATE_RIGHT_CONTOUR:
st->state = EOSContoursReaderState::STATE_RIGHT_CONTOUR_ARRAY;
break;
case EOSContoursReaderState::STATE_LEFT_CONTOUR:
st->state = EOSContoursReaderState::STATE_LEFT_CONTOUR_ARRAY;
break;
default:
st->state = EOSContoursReaderState::STATE_ERROR;
break;
}
break;
case kJSONNumber:
switch (st->state) {
case EOSContoursReaderState::STATE_RIGHT_CONTOUR_ARRAY:
st->fac->appendModelRightContour((uint16_t)info->number);
break;
case EOSContoursReaderState::STATE_LEFT_CONTOUR_ARRAY:
st->fac->appendModelLeftContour((uint16_t)info->number);
break;
default:
st->state = EOSContoursReaderState::STATE_ERROR;
break;
}
break;
case kJSONMember:
if (!strcmp(info->value, "model_contour"))
st->state = EOSContoursReaderState::STATE_MODEL_CONTOUR;
else if (!strcmp(info->value, "right_contour"))
st->state = EOSContoursReaderState::STATE_RIGHT_CONTOUR;
else if (!strcmp(info->value, "left_contour"))
st->state = EOSContoursReaderState::STATE_LEFT_CONTOUR;
else
st->state = EOSContoursReaderState::STATE_ERROR;
break;
}
return (EOSContoursReaderState::STATE_ERROR == st->state) ? kIOErrSyntax : kIOErrNone;
}
static FaceIOErr EOSContoursCloseNode(JSONInfo *info) {
EOSContoursReaderState *st = (EOSContoursReaderState *)(info->userData);
switch (info->type) {
case kJSONObject:
--(st->nest);
break;
case kJSONArray:
--(st->nest);
st->state = EOSContoursReaderState::STATE_NULL;
break;
}
return kIOErrNone;
}
static FaceIOErr EOSReadContours(FaceIOAdapter *fac, const char *fileName) {
JSONReader rdr(&EOSContoursOpenNode, &EOSContoursCloseNode);
EOSContoursReaderState st;
FaceIOErr err;
fac->setModelRightContourSize(0);
fac->setModelLeftContourSize(0);
st.fac = fac;
err = rdr.parse(fileName, &st);
return err;
}
struct EOSTopologyReaderState {
enum {
STATE_NULL,
STATE_EDGES,
STATE_FACES,
STATE_VERTICES,
STATE_FACES_ARRAY,
STATE_VERTICES_ARRAY,
STATE_FACES_ARRAY_OBJ,
STATE_VERTICES_ARRAY_OBJ,
STATE_ERROR
};
int state, nest;
FaceIOAdapter *fac;
EOSTopologyReaderState() {
state = STATE_NULL;
nest = 0;
fac = nullptr;
};
};
static FaceIOErr EOSTopologyOpenNode(JSONInfo *info) {
EOSTopologyReaderState *st = (EOSTopologyReaderState *)(info->userData);
switch (info->type) {
case kJSONObject:
++(st->nest);
switch (st->state) {
case EOSTopologyReaderState::STATE_FACES_ARRAY:
st->state = EOSTopologyReaderState::STATE_FACES_ARRAY_OBJ;
break;
case EOSTopologyReaderState::STATE_VERTICES_ARRAY:
st->state = EOSTopologyReaderState::STATE_VERTICES_ARRAY_OBJ;
break;
default:
break;
}
break;
case kJSONArray:
++(st->nest);
switch (st->state) {
case EOSTopologyReaderState::STATE_FACES:
st->state = EOSTopologyReaderState::STATE_FACES_ARRAY;
break;
case EOSTopologyReaderState::STATE_VERTICES:
st->state = EOSTopologyReaderState::STATE_VERTICES_ARRAY;
break;
default:
st->state = EOSTopologyReaderState::STATE_ERROR;
break;
}
break;
case kJSONNumber:
switch (st->state) {
case EOSTopologyReaderState::STATE_FACES_ARRAY:
case EOSTopologyReaderState::STATE_FACES_ARRAY_OBJ:
st->fac->appendAdjacentFace((uint16_t)info->number);
break;
case EOSTopologyReaderState::STATE_VERTICES_ARRAY:
case EOSTopologyReaderState::STATE_VERTICES_ARRAY_OBJ:
st->fac->appendAdjacentVertex((uint16_t)info->number);
break;
default:
st->state = EOSTopologyReaderState::STATE_ERROR;
break;
}
break;
case kJSONMember:
if (!strcmp(info->value, "edge_topology"))
st->state = EOSTopologyReaderState::STATE_EDGES;
else if (!strcmp(info->value, "adjacent_faces"))
st->state = EOSTopologyReaderState::STATE_FACES;
else if (!strcmp(info->value, "adjacent_vertices"))
st->state = EOSTopologyReaderState::STATE_VERTICES;
else if ((!strcmp(info->value, "value0") || !strcmp(info->value, "value1")) &&
(EOSTopologyReaderState::STATE_FACES_ARRAY_OBJ == st->state ||
EOSTopologyReaderState::STATE_VERTICES_ARRAY_OBJ == st->state)) {
} else
st->state = EOSTopologyReaderState::STATE_ERROR;
break;
}
return (EOSTopologyReaderState::STATE_ERROR == st->state) ? kIOErrSyntax : kIOErrNone;
}
static FaceIOErr EOSTopologyCloseNode(JSONInfo *info) {
EOSTopologyReaderState *st = (EOSTopologyReaderState *)(info->userData);
switch (info->type) {
case kJSONObject:
--(st->nest);
switch (st->state) {
case EOSTopologyReaderState::STATE_FACES_ARRAY_OBJ:
st->state = EOSTopologyReaderState::STATE_FACES_ARRAY;
break;
case EOSTopologyReaderState::STATE_VERTICES_ARRAY_OBJ:
st->state = EOSTopologyReaderState::STATE_VERTICES_ARRAY;
break;
default:
st->state = EOSTopologyReaderState::STATE_ERROR;
break;
}
break;
case kJSONArray:
--(st->nest);
st->state = EOSTopologyReaderState::STATE_NULL;
break;
}
return kIOErrNone;
}
static FaceIOErr EOSReadTopology(FaceIOAdapter *fac, const char *fileName) {
JSONReader rdr(&EOSTopologyOpenNode, &EOSTopologyCloseNode);
EOSTopologyReaderState st;
FaceIOErr err;
fac->setAdjacentFacesSize(0);
fac->setAdjacentVerticesSize(0);
st.fac = fac;
err = rdr.parse(fileName, &st);
return err;
}
/********************************************************************************
* API ReadEOSFaceModel API *
********************************************************************************/
FaceIOErr ReadEOSFaceModel(const char *shape, unsigned int /*ibugNumLandmarks*/, const char *blendShapes, const char *contours,
const char *topology, FaceIOAdapter *fac) {
FaceIOErr err = kIOErrNone;
FILE *fd;
if (shape) /* Shape */
{
if (!HasSuffix(shape, ".bin")) {
PrintUnknownFormatMessage(shape, "for shape");
BAIL(err, kIOErrFormat);
}
#ifndef _MSC_VER
fd = fopen(shape, "rb");
#else /* _MSC_VER */
if (0 != fopen_s(&fd, shape, "rb"))
fd = nullptr;
#endif /* _MSC_VER */
if (!fd) {
PrintIOError(shape, err = kIOErrFileOpen);
goto bail;
}
err = EOSReadMorphableModel(fac, fd);
fclose(fd);
BAIL_IF_ERR(err);
}
if (0) /* IBUG */
{
/* Insert ibug mappings reading code here */
} else {
unsigned n = sizeof(ibugMapping.landmarkMap) / sizeof(ibugMapping.landmarkMap[0][0]);
memcpy(fac->getIbugLandmarkMappings(n), &ibugMapping.landmarkMap[0][0],
n * sizeof(*fac->getIbugLandmarkMappings()));
n = sizeof(ibugMapping.rightContour) / sizeof(ibugMapping.rightContour[0]);
memcpy(fac->getIbugRightContour(n), ibugMapping.rightContour, n * sizeof(*fac->getIbugRightContour()));
n = sizeof(ibugMapping.leftContour) / sizeof(ibugMapping.leftContour[0]);
memcpy(fac->getIbugLeftContour(n), ibugMapping.leftContour, n * sizeof(*fac->getIbugLeftContour()));
}
if (blendShapes) /* Blend Shapes */
{
if (!HasSuffix(blendShapes, ".bin")) {
PrintUnknownFormatMessage(blendShapes, "for blend shapes");
BAIL(err, kIOErrFormat);
}
#ifndef _MSC_VER
fd = fopen(blendShapes, "rb");
#else /* _MSC_VER */
if (0 != fopen_s(&fd, blendShapes, "rb"))
fd = nullptr;
#endif /* _MSC_VER */
if (!fd) {
PrintIOError(blendShapes, err = kIOErrFileOpen);
goto bail;
}
err = EOSReadBlendShapes(fac, fd);
fclose(fd);
BAIL_IF_ERR(err);
}
if (contours) /* Contours */
{
if (!HasSuffix(contours, ".json")) {
PrintUnknownFormatMessage(contours, "for contours");
BAIL(err, kIOErrFormat);
}
err = EOSReadContours(fac, contours);
if (kIOErrNone != err) {
PrintIOError(contours, err);
goto bail;
}
}
if (topology) /* Topology */
{
if (!HasSuffix(topology, ".json")) {
PrintUnknownFormatMessage(topology, "for topology");
BAIL(err, kIOErrFormat);
}
err = EOSReadTopology(fac, topology);
if (kIOErrNone != err) {
PrintIOError(topology, err);
goto bail;
}
}
bail:
return err;
}
/********************************************************************************
********************************************************************************
********************************************************************************
***** JSON Output *****
********************************************************************************
********************************************************************************
********************************************************************************/
/* The maximum number of elements to print in one row when printing an array */
#define MAX_ROW_SIZE 12
static void JSONPrintModes(uint32_t size, uint32_t numModes, const float *data, JSONWriter &wtr, const char *tag) {
uint32_t modeSize = size / numModes;
uint32_t maxCols = (modeSize < MAX_ROW_SIZE) ? modeSize : MAX_ROW_SIZE;
wtr.openObject(tag);
if (modeSize > 1) wtr.writeNumber(modeSize, "mode_size");
if (numModes > 1) wtr.writeNumber(numModes, "num_modes");
wtr.writeNumericArray(size, data, maxCols, "data");
wtr.closeObject();
}
static void JSONPrintShapeModel(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
wtr.openObject(tag);
JSONPrintModes(fac->getShapeMeanSize(), 1, fac->getShapeMean(), wtr, "mean"); // This is really a vector
JSONPrintModes(fac->getShapeModesSize(), fac->getShapeNumModes(), fac->getShapeModes(), wtr, "pca_basis");
JSONPrintModes(fac->getShapeEigenvaluesSize(), fac->getShapeEigenvaluesSize(), fac->getShapeEigenvalues(), wtr,
"eigenvalues");
wtr.writeNumericArray(fac->getTriangleListSize(), fac->getTriangleList(), 3, "triangles");
wtr.closeObject();
}
static void JSONPrintColorModel(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
wtr.openObject(tag);
JSONPrintModes(fac->getColorMeanSize(), 1, fac->getColorMean(), wtr, "mean"); // This is really a vector
JSONPrintModes(fac->getColorModesSize(), fac->getColorNumModes(), fac->getColorModes(), wtr, "pca_basis");
JSONPrintModes(fac->getColorEigenvaluesSize(), fac->getColorEigenvaluesSize(), fac->getColorEigenvalues(), wtr,
"eigenvalues");
wtr.writeNumericArray(fac->getTriangleListSize(), fac->getTriangleList(), 3, "triangles");
wtr.closeObject();
}
static void JSONPrintMorphableModel(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
wtr.openObject(tag);
JSONPrintShapeModel(fac, wtr, "shape_model");
if (fac->getColorMeanSize() + fac->getColorModesSize() + fac->getColorEigenvaluesSize() != 0)
JSONPrintColorModel(fac, wtr, "color_model"); // Don't print color unless there is something there
if (fac->getTextureCoordinatesSize())
wtr.writeNumericArray(fac->getTextureCoordinatesSize(), fac->getTextureCoordinates(), 2, "texture_coordinates");
wtr.closeObject();
}
static void JSONPrintBlendShapes(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
uint32_t i, n;
wtr.openArray(tag);
for (i = 0, n = fac->getNumBlendShapes(); i < n; ++i) {
wtr.openObject(nullptr);
wtr.writeString(fac->getBlendShapeName(i), "name");
JSONPrintModes(fac->getBlendShapeSize(i), 1, fac->getBlendShape(i), wtr, "blend_shape"); // This is really a vector
wtr.closeObject();
}
wtr.closeArray();
}
static void JSONPrintIbugMappings(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
uint32_t n;
wtr.openObject(tag);
wtr.writeNumericArray(fac->getIbugLandmarkMappingsSize(), fac->getIbugLandmarkMappings(), 2, "landmark_mappings");
n = (fac->getIbugRightContourSize());
wtr.writeNumericArray(n, fac->getIbugRightContour(), n, "right_contour"); // straight across
n = fac->getIbugLeftContourSize();
wtr.writeNumericArray(n, fac->getIbugLeftContour(), n, "left_contour"); // straight across
wtr.closeObject();
}
static FaceIOErr JSONPrintContours(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
wtr.openObject(tag);
wtr.writeNumericArray(fac->getModelRightContourSize(), fac->getModelRightContour(),
fac->getModelRightContourSize(), "right_contour");
wtr.writeNumericArray(fac->getModelLeftContourSize(), fac->getModelLeftContour(),
fac->getModelLeftContourSize(), "left_contour");
wtr.closeObject();
return kIOErrNone;
}
static FaceIOErr JSONPrintTopology(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
if (!tag) tag = "edge_topology";
wtr.openObject(tag);
wtr.writeNumericArray(fac->getAdjacentFacesSize(), fac->getAdjacentFaces(), 2, "adjacent_faces");
wtr.writeNumericArray(fac->getAdjacentVerticesSize(), fac->getAdjacentVertices(), 2, "adjacent_vertices");
wtr.closeObject();
return kIOErrNone;
}
static void JSONPrintNvlm(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
uint32_t n;
wtr.openObject(tag);
wtr.writeNumericArray(fac->getNvlmLandmarksSize(), fac->getNvlmLandmarks(), 2, "landmark_mapping");
n = (fac->getNvlmRightContourSize());
wtr.writeNumericArray(n, fac->getNvlmRightContour(), n, "right_contour"); // straight across
n = fac->getNvlmLeftContourSize();
wtr.writeNumericArray(n, fac->getNvlmLeftContour(), n, "left_contour"); // straight across
wtr.closeObject();
}
static void JSONPrintPartitions(const FaceIOAdapter *fac, JSONWriter &wtr, const char *tag) {
uint32_t i, n;
wtr.openArray(tag);
for (i = 0, n = fac->getNumPartitions(); i < n; ++i) {
uint32_t partitionIndex, faceIndex, numFaces, vertexIndex, numVertices;
int32_t smoothingGroup;
const char *str;
wtr.openObject(nullptr);
partitionIndex = fac->getPartition(i, &faceIndex, &numFaces, &vertexIndex, &numVertices, &smoothingGroup);
wtr.writeNumber(partitionIndex, "partition_index");
wtr.writeNumber(faceIndex, "face_index");
wtr.writeNumber(numFaces, "num_faces");
wtr.writeNumber(vertexIndex, "vertex_index");
wtr.writeNumber(numVertices, "num_vertices");
wtr.writeNumber(smoothingGroup, "smoothing_group");
if (nullptr != (str = fac->getPartitionName(i)) && str[0])
wtr.writeString(str, "name");
if (nullptr != (str = fac->getPartitionMaterialName(i)) && str[0])
wtr.writeString(str, "material");
wtr.closeObject();
}
wtr.closeArray();
}
/********************************************************************************
* API PrintJSONFaceModel API *
********************************************************************************/
FaceIOErr PrintJSONFaceModel(FaceIOAdapter *fac, const char *file) {
FaceIOErr err;
JSONWriter wtr;
err = wtr.open(file);
if (kIOErrNone != err) {
PrintIOError(file, err);
return err;
}
wtr.openObject();
JSONPrintMorphableModel(fac, wtr, "morphable_model");
JSONPrintIbugMappings(fac, wtr, "ibug_mappings");
JSONPrintBlendShapes(fac, wtr, "blend_shapes");
JSONPrintContours(fac, wtr, "contours");
JSONPrintTopology(fac, wtr, "edge_topology");
if (fac->getNvlmLandmarksSize())
JSONPrintNvlm(fac, wtr, "nvidia_mappings");
if (fac->getNumPartitions())
JSONPrintPartitions(fac, wtr, "partitions");
wtr.closeObject();
return err;
}