void CqMixedImageBuffer::compositeOver(const CqMixedImageBuffer& source, const TqChannelNameMap& nameMap, TqInt topLeftX, TqInt topLeftY, const std::string alphaName) { if(!source.channelList().hasChannel(alphaName)) { copyFrom(source, nameMap, topLeftX, topLeftY); } else { // compute size and top left coords of region to copy. TqInt copyWidth = 0; TqInt destTopLeftX = 0; TqInt srcTopLeftX = 0; getCopyRegionSize(topLeftX, source.m_width, m_width, srcTopLeftX, destTopLeftX, copyWidth); TqInt copyHeight = 0; TqInt destTopLeftY = 0; TqInt srcTopLeftY = 0; getCopyRegionSize(topLeftY, source.m_height, m_height, srcTopLeftY, destTopLeftY, copyHeight); // return if no overlap if(copyWidth <= 0 || copyHeight <= 0) return; for(TqChannelNameMap::const_iterator i = nameMap.begin(), e = nameMap.end(); i != e; ++i) { channel(i->first, destTopLeftX, destTopLeftY, copyWidth, copyHeight) ->compositeOver(*source.channel(i->second, srcTopLeftX, srcTopLeftY, copyWidth, copyHeight), *source.channel(alphaName, srcTopLeftX, srcTopLeftY, copyWidth, copyHeight)); } } }
void CqMixedImageBuffer::copyFrom(const CqMixedImageBuffer& source, TqInt topLeftX, TqInt topLeftY) { if(source.m_channelList.numChannels() != m_channelList.numChannels()) AQSIS_THROW_XQERROR(XqInternal, EqE_Limit, "Number of source and destination channels do not match"); // compute size and top left coords of region to copy. TqInt copyWidth = 0; TqInt destTopLeftX = 0; TqInt srcTopLeftX = 0; getCopyRegionSize(topLeftX, source.m_width, m_width, srcTopLeftX, destTopLeftX, copyWidth); TqInt copyHeight = 0; TqInt destTopLeftY = 0; TqInt srcTopLeftY = 0; getCopyRegionSize(topLeftY, source.m_height, m_height, srcTopLeftY, destTopLeftY, copyHeight); // return if no overlap if(copyWidth <= 0 || copyHeight <= 0) return; for(TqInt i = 0; i < m_channelList.numChannels(); ++i) { channel(i, destTopLeftX, destTopLeftY, copyWidth, copyHeight) ->copyFrom(*source.channel(i, srcTopLeftX, srcTopLeftY, copyWidth, copyHeight)); } }
void CqTiffOutputFile::writePixelsImpl(const CqMixedImageBuffer& buffer) { if(!buffer.channelList().channelTypesMatch(m_header.channelList())) { AQSIS_THROW_XQERROR(XqInternal, EqE_Bug, "Buffer and file channels don't match"); } if(m_header.findPtr<Attr::TileInfo>()) writeTiledPixels(buffer); else writeScanlinePixels(buffer); }
void CqTiffOutputFile::writeTiledPixels(const CqMixedImageBuffer& buffer) { SqTileInfo tileInfo = m_header.find<Attr::TileInfo>(); // Check that the buffer has a height that is a multiple of the tile height. if( buffer.height() % tileInfo.height != 0 && m_currentLine + buffer.height() != m_header.height() ) { AQSIS_THROW_XQERROR(XqInternal, EqE_Bug, "pixel buffer with height = " << buffer.height() << " must be a multiple " "of requested tile height (= " << tileInfo.height << ") or run exactly to " "the full image height (= " << m_header.height() << ")."); } CqTiffDirHandle dirHandle(m_fileHandle); const TqUint8* rawBuf = buffer.rawData(); const TqInt bytesPerPixel = buffer.channelList().bytesPerPixel(); boost::scoped_array<TqUint8> tileBuf( new TqUint8[bytesPerPixel*tileInfo.width*tileInfo.height]); const TqInt rowStride = bytesPerPixel*buffer.width(); const TqInt tileRowStride = bytesPerPixel*tileInfo.width; const TqInt endLine = m_currentLine + buffer.height(); const TqInt numTileCols = (buffer.width()-1)/tileInfo.width + 1; for(TqInt line = m_currentLine; line < endLine; line += tileInfo.height) { // srcBuf will point to the beginning of the memory region which will // become the tile. const TqUint8* srcBuf = rawBuf; for(TqInt tileCol = 0; tileCol < numTileCols; ++tileCol) { const TqInt tileDataLen = min(tileRowStride, rowStride - tileCol*tileRowStride); const TqInt tileDataHeight = min(tileInfo.height, buffer.height() - line); // Copy parts of the scanlines into the tile buffer. stridedCopy(tileBuf.get(), tileRowStride, srcBuf, rowStride, tileDataHeight, tileDataLen); TIFFWriteTile(dirHandle.tiffPtr(), reinterpret_cast<tdata_t>(const_cast<TqUint8*>(tileBuf.get())), tileCol*tileInfo.width, line, 0, 0); srcBuf += tileRowStride; } rawBuf += rowStride*tileInfo.height; } m_currentLine = endLine; }
void CqTiffOutputFile::writeScanlinePixels(const CqMixedImageBuffer& buffer) { CqTiffDirHandle dirHandle(m_fileHandle); // Simplest possible implementation using scanline TIFF I/O. Could use // Strip-based IO if performance is ever a problem here. const TqUint8* rawBuf = buffer.rawData(); const TqInt rowStride = buffer.channelList().bytesPerPixel()*buffer.width(); const TqInt endLine = m_currentLine + buffer.height(); // Temporary buffer for scanlines. We need to copy the data into here // since libtiff trashes the buffer when encoding is turned on. (The TIFF // docs don't seem to mention this though, ugh.) boost::scoped_array<TqUint8> lineBuf(new TqUint8[rowStride]); for(TqInt line = m_currentLine; line < endLine; ++line) { // copy the data into temp buffer. std::memcpy(lineBuf.get(), rawBuf, rowStride); // write data TIFFWriteScanline( dirHandle.tiffPtr(), reinterpret_cast<tdata_t>(lineBuf.get()), static_cast<uint32>(line) ); rawBuf += rowStride; } m_currentLine = endLine; }