bool HdrInput::read_native_scanline (int y, int z, void *data) { if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); int miplevel = current_miplevel(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (subimage, miplevel, dummyspec)) return false; // Somehow, the re-open failed assert (m_next_scanline == 0 && current_subimage() == subimage && current_miplevel() == miplevel); } while (m_next_scanline <= y) { // Keep reading until we're read the scanline we really need int r = RGBE_ReadPixels_RLE (m_fd, (float *)data, m_spec.width, 1, rgbe_error); ++m_next_scanline; if (r != RGBE_RETURN_SUCCESS) { error ("%s", rgbe_error); return false; } } return true; }
bool ZfileInput::read_native_scanline(int subimage, int miplevel, int y, int z, void* data) { lock_guard lock(m_mutex); if (!seek_subimage(subimage, miplevel)) return false; if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); if (!close() || !open(m_filename, dummyspec) || !seek_subimage(subimage, miplevel)) return false; // Somehow, the re-open failed ASSERT(m_next_scanline == 0 && current_subimage() == subimage); } while (m_next_scanline <= y) { // Keep reading until we're read the scanline we really need gzread(m_gz, data, m_spec.width * sizeof(float)); ++m_next_scanline; } if (m_swab) swap_endian((float*)data, m_spec.width); return true; }
bool PNGInput::read_native_scanline (int y, int z, void *data) { y -= m_spec.y; if (y < 0 || y >= m_spec.height) // out of range scanline return false; if (m_interlace_type != 0) { // Interlaced. Punt and read the whole image if (m_buf.empty ()) readimg (); size_t size = spec().scanline_bytes(); memcpy (data, &m_buf[0] + y * size, size); } else { // Not an interlaced image -- read just one row if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (subimage, dummyspec)) return false; // Somehow, the re-open failed assert (m_next_scanline == 0 && current_subimage() == subimage); } while (m_next_scanline <= y) { // Keep reading until we're read the scanline we really need // std::cerr << "reading scanline " << m_next_scanline << "\n"; std::string s = PNG_pvt::read_next_scanline (m_png, data); if (s.length ()) { close (); error ("%s", s.c_str ()); return false; } ++m_next_scanline; } } // PNG specifically dictates unassociated (un-"premultiplied") alpha. // Convert to associated unless we were requested not to do so. if (m_spec.alpha_channel != -1 && !m_keep_unassociated_alpha) { float gamma = m_spec.get_float_attribute ("oiio:Gamma", 1.0f); if (m_spec.format == TypeDesc::UINT16) associateAlpha ((unsigned short *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); else associateAlpha ((unsigned char *)data, m_spec.width, m_spec.nchannels, m_spec.alpha_channel, gamma); } return true; }
bool JpgInput::read_native_scanline(int subimage, int miplevel, int y, int z, void* data) { if (!seek_subimage(subimage, miplevel)) return false; if (m_raw) return false; if (y < 0 || y >= (int)m_cinfo.output_height) // out of range scanline return false; if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: close the file and re-open. ImageSpec dummyspec; int subimage = current_subimage(); if (!close() || !open(m_filename, dummyspec) || !seek_subimage(subimage, 0)) return false; // Somehow, the re-open failed assert(m_next_scanline == 0 && current_subimage() == subimage); } // Set up our custom error handler if (setjmp(m_jerr.setjmp_buffer)) { // Jump to here if there's a libjpeg internal error return false; } void* readdata = data; if (m_cmyk) { // If the file's data is CMYK, read into a 4-channel buffer, then // we'll have to convert. m_cmyk_buf.resize(m_spec.width * 4); readdata = &m_cmyk_buf[0]; ASSERT(m_spec.nchannels == 3); } for (; m_next_scanline <= y; ++m_next_scanline) { // Keep reading until we've read the scanline we really need if (jpeg_read_scanlines(&m_cinfo, (JSAMPLE**)&readdata, 1) != 1 || m_fatalerr) { error("JPEG failed scanline read (\"%s\")", filename().c_str()); return false; } } if (m_cmyk) cmyk_to_rgb(m_spec.width, (unsigned char*)readdata, 4, (unsigned char*)data, 3); return true; }
bool NullInput::seek_subimage(int subimage, int miplevel) { if (subimage == current_subimage() && miplevel == current_miplevel()) { return true; } if (subimage != 0) return false; // We only make one subimage m_subimage = subimage; if (miplevel > 0 && !m_mip) return false; // Asked for MIP levels but we aren't makign them m_spec = m_topspec; for (m_miplevel = 0; m_miplevel < miplevel; ++m_miplevel) { if (m_spec.width == 1 && m_spec.height == 1 && m_spec.depth == 1) return false; // Asked for more MIP levels than were available m_spec.width = std::max(1, m_spec.width / 2); m_spec.height = std::max(1, m_spec.height / 2); m_spec.depth = std::max(1, m_spec.depth / 2); m_spec.full_width = m_spec.width; m_spec.full_height = m_spec.height; m_spec.full_depth = m_spec.depth; } return true; }
bool HdrInput::seek_subimage(int subimage, int miplevel) { // HDR doesn't support multiple subimages or mipmaps if (subimage != 0 || miplevel != 0) return false; // Skip the hard work if we're already on the requested subimage if (subimage == current_subimage()) { return true; } close(); // Check that file exists and can be opened m_fd = Filesystem::fopen(m_filename, "rb"); if (m_fd == NULL) { error("Could not open file \"%s\"", m_filename.c_str()); return false; } rgbe_header_info h; int width, height; int r = RGBE_ReadHeader(m_fd, &width, &height, &h, rgbe_error); if (r != RGBE_RETURN_SUCCESS) { error("%s", rgbe_error); close(); return false; } m_spec = ImageSpec(width, height, 3, TypeDesc::FLOAT); if (h.valid & RGBE_VALID_GAMMA) { // Round gamma to the nearest hundredth to prevent stupid // precision choices and make it easier for apps to make // decisions based on known gamma values. For example, you want // 2.2, not 2.19998. float g = float(1.0 / h.gamma); g = roundf(100.0 * g) / 100.0f; m_spec.attribute("oiio:Gamma", g); if (g == 1.0f) m_spec.attribute("oiio:ColorSpace", "linear"); else m_spec.attribute("oiio:ColorSpace", Strutil::sprintf("GammaCorrected%.2g", g)); } else { // Presume linear color space m_spec.attribute("oiio:ColorSpace", "linear"); } if (h.valid & RGBE_VALID_ORIENTATION) m_spec.attribute("Orientation", h.orientation); // FIXME -- should we do anything about exposure, software, // pixaspect, primaries? (N.B. rgbe.c doesn't even handle most of them) m_subimage = subimage; m_next_scanline = 0; return true; }
bool HdrInput::seek_subimage (int subimage, int miplevel, ImageSpec &newspec) { // HDR doesn't support multiple subimages or mipmaps if (subimage != 0 || miplevel != 0) return false; // Skip the hard work if we're already on the requested subimage if (subimage == current_subimage()) { newspec = spec(); return true; } close(); // Check that file exists and can be opened m_fd = Filesystem::fopen (m_filename, "rb"); if (m_fd == NULL) { error ("Could not open file \"%s\"", m_filename.c_str()); return false; } rgbe_header_info h; int width, height; int r = RGBE_ReadHeader (m_fd, &width, &height, &h, rgbe_error); if (r != RGBE_RETURN_SUCCESS) { error ("%s", rgbe_error); close (); return false; } m_spec = ImageSpec (width, height, 3, TypeDesc::FLOAT); if (h.valid & RGBE_VALID_GAMMA) m_spec.attribute ("oiio:Gamma", h.gamma); if (h.valid & RGBE_VALID_ORIENTATION) m_spec.attribute ("Orientation", h.orientation); // FIXME -- should we do anything about exposure, software, // pixaspect, primaries? (N.B. rgbe.c doesn't even handle most of them) m_subimage = subimage; m_next_scanline = 0; newspec = m_spec; return true; }
bool TIFFInput::read_native_scanline (int y, int z, void *data) { y -= m_spec.y; // For compression modes that don't support random access to scanlines // (which I *think* is only LZW), we need to emulate random access by // re-seeking. if (m_no_random_access) { if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: start over. // FIXME: I'm too tired to look into it now, but I wonder if // it is able to randomly seek to the first line in any // "strip", in which case we don't need to start from 0, just // start from the beginning of the strip we need. ImageSpec dummyspec; int old_subimage = current_subimage(); int old_miplevel = current_miplevel(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (old_subimage, old_miplevel, dummyspec)) { return false; // Somehow, the re-open failed } ASSERT (m_next_scanline == 0 && current_subimage() == old_subimage && current_miplevel() == old_miplevel); } while (m_next_scanline < y) { // Keep reading until we're read the scanline we really need m_scratch.resize (m_spec.scanline_bytes()); if (TIFFReadScanline (m_tif, &m_scratch[0], m_next_scanline) < 0) { error ("%s", lasterr.c_str()); return false; } ++m_next_scanline; } } m_next_scanline = y+1; int nvals = m_spec.width * m_spec.nchannels; m_scratch.resize (m_spec.scanline_bytes()); bool no_bit_convert = (m_bitspersample == 8 || m_bitspersample == 16 || m_bitspersample == 32); if (m_photometric == PHOTOMETRIC_PALETTE) { // Convert from palette to RGB if (TIFFReadScanline (m_tif, &m_scratch[0], y) < 0) { error ("%s", lasterr.c_str()); return false; } palette_to_rgb (m_spec.width, &m_scratch[0], (unsigned char *)data); } else { // Not palette int plane_bytes = m_spec.width * m_spec.format.size(); int planes = m_separate ? m_spec.nchannels : 1; std::vector<unsigned char> scratch2 (m_separate ? m_spec.scanline_bytes() : 0); // Where to read? Directly into user data if no channel shuffling // or bit shifting is needed, otherwise into scratch space. unsigned char *readbuf = (no_bit_convert && !m_separate) ? (unsigned char *)data : &m_scratch[0]; // Perform the reads. Note that for contig, planes==1, so it will // only do one TIFFReadScanline. for (int c = 0; c < planes; ++c) /* planes==1 for contig */ if (TIFFReadScanline (m_tif, &readbuf[plane_bytes*c], y, c) < 0) { error ("%s", lasterr.c_str()); return false; } if (m_bitspersample < 8) { // m_scratch now holds nvals n-bit values, contig or separate std::swap (m_scratch, scratch2); for (int c = 0; c < planes; ++c) /* planes==1 for contig */ bit_convert (m_separate ? m_spec.width : nvals, &scratch2[plane_bytes*c], m_bitspersample, m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 8); } else if (m_bitspersample > 8 && m_bitspersample < 16) { // m_scratch now holds nvals n-bit values, contig or separate std::swap (m_scratch, scratch2); for (int c = 0; c < planes; ++c) /* planes==1 for contig */ bit_convert (m_separate ? m_spec.width : nvals, &scratch2[plane_bytes*c], m_bitspersample, m_separate ? &m_scratch[plane_bytes*c] : (unsigned char *)data+plane_bytes*c, 16); } if (m_separate) { // Convert from separate (RRRGGGBBB) to contiguous (RGBRGBRGB). // We know the data is in m_scratch at this point, so // contiguize it into the user data area. separate_to_contig (m_spec.width, &m_scratch[0], (unsigned char *)data); } } if (m_photometric == PHOTOMETRIC_MINISWHITE) invert_photometric (nvals, data); // If alpha is unassociated and we aren't requested to keep it that // way, multiply the colors by alpha per the usual OIIO conventions // to deliver associated color & alpha. if (m_convert_alpha) unassalpha_to_assocalpha (m_spec.width, data); return true; }
bool TIFFInput::read_native_scanline (int y, int z, void *data) { y -= m_spec.y; // For compression modes that don't support random access to scanlines // (which I *think* is only LZW), we need to emulate random access by // re-seeking. if (m_no_random_access) { if (m_next_scanline > y) { // User is trying to read an earlier scanline than the one we're // up to. Easy fix: start over. // FIXME: I'm too tired to look into it now, but I wonder if // it is able to randomly seek to the first line in any // "strip", in which case we don't need to start from 0, just // start from the beginning of the strip we need. ImageSpec dummyspec; int old_subimage = current_subimage(); int old_miplevel = current_miplevel(); if (! close () || ! open (m_filename, dummyspec) || ! seek_subimage (old_subimage, old_miplevel, dummyspec)) { return false; // Somehow, the re-open failed } ASSERT (m_next_scanline == 0 && current_subimage() == old_subimage && current_miplevel() == old_miplevel); } while (m_next_scanline < y) { // Keep reading until we're read the scanline we really need m_scratch.resize (m_spec.scanline_bytes()); if (TIFFReadScanline (m_tif, &m_scratch[0], m_next_scanline) < 0) { error ("%s", lasterr.c_str()); return false; } ++m_next_scanline; } } m_next_scanline = y+1; if (m_photometric == PHOTOMETRIC_PALETTE) { // Convert from palette to RGB m_scratch.resize (m_spec.width); if (TIFFReadScanline (m_tif, &m_scratch[0], y) < 0) { error ("%s", lasterr.c_str()); return false; } palette_to_rgb (m_spec.width, &m_scratch[0], (unsigned char *)data); } else if (m_planarconfig == PLANARCONFIG_SEPARATE && m_spec.nchannels > 1) { // Convert from separate (RRRGGGBBB) to contiguous (RGBRGBRGB) m_scratch.resize (m_spec.scanline_bytes()); int plane_bytes = m_spec.width * m_spec.format.size(); for (int c = 0; c < m_spec.nchannels; ++c) if (TIFFReadScanline (m_tif, &m_scratch[plane_bytes*c], y, c) < 0) { error ("%s", lasterr.c_str()); return false; } separate_to_contig (m_spec.width, &m_scratch[0], (unsigned char *)data); } else if (m_bitspersample == 1 || m_bitspersample == 2 || m_bitspersample == 4) { // <8 bit images m_scratch.resize (m_spec.width); if (TIFFReadScanline (m_tif, &m_scratch[0], y) < 0) { error ("%s", lasterr.c_str()); return false; } nbit_to_8bit (m_spec.width, &m_scratch[0], (unsigned char *)data, m_bitspersample); } else { // Contiguous, >= 8 bit per sample -- the "usual" case if (TIFFReadScanline (m_tif, data, y) < 0) { error ("%s", lasterr.c_str()); return false; } } if (m_photometric == PHOTOMETRIC_MINISWHITE) invert_photometric (m_spec.width * m_spec.nchannels, data); // If alpha is unassociated and we aren't requested to keep it that // way, multiply the colors by alpha per the usual OIIO conventions // to deliver associated color & alpha. if (m_convert_alpha) unassalpha_to_assocalpha (m_spec.width, data); return true; }