#include #include #include #include #include #include #include "myimg.h" TIFFimg::TIFFimg(const std::string path, uint32_t width, uint32_t height, uint32_t bps, uint32_t bpp, void *data) : myimg(path) { tiff_ = TIFFOpen(path.c_str(), "w"); if (tiff_ == nullptr) throw "TIFFOpen"; this->geometry_.w = width; this->geometry_.h = height; this->bps = bps * CHAR_BIT; this->spp = bpp / bps; TIFFSetField(tiff_, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(tiff_, TIFFTAG_IMAGELENGTH, height); TIFFSetField(tiff_, TIFFTAG_BITSPERSAMPLE, this->bps); TIFFSetField(tiff_, TIFFTAG_SAMPLESPERPIXEL, this->spp); TIFFSetField(tiff_, TIFFTAG_ROWSPERSTRIP, 1); TIFFSetField(tiff_, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tiff_, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); assert(geometry_.w > 0); assert(geometry_.h > 0); printf("saving to: %s: %ux%u, bits/s %u, s/px %u\n", path.c_str(), geometry_.w, geometry_.h, this->bps, this->spp); uint8_t *strip = static_cast(data); for (size_t i = 0; i < height; ++i) { const size_t bytes = width * this->spp; tmsize_t x = TIFFWriteEncodedStrip(tiff_, i, strip, bytes); if (x < 0) errx(1, "TIFFWriteEncodedStrip %zu returned %ld", i, x); strip += bytes; } TIFFFlush(tiff_); } TIFFimg::TIFFimg(const std::string path, const struct buffer_t *buf, int x, int y) : myimg(path) { tiff_ = TIFFOpen(path.c_str(), "w"); if (tiff_ == nullptr) throw "TIFFOpen"; geometry_.w = buf->extent[1]; geometry_.h = buf->extent[2]; printf("writing stride %d %d %d\n", buf->stride[0], buf->stride[1], buf->stride[2]); printf("writing extent %d %d %d\n", buf->extent[0], buf->extent[1], buf->extent[2]); bps = buf->stride[0] * CHAR_BIT; // Halide requires the dim0 w/stride 1 spp = buf->extent[0]; assert(geometry_.w > 0); assert(geometry_.h > 0); TIFFSetField(tiff_, TIFFTAG_IMAGEWIDTH, geometry_.w); TIFFSetField(tiff_, TIFFTAG_IMAGELENGTH, geometry_.h); TIFFSetField(tiff_, TIFFTAG_BITSPERSAMPLE, bps); TIFFSetField(tiff_, TIFFTAG_SAMPLESPERPIXEL, spp); TIFFSetField(tiff_, TIFFTAG_ROWSPERSTRIP, 1); TIFFSetField(tiff_, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(tiff_, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); if (x && y) { float res = 150.0; // dpi TIFFSetField(tiff_, TIFFTAG_XPOSITION, x / res); TIFFSetField(tiff_, TIFFTAG_YPOSITION, y / res); TIFFSetField(tiff_, TIFFTAG_XRESOLUTION, res); TIFFSetField(tiff_, TIFFTAG_YRESOLUTION, res); } printf("saving buffer to: %s: %ux%u, bits/s %u, s/px %u\n", path.c_str(), geometry_.w, geometry_.h, bps, spp); const size_t bytes = geometry_.w * spp * (bps / CHAR_BIT); uint8_t *strip = buf->host; for (int i = 0; i < geometry_.h; ++i) { tmsize_t got = TIFFWriteEncodedStrip(tiff_, i, strip, bytes); if (got < 0) errx(1, "TIFFWriteEncodedStrip %d returned %ld", i, got); strip += bytes; } TIFFFlush(tiff_); } TIFFimg::TIFFimg(const std::string path) : myimg(path) { tiff_ = TIFFOpen(path.c_str(), "r"); if (tiff_ == nullptr) throw "TIFFOpen"; float physx, physy, resx, resy; TIFFGetField(tiff_, TIFFTAG_IMAGEWIDTH, &geometry_.w); TIFFGetField(tiff_, TIFFTAG_IMAGELENGTH, &geometry_.h); TIFFGetField(tiff_, TIFFTAG_XPOSITION, &physx); TIFFGetField(tiff_, TIFFTAG_YPOSITION, &physy); TIFFGetField(tiff_, TIFFTAG_XRESOLUTION, &resx); TIFFGetField(tiff_, TIFFTAG_YRESOLUTION, &resy); TIFFGetField(tiff_, TIFFTAG_BITSPERSAMPLE, &bps); TIFFGetField(tiff_, TIFFTAG_SAMPLESPERPIXEL, &spp); TIFFGetField(tiff_, TIFFTAG_ROWSPERSTRIP, &rpstrip); geometry_.x = physx * resx; geometry_.y = physy * resy; assert(geometry_.w > 0); // unsigned overflow assert(geometry_.h > 0); // unsigned overflow assert(geometry_.y >= 0); // TIFF 6.0 spec, section 12 printf("image: %s: %ux%u at %dx%d , bits/s %u, s/px %u, r/strip %u\n", path.c_str(), geometry_.w, geometry_.h, geometry_.x, geometry_.y, bps, spp, rpstrip); if ((data_ = _TIFFmalloc(size())) == nullptr) throw "_TIFFmalloc"; std::vector threads; size_t lim = std::thread::hardware_concurrency(); size_t unit = strips() / lim, off = 0; if (strips() > 4 * lim) { for (unsigned i = 0; i < lim; ++i) { threads.emplace_back( std::thread(&TIFFimg::load_strip, this, off, off + unit)); off += unit; } for (auto &t : threads) t.join(); } else { /* Too small for threads to have any benefit. */ load_strip(0, strips()); } } void TIFFimg::load_strip(size_t start, size_t end) { TIFF *tiff = TIFFOpen(path_.c_str(), "r"); if (tiff == nullptr) err(1, "TIFFOpen - file got deleted?"); void *strip = _TIFFmalloc(stripsize()); size_t off = start * stripsize(); uint8_t *dst = static_cast(data_); for (size_t i = start; i < end; ++i) { tmsize_t got = TIFFReadEncodedStrip(tiff, i, strip, -1); if (got < 0) errx(1, "TIFFReadEncodedStrip"); memcpy(dst + off, strip, got); off += got; } _TIFFfree(strip); TIFFClose(tiff); } size_t TIFFimg::size(void) const { return width() * height() * pixelbytes(); } size_t TIFFimg::strips(void) const { return TIFFNumberOfStrips(tiff_); } size_t TIFFimg::stripsize(void) const { return TIFFStripSize(tiff_); } struct buffer_t & TIFFimg::prepare_buffer(void) { memset(&buf_, 0, sizeof buf_); buf_.host = static_cast(data_); buf_.elem_size = samplebytes(); // Halide secretly needs the stride of the dimension 0 to be == 1. // So for interleaved RGBA images (the only kind we care right now) // the colour dimension will have to go first. // The X dimension pixel boundary is every RGBA block, // and there's "width" of them in one line. buf_.stride[1] = pixelbytes(); buf_.extent[1] = width(); // The Y dimension pixel boundary is every line, // and there's "height" of them in the picture. buf_.stride[2] = width() * pixelbytes(); buf_.extent[2] = height(); // The colour dimension is every byte or two (8- or 16-bit), // and there's "pixelbytes" of them in one pixel (typically 4: RGBA). buf_.stride[0] = samplebytes(); buf_.extent[0] = pixelbytes(); // We don't use "buf_.min" as it would require allocating // ginormous amounts of memory for no good use. return buf_; } TIFFimg::~TIFFimg(void) { _TIFFfree(data_); TIFFClose(tiff_); }