int tiff_finalize(FAR struct tiff_info_s *info) { struct tiff_ifdentry_s ifdentry; FAR uint8_t *ptr; size_t maxoffsets; #ifdef CONFIG_DEBUG_GRAPHICS size_t total; #endif off_t offset; int ret; int i; int j; /* Put all of the pieces together to create the final output file. There * are three pieces: * * 1) outfile: The partial output file containing the header, IFD and strip * counts. This includes the StripOffsets and StripByteCounts that need * to be updated. Size=outsize; * 2) tmpfile1: This contains the offsets into tmpfile3 for each strip. The * size of this file is tmp1size. These offsets are relative to the * beginning of tmpfile3 and need to be offset by outsize+tmp1size. * 3) tmpfile3: The strip data. Size is tmp2size. This is raw image data; * no fixups are required. */ DEBUGASSERT(info && info->outfd >= 0 && info->tmp1fd >= 0 && info->tmp2fd >= 0); DEBUGASSERT((info->outsize & 3) == 0 && (info->tmp1size & 3) == 0); /* Fix-up the count value in the StripByteCounts IFD entry in the outfile. * The actual number of strips was unknown at the time that the IFD entry * was written. */ ret = tiff_readifdentry(info->outfd, info->filefmt->sbcifdoffset, &ifdentry); if (ret < 0) { goto errout; } tiff_put32(ifdentry.count, info->nstrips); ret = tiff_writeifdentry(info->outfd, info->filefmt->sbcifdoffset, &ifdentry); if (ret < 0) { goto errout; } /* Fix-up the count and offset values in the StripOffsets IFD entry in the * outfile. The StripOffsets data will be stored immediately after the * outfile, hence, the correct offset is outsize. */ ret = tiff_readifdentry(info->outfd, info->filefmt->soifdoffset, &ifdentry); if (ret < 0) { goto errout; } tiff_put32(ifdentry.count, info->nstrips); tiff_put32(ifdentry.offset, info->outsize); ret = tiff_writeifdentry(info->outfd, info->filefmt->soifdoffset, &ifdentry); if (ret < 0) { goto errout; } /* Rewind to the beginning of tmpfile1 */ offset = lseek(info->tmp1fd, 0, SEEK_SET); if (offset == (off_t)-1) { ret = -errno; goto errout; } /* Seek to the end of the outfile */ ret = lseek(info->outfd, 0, SEEK_END); if (offset == (off_t)-1) { ret = -errno; goto errout; } /* Now read strip offset data from tmpfile1, update the offsets, and write * the updated offsets to the outfile. The strip data will begin at offset * outsize + tmp1size; */ maxoffsets = info->iosize >> 2; #ifdef CONFIG_DEBUG_GRAPHICS total = 0; #endif for (i = 0; i < info->nstrips; ) { size_t noffsets; ssize_t nbytes; /* Read a group of up to 32-bit values */ noffsets = info->nstrips - i; if (noffsets > maxoffsets) { noffsets = maxoffsets; } nbytes = tiff_read(info->tmp1fd, info->iobuffer, noffsets << 2); /* If an error occurs or we fail to read exactly this number of * bytes, then something bad happened. */ if (nbytes != noffsets << 2) { goto errout; } /* Fix up the offsets */ for (j = 0, ptr = info->iobuffer; j < noffsets; j++, ptr += 4) { uint32_t stripoff = tiff_get32(ptr); stripoff += (info->outsize + info->tmp1size); tiff_put32(ptr, stripoff); } /* Then write the corrected offsets to the outfile */ ret = tiff_write(info->outfd, info->iobuffer, nbytes); if (ret < 0) { goto errout; } /* Update the count of offsets written */ i += noffsets; #ifdef CONFIG_DEBUG_GRAPHICS total += nbytes; #endif } #ifdef CONFIG_DEBUG_GRAPHICS ASSERT(total == info->tmp1size); #endif /* Rewind to the beginning of tmpfile2 */ offset = lseek(info->tmp2fd, 0, SEEK_SET); if (offset == (off_t)-1) { ret = -errno; goto errout; } /* Finally, copy the tmpfile2 to the end of the outfile */ #ifdef CONFIG_DEBUG_GRAPHICS total = 0; #endif for (;;) { ssize_t nbytes; /* Read a block of data from tmpfile2 */ nbytes = tiff_read(info->tmp2fd, info->iobuffer, info->iosize); /* Check for tead errors and for end-of-file */ if (nbytes < 0) { ret = (int)nbytes; goto errout; } else if (nbytes == 0) { break; } /* Then copy the data to the outfile */ ret = tiff_write(info->outfd, info->iobuffer, nbytes); if (ret < 0) { goto errout; } #ifdef CONFIG_DEBUG_GRAPHICS total += nbytes; #endif } #ifdef CONFIG_DEBUG_GRAPHICS ASSERT(total == info->tmp2size); #endif /* Close all files and return success */ tiff_cleanup(info); return OK; errout: tiff_abort(info); return ret; }
int tiff_initialize(FAR struct tiff_info_s *info) { uint16_t val16; #if CONFIG_DEBUG_TIFFOFFSETS off_t offset = 0; #endif char timbuf[TIFF_DATETIME_STRLEN + 8]; int ret = -EINVAL; DEBUGASSERT(info && info->outfile && info->tmpfile1 && info->tmpfile2); /* Open all output files */ info->outfd = open(info->outfile, O_RDWR|O_CREAT|O_TRUNC, 0666); if (info->outfd < 0) { gdbg("Failed to open %s for reading/writing: %d\n", info->outfile, errno); goto errout; } info->tmp1fd = open(info->tmpfile1, O_RDWR|O_CREAT|O_TRUNC, 0666); if (info->tmp1fd < 0) { gdbg("Failed to open %s for reading/writing: %d\n", info->tmpfile1, errno); goto errout; } info->tmp2fd = open(info->tmpfile1, O_RDWR|O_CREAT|O_TRUNC, 0666); if (info->tmp2fd < 0) { gdbg("Failed to open %s for reading/writing: %d\n", info->tmpfile1, errno); goto errout; } /* Make some decisions using the color format. Only the following are * supported: */ info->pps = info->imgwidth * info->rps; /* Pixels per strip */ switch (info->colorfmt) { case FB_FMT_Y1: /* BPP=1, monochrome, 0=black */ info->filefmt = &g_bilevinfo; /* Bi-level file image file info */ info->imgflags = IMGFLAGS_FMT_Y1; /* Bit encoded image characteristics */ info->bps = (info->pps + 7) >> 3; /* Bytes per strip */ break; case FB_FMT_Y4: /* BPP=4, 4-bit greyscale, 0=black */ info->filefmt = &g_greyinfo; /* Greyscale file image file info */ info->imgflags = IMGFLAGS_FMT_Y4; /* Bit encoded image characteristics */ info->bps = (info->pps + 1) >> 1; /* Bytes per strip */ break; case FB_FMT_Y8: /* BPP=8, 8-bit greyscale, 0=black */ info->filefmt = &g_greyinfo; /* Greyscale file image file info */ info->imgflags = IMGFLAGS_FMT_Y8; /* Bit encoded image characteristics */ info->bps = info->pps; /* Bytes per strip */ break; case FB_FMT_RGB16_565: /* BPP=16 R=6, G=6, B=5 */ info->filefmt = &g_rgbinfo; /* RGB file image file info */ info->imgflags = IMGFLAGS_FMT_RGB16_565; /* Bit encoded image characteristics */ info->bps = 3 * info->pps; /* Bytes per strip */ break; case FB_FMT_RGB24: /* BPP=24 R=8, G=8, B=8 */ info->filefmt = &g_rgbinfo; /* RGB file image file info */ info->imgflags = IMGFLAGS_FMT_RGB24; /* Bit encoded image characteristics */ info->bps = 3 *info->pps; /* Bytes per strip */ break; default: gdbg("Unsupported color format: %d\n", info->colorfmt); return -EINVAL; } /* Write the TIFF header data to the outfile: * * Header: 0 Byte Order "II" or "MM" * 2 Magic Number 42 * 4 1st IFD offset 10 * 8 [2 bytes padding] */ ret = tiff_putheader(info); if (ret < 0) { goto errout; } tiff_offset(offset, TIFF_IFD_OFFSET); /* Write the Number of directory entries * * All formats: Offset 10 Number of Directory Entries 12 */ ret = tiff_putint16(info->outfd, info->filefmt->nifdentries); if (ret < 0) { goto errout; } tiff_offset(offset, 2); /* Write the NewSubfileType IFD entry * * All formats: Offset 12 NewSubfileType */ ret = tiff_putifdentry16(info, IFD_TAG_NEWSUBFILETYPE, IFD_FIELD_LONG, 1, 0); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write ImageWidth and ImageLength * * All formats: Offset 24 ImageWidth Number of columns is a user parameter * 36 ImageLength Number of rows is a user parameter */ ret = tiff_putifdentry16(info, IFD_TAG_IMAGEWIDTH, IFD_FIELD_SHORT, 1, info->imgwidth); if (ret == OK) { ret= tiff_putifdentry16(info, IFD_TAG_IMAGELENGTH, IFD_FIELD_SHORT, 1, info->imgheight); } if (ret < 0) { goto errout; } tiff_offset(offset, 2*SIZEOF_IFD_ENTRY); /* Write BitsPerSample * * Bi-level Images: None * Greyscale: Offset 48 BitsPerSample (4 or 8) * RGB: Offset 48 BitsPerSample (8,8,8) */ tiff_checkoffs(offset, 48); if (IMGFLAGS_ISGREY(info->imgflags)) { if (IMGFLAGS_ISGREY8(info->imgflags)) { val16 = 8; } else { val16 = 4; } ret = tiff_putifdentry16(info, IFD_TAG_BITSPERSAMPLE, IFD_FIELD_SHORT, 1, val16); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); } else if (IMGFLAGS_ISRGB(info->imgflags)) { ret = tiff_putifdentry(info, IFD_TAG_BITSPERSAMPLE, IFD_FIELD_SHORT, 3, TIFF_RGB_BPSOFFSET); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); } /* Write Compression: * * Bi-level Images: Offset 48 Hard-coded no compression (for now) * Greyscale: Offset 60 " " " " "" " " " " " " * RGB: Offset 60 " " " " "" " " " " " " */ ret = tiff_putifdentry16(info, IFD_TAG_COMPRESSION, IFD_FIELD_SHORT, 1, TAG_COMP_NONE); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write PhotometricInterpretation: * * Bi-level Images: Offset 48 Hard-coded BlackIsZero * Greyscale: Offset 72 Hard-coded BlackIsZero * RGB: Offset 72 Hard-coded RGB */ if (IMGFLAGS_ISRGB(info->imgflags)) { val16 = TAG_PMI_RGB; } else { val16 = TAG_PMI_BLACK; } ret = tiff_putifdentry16(info, IFD_TAG_PMI, IFD_FIELD_SHORT, 1, val16); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write StripOffsets: * * Bi-level Images: Offset 72 Value determined by switch statement above * Greyscale: Offset 84 Value determined by switch statement above * RGB: Offset 84 Value determined by switch statement above */ tiff_checkoffs(offset, info->filefmt->soifdoffset); ret = tiff_putifdentry(info, IFD_TAG_STRIPOFFSETS, IFD_FIELD_LONG, 0, 0); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write SamplesPerPixel * * Bi-level Images: N/A * Greyscale: N/A * RGB: Offset 96 Hard-coded to 3 */ if (IMGFLAGS_ISRGB(info->imgflags)) { ret = tiff_putifdentry16(info, IFD_TAG_SAMPLESPERPIXEL, IFD_FIELD_SHORT, 1, 3); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); } /* Write RowsPerStrip: * * Bi-level Images: Offset 84 Value is a user parameter * Greyscale: Offset 96 Value is a user parameter * RGB: Offset 108 Value is a user parameter */ ret = tiff_putifdentry16(info, IFD_TAG_ROWSPERSTRIP, IFD_FIELD_SHORT, 1, info->rps); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write StripByteCounts: * * Bi-level Images: Offset 96 Count determined as strips added, Value offset = 216 * Greyscale: Offset 108 Count determined as strips added, Value offset = 228 * RGB: Offset 120 Count determined as strips added, Value offset = 248 */ tiff_checkoffs(offset, info->filefmt->sbcifdoffset); ret = tiff_putifdentry(info, IFD_TAG_STRIPCOUNTS, IFD_FIELD_LONG, 0, info->filefmt->sbcoffset); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write XResolution and YResolution: * * Bi-level Images: Offset 108 and 120, Values are a user parameters * Greyscale: Offset 120 and 132, Values are a user parameters * RGB: Offset 132 and 144, Values are a user parameters */ ret = tiff_putifdentry(info, IFD_TAG_XRESOLUTION, IFD_FIELD_RATIONAL, 1, info->filefmt->xresoffset); if (ret == OK) { ret = tiff_putifdentry(info, IFD_TAG_YRESOLUTION, IFD_FIELD_RATIONAL, 1, info->filefmt->yresoffset); } if (ret < 0) { goto errout; } tiff_offset(offset, 2*SIZEOF_IFD_ENTRY); /* Write ResolutionUnit: * * Bi-level Images: Offset 132, Hard-coded to "inches" * Greyscale: Offset 144, Hard-coded to "inches" * RGB: Offset 156, Hard-coded to "inches" */ ret = tiff_putifdentry16(info, IFD_TAG_RESUNIT, IFD_FIELD_SHORT, 1, TAG_RESUNIT_INCH); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write Software: * * Bi-level Images: Offset 144 Count, Hard-coded "NuttX" * Greyscale: Offset 156 Count, Hard-coded "NuttX" * RGB: Offset 168 Count, Hard-coded "NuttX" */ ret = tiff_putifdentry(info, IFD_TAG_SOFTWARE, IFD_FIELD_ASCII, TIFF_SOFTWARE_STRLEN, info->filefmt->swoffset); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write DateTime: * * Bi-level Images: Offset 156 Count, Format "YYYY:MM:DD HH:MM:SS" * Greyscale: Offset 168 Count, Format "YYYY:MM:DD HH:MM:SS" * RGB: Offset 180 Count, Format "YYYY:MM:DD HH:MM:SS" */ ret = tiff_putifdentry(info, IFD_TAG_DATETIME, IFD_FIELD_ASCII, TIFF_DATETIME_STRLEN, info->filefmt->dateoffset); if (ret < 0) { goto errout; } tiff_offset(offset, SIZEOF_IFD_ENTRY); /* Write Next IFD Offset and 2 bytes of padding: * * Bi-level Images: Offset 168, Next IFD offset * Offset 170, [2 bytes padding] * Greyscale: Offset 180, Next IFD offset * Offset 182, [2 bytes padding] * RGB: Offset 192, Next IFD offset * Offset 194, [2 bytes padding] */ ret = tiff_putint32(info->outfd, 0); if (ret < 0) { goto errout; } tiff_offset(offset, 4); /* Now we begin the value section of the file */ tiff_checkoffs(offset, info->filefmt->valoffset); /* Write the XResolution and YResolution data: * * Bi-level Images: Offset 172 Count, Hard-coded to 300/1 * Offset 180 Count, Hard-coded to 300/1 * Greyscale: Offset 184 Count, Hard-coded to 300/1 * Offset 192 Count, Hard-coded to 300/1 * RGB: Offset 196 Count, Hard-coded to 300/1 * Offset 204 Count, Hard-coded to 300/1 */ tiff_checkoffs(offset, info->filefmt->xresoffset); ret = tiff_putint32(info->outfd, 300); if (ret == OK) { ret = tiff_putint32(info->outfd, 1); } if (ret < 0) { goto errout; } tiff_offset(offset, 8); tiff_checkoffs(offset, info->filefmt->yresoffset); ret = tiff_putint32(info->outfd, 300); if (ret == OK) { ret = tiff_putint32(info->outfd, 1); } if (ret < 0) { goto errout; } tiff_offset(offset, 8); /* Write RGB BitsPerSample Data: * * Bi-level Images: N/A * Greyscale: N/A * RGB: Offset 212 BitsPerSample (8,8,8) * Offset 218 [2 bytes padding] */ if (IMGFLAGS_ISRGB(info->imgflags)) { tiff_checkoffs(offset, TIFF_RGB_BPSOFFSET); tiff_putint16(info->outfd, 8); tiff_putint16(info->outfd, 8); tiff_putint16(info->outfd, 8); tiff_putint16(info->outfd, 0); tiff_offset(offset, 8); } /* Write the Software string: * * * Bi-level Images: Offset 188, Hard-coded "NuttX" * Greyscale: Offset 200, Hard-coded "NuttX" * RGB: Offset 220, Hard-coded "NuttX" */ tiff_checkoffs(offset, info->filefmt->swoffset); ret = tiff_putstring(info->outfd, TIFF_SOFTWARE_STRING, TIFF_SOFTWARE_STRLEN); if (ret < 0) { goto errout; } tiff_offset(offset, TIFF_SOFTWARE_STRLEN); /* Write the DateTime string: * * * Bi-level Images: Offset 188, Format "YYYY:MM:DD HH:MM:SSS" * Greyscale: Offset 200, Hard-coded "NuttX" * RGB: Offset 220, Hard-coded "NuttX" */ tiff_checkoffs(offset, info->filefmt->dateoffset); ret = tiff_datetime(timbuf, TIFF_DATETIME_STRLEN + 8); if (ret < 0) { goto errout; } ret = tiff_putstring(info->outfd, timbuf, TIFF_DATETIME_STRLEN); if (ret < 0) { goto errout; } tiff_offset(offset, TIFF_DATETIME_STRLEN); /* Add two bytes of padding */ ret = tiff_putint16(info->outfd, 0); if (ret < 0) { goto errout; } tiff_offset(offset, 2); /* And that should do it! */ tiff_checkoffs(offset, info->filefmt->sbcoffset); info->outsize = info->filefmt->sbcoffset; return OK; errout: tiff_abort(info); return ret; }