/*############################################################################### # # 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 #include #include #include #include #include #include #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 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 count; void doIndent(); void maybeComma(); void maybeTag(const char *tag); void commaIndentTag(const char *tag); template 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 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 uint32_t NVFSize(uint32_t n, const ElementType *v) { return (uint32_t)(n * sizeof(*v)); } template 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; }