Exemplo n.º 1
0
void CqTiffDirHandle::fillHeaderRequiredAttrs(CqTexFileHeader& header) const
{
	// Fill header with general metadata which won't affect the details of the
	// pixel memory layout.
	header.setWidth(tiffTagValue<uint32>(TIFFTAG_IMAGEWIDTH));
	header.setHeight(tiffTagValue<uint32>(TIFFTAG_IMAGELENGTH));
	if(TIFFIsTiled(tiffPtr()))
	{
		header.set<Attr::TileInfo>( SqTileInfo(
					tiffTagValue<uint32>(TIFFTAG_TILEWIDTH),
					tiffTagValue<uint32>(TIFFTAG_TILELENGTH)) );
	}
	// Get the compression type.
	header.set<Attr::Compression>(
			tiffCompressionNameFromTag(tiffTagValue<uint16>(TIFFTAG_COMPRESSION)) );
	// Compute pixel aspect ratio
	TqFloat xRes = 0;
	TqFloat yRes = 0;
	if(TIFFGetField(tiffPtr(), TIFFTAG_XRESOLUTION, &xRes)
			&& TIFFGetField(tiffPtr(), TIFFTAG_YRESOLUTION, &yRes))
	{
		// yRes/xRes should be the correct quantity corresponding to the
		// pixelAspectRatio used in OpenEXR.
		header.set<Attr::PixelAspectRatio>(yRes/xRes);
	}
	else
	{
		header.set<Attr::PixelAspectRatio>(1.0f);
	}
}
Exemplo n.º 2
0
void CqImage::saveToFile(const std::string& fileName) const
{
	boost::mutex::scoped_lock lock(mutex());

	CqTexFileHeader header;

	// Required attributes
	header.setWidth(m_realData->width());
	header.setHeight(m_realData->height());
	header.channelList() = m_realData->channelList();
	// Informational strings
	header.set<Attr::Software>( (boost::format("Aqsis %s (%s %s)")
			 % AQSIS_VERSION_STR % __DATE__ % __TIME__).str());

	header.set<Attr::DisplayWindow>(SqImageRegion(m_frameWidth, m_frameHeight, m_originX, m_originY));
	header.set<Attr::PixelAspectRatio>(1.0);

	// Set some default compression scheme for now - later we can accept user
	// input for this.
	header.set<Attr::Compression>("lzw");

	// \todo: Attributes which might be good to add:
	//   Host computer
	//   Image description
	//   Transformation matrices

	try
	{
		// Now create the image, and output the pixel data.
		boost::shared_ptr<IqTexOutputFile> outFile
			= IqTexOutputFile::open(fileName, ImageFile_Tiff, header);

		// Write all pixels out at once.
		outFile->writePixels(*m_realData);
	}
	catch(XqInternal& e)
	{
		Aqsis::log() << error << "Could not save image \"" << fileName << "\": "
			<< e.what() << "\n";
		return;
	}
}
Exemplo n.º 3
0
/** \brief Convert an OpenEXR header to our own header representation.
 *
 * \param exrHeader - input header
 * \param header - output header
 */
void convertHeader(const Imf::Header& exrHeader, CqTexFileHeader& header)
{
	// Set width, height
	const Imath::Box2i& dataBox = exrHeader.dataWindow();
	header.setWidth(dataBox.max.x - dataBox.min.x+1);
	header.setHeight(dataBox.max.y - dataBox.min.y+1);
	// display window
	const Imath::Box2i& displayBox = exrHeader.displayWindow();
	header.set<Attr::DisplayWindow>( SqImageRegion(
				displayBox.max.x - displayBox.min.x,
				displayBox.max.y - displayBox.min.y,
				displayBox.min.x - dataBox.min.x,
				displayBox.min.y - dataBox.min.y) );

	// Set tiling information ?

	// Aspect ratio
	header.set<Attr::PixelAspectRatio>(exrHeader.pixelAspectRatio());

	TqChannelNameMap channelNameMap;
	// Convert channel representation
	const Imf::ChannelList& exrChannels = exrHeader.channels();
	CqChannelList& channels = header.channelList();
	for(Imf::ChannelList::ConstIterator i = exrChannels.begin();
			i != exrChannels.end(); ++i)
	{
		// use lower case names for channels; OpenEXR uses upper case.
		std::string chanName = i.name();
		std::transform(chanName.begin(), chanName.end(), chanName.begin(),
				::tolower);
		channelNameMap[chanName] = i.name();
		channels.addChannel( SqChannelInfo(chanName,
				channelTypeFromExr(i.channel().type)) );
	}
	header.set<Attr::ExrChannelNameMap>(channelNameMap);
	channels.reorderChannels();

	// Set compresssion type
	header.set<Attr::Compression>(exrCompressionToString(exrHeader.compression()));
}
Exemplo n.º 4
0
//------------------------------------------------------------------------------
// IqTexOutputFile implementation
boost::shared_ptr<IqTexOutputFile> IqTexOutputFile::open(
		const boostfs::path& fileName, EqImageFileType fileType,
		const CqTexFileHeader& header)
{
	// Check some of the header data to make sure it's minimally sane...
	if(header.width() <= 0 || header.height() <= 0)
	{
		AQSIS_THROW_XQERROR(XqInternal, EqE_BadFile, "Cannot open \"" << fileName
				<< "\" - image width and height cannot be negative or zero.");
	}
	if(header.channelList().numChannels() == 0)
	{
		AQSIS_THROW_XQERROR(XqInternal, EqE_BadFile, "Cannot open \"" << fileName
				<< "\" - no data channels present.");
	}

	// Create the new file object
	boost::shared_ptr<IqTexOutputFile> newFile
		= openMultiOutputFile(fileName, fileType, header);
	if(newFile)
		return newFile;

	switch(fileType)
	{
		// case ...:  // Add new output formats here!
		case ImageFile_Exr:
		case ImageFile_Jpg:
		case ImageFile_Png:
			AQSIS_THROW_XQERROR(XqInternal, EqE_Unimplement, "Cannot open \""
					<< fileName << "\" - unimplemented file type \"" << fileType << "\"");
		default:
			AQSIS_THROW_XQERROR(XqInternal, EqE_BadFile, "Cannot open \""
					<< fileName << "\" - unknown file type \"" << fileType << "\"");
	}

	return newFile;
}
Exemplo n.º 5
0
void CqTiffDirHandle::writeRequiredAttrs(const CqTexFileHeader& header)
{
	// Width, height...
	setTiffTagValue<uint32>(TIFFTAG_IMAGEWIDTH, header.width());
	setTiffTagValue<uint32>(TIFFTAG_IMAGELENGTH, header.height());

	// Orientation & planar config should always be fixed.
	setTiffTagValue<uint16>(TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
	setTiffTagValue<uint16>(TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);

	// Pixel aspect ratio
	// We have no meaningful resolution unit - we're only interested in pixel
	// aspect ratio, so set the resolution unit to none.
	setTiffTagValue<uint16>(TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
	setTiffTagValue<float>(TIFFTAG_XRESOLUTION, 1.0f);
	setTiffTagValue<float>(TIFFTAG_YRESOLUTION, header.find<Attr::PixelAspectRatio>(1));

	// Compression-related stuff
	writeCompressionAttrs(header);
	// Channel-related stuff
	writeChannelAttrs(header);

	const SqTileInfo* tileInfo = header.findPtr<Attr::TileInfo>();
	if(tileInfo)
	{
		// Set tile dimensions if present.
		setTiffTagValue<uint32>(TIFFTAG_TILEWIDTH, tileInfo->width);
		setTiffTagValue<uint32>(TIFFTAG_TILELENGTH, tileInfo->height);
	}
	else
	{
		// Else write strip size - AFAICT libtiff uses the values of some other
		// fields (compression) to choose a default, so do this last.
		setTiffTagValue<uint32>(TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tiffPtr(), 0));
	}
}
Exemplo n.º 6
0
void CqTiffDirHandle::writeCompressionAttrs(const CqTexFileHeader& header)
{
	// Set the compression type.
	uint16 compression = tiffCompressionTagFromName(header.find<Attr::Compression>("none"));
	if(!TIFFIsCODECConfigured(compression))
	{
		Aqsis::log() << warning << "No TIFF codec found for compression scheme \""
			<< header.find<Attr::Compression>("none") << "\"\n";
		return;
	}
	setTiffTagValue<uint16>(TIFFTAG_COMPRESSION, compression);

	if(compression == COMPRESSION_LZW || compression == COMPRESSION_DEFLATE)
	{
		// Add a compression predictor if possible; this drastically increases
		// the compression ratios.  Even though the online docs seem to suggest
		// that predictors are independent of the compression codec, this is
		// not the case for libtiff, which appears to give errors if predictors
		// used with anything other than the lzw or deflate codecs.
		//
		// (the innards of libtiff suggest that TIFFPredictorInit() is only
		// called by certian codecs)
		//
		// \todo Test whether PREDICTOR_FLOATINGPOINT is actually beneficial.
		// (Some places on the web suggest not.)
		if(header.channelList().sharedChannelType() == Channel_Float32)
			setTiffTagValue<uint16>(TIFFTAG_PREDICTOR, PREDICTOR_FLOATINGPOINT);
		else
			setTiffTagValue<uint16>(TIFFTAG_PREDICTOR, PREDICTOR_HORIZONTAL);
	}
	if(compression == COMPRESSION_JPEG)
	{
		// Set the jpeg compression quality level if necessary.
		setTiffTagValue<int>(TIFFTAG_JPEGQUALITY,
				header.find<Attr::CompressionQuality>(85));
	}
}
Exemplo n.º 7
0
void CqTiffDirHandle::writeChannelAttrs(const CqTexFileHeader& header)
{
	const CqChannelList& channelList = header.channelList();
	EqChannelType channelType = channelList.sharedChannelType();
	// Assume that the channel type is uniform across the various channels.
	assert(channelType != Channel_TypeUnknown && channelType != Channel_Float16);
	TqInt numChannels = channelList.numChannels();
	assert(numChannels > 0);

	setTiffTagValue<uint16>(TIFFTAG_SAMPLESPERPIXEL, numChannels); 
	setTiffTagValue<uint16>(TIFFTAG_BITSPERSAMPLE, 8*bytesPerPixel(channelType));
	// It's hard to know which algorithm for deciding the photometric type is
	// the best here.  Perhaps it would be better to simply depend on the
	// number of channels, since TIFF doesn't have a standard facility to store
	// channel names...
	if( (channelList.hasIntensityChannel() || numChannels <= 2)
			&& !channelList.hasRgbChannel() )
	{
		// greyscale image
		setTiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
		if(numChannels == 2)
		{
			// Set extra sample types
			std::vector<uint16> extraSamples(numChannels - 1, EXTRASAMPLE_UNSPECIFIED);
			if(channelList[1].name == "a")
				extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
			setTiffTagValue(TIFFTAG_EXTRASAMPLES, extraSamples);
		}
		// \todo PHOTOMETRIC_LOGL alternative for floats
	}
	else
	{
		// Assume a colour image by default (use PHOTOMETRIC_RGB)
		setTiffTagValue<uint16>(TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
		/// \todo PHOTOMETRIC_LOGLUV alternative for floats
		if(numChannels > 3)
		{
			std::vector<uint16> extraSamples(numChannels - 3, EXTRASAMPLE_UNSPECIFIED);
			// Set type of extra samples.
			if(channelList[3].name == "a")
				extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
			if(numChannels >= 6)
			{
				// Initial support for setting extra samples for three channel
				// alpha... This isn't likely to be terribly robust...
				if(channelList[0].name == "r" && channelList[3].name == "ra")
					extraSamples[0] = EXTRASAMPLE_ASSOCALPHA;
				if(channelList[1].name == "g" && channelList[4].name == "ga")
					extraSamples[1] = EXTRASAMPLE_ASSOCALPHA;
				if(channelList[2].name == "b" && channelList[5].name == "ba")
					extraSamples[2] = EXTRASAMPLE_ASSOCALPHA;
			}
			setTiffTagValue(TIFFTAG_EXTRASAMPLES, extraSamples);
		}
	}
	/// \todo: deal with TIFFTAG_SGILOGDATAFMT
	uint16 sampleFormat = 0;
	switch(channelType)
	{
        case Channel_Float32:
			sampleFormat = SAMPLEFORMAT_IEEEFP;
			break;
        case Channel_Signed32:
        case Channel_Signed16:
        case Channel_Signed8:
			sampleFormat = SAMPLEFORMAT_INT;
			break;
        case Channel_Unsigned32:
        case Channel_Unsigned16:
        case Channel_Unsigned8:
			sampleFormat = SAMPLEFORMAT_UINT;
			break;
		default:
			AQSIS_THROW_XQERROR(XqInternal, EqE_Limit,
				"Cannot handle provided pixel sample format");
			break;
    }
	setTiffTagValue<uint16>(TIFFTAG_SAMPLEFORMAT, sampleFormat);
}
Exemplo n.º 8
0
void CqZInputFile::readHeader(std::istream& inStream, CqTexFileHeader& header)
{
	const char zFileMagicNum[] = "Aqsis ZFile";
	const TqInt magicNumSize = sizeof(zFileMagicNum)-1;
	const TqInt versionNumSize = sizeof(AQSIS_VERSION_STR)-1;
	std::vector<char> buf(max(magicNumSize, versionNumSize));

	// Read in magic number
	inStream.read(&buf[0], magicNumSize);
	if(!std::equal(buf.begin(), buf.begin() + magicNumSize, zFileMagicNum)
		|| inStream.gcount() != magicNumSize)
	{
		AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
				"Magic number missmatch in zfile");
	}

	// Read in Aqsis version.  We require this to match the current aqsis version.
	inStream.read(&buf[0], versionNumSize);
	if(!std::equal(buf.begin(), buf.begin() + versionNumSize, AQSIS_VERSION_STR)
		|| inStream.gcount() != versionNumSize)
	{
		AQSIS_THROW_XQERROR(XqBadTexture, EqE_Version,
				"zfile was created with a different aqsis version");
	}

	// Read in map width
	TqUint width = 0;
	inStream.read(reinterpret_cast<char*>(&width), sizeof(width));
	if(inStream.gcount() != sizeof(width))
		AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
			"cannot read width from aqsis z-file");
	// Read in map height
	TqUint height = 0;
	inStream.read(reinterpret_cast<char*>(&height), sizeof(height));
	if(inStream.gcount() != sizeof(height))
		AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
			"cannot read height from aqsis z-file");

	// Read world to camera transformation matrix
	CqMatrix worldToCamera;
	worldToCamera.SetfIdentity(false);
	inStream.read(reinterpret_cast<char*>(worldToCamera.pElements()), 16*sizeof(TqFloat));
	if(inStream.gcount() != 16*sizeof(TqFloat))
		AQSIS_THROW_XQERROR(XqBadTexture,EqE_BadFile,
			"could not read world to camera matrix from aqsis z-file");

	// Read world to screen transformation matrix
	CqMatrix worldToScreen;
	worldToScreen.SetfIdentity(false);
	inStream.read(reinterpret_cast<char*>(worldToScreen.pElements()), 16*sizeof(TqFloat));
	if(inStream.gcount() != 16*sizeof(TqFloat))
		AQSIS_THROW_XQERROR(XqBadTexture, EqE_BadFile,
			"could not read world to screen matrix from aqsis z-file");

	// Save the read header attributes into the file header.
	header.setWidth(width);
	header.setHeight(height);
	header.set<Attr::WorldToScreenMatrix>(worldToScreen);
	header.set<Attr::WorldToCameraMatrix>(worldToCamera);
	// Complete the header with some other attributes implied by the file format
	header.set<Attr::TextureFormat>(TextureFormat_Shadow);
	header.channelList().addChannel(SqChannelInfo("z", Channel_Float32));
}