bool ImageWriterEXR::writeStandardImage(const std::string& filePath, const OutputImage& image, unsigned int channels, bool fullFloat)
{
	unsigned int width = image.getWidth();
	unsigned int height = image.getHeight();

	Imf::Header header(width, height);

	Imf::StringAttribute sourceAttribute;
	sourceAttribute.value() = "Created with Imagine 0.98";
	header.insert("comments", sourceAttribute);

	Imf::PixelType pixelType = (fullFloat) ? Imf::FLOAT : Imf::HALF;

	if (channels & ImageWriter::RGB)
	{
		header.channels().insert("R", Imf::Channel(pixelType));
		header.channels().insert("G", Imf::Channel(pixelType));
		header.channels().insert("B", Imf::Channel(pixelType));
	}

	if (channels & ImageWriter::ALPHA)
		header.channels().insert("A", Imf::Channel(pixelType));

	// if we haven't got any depth data, don't bother
	if (!(image.components() & COMPONENT_DEPTH))
		channels = channels & ~ImageWriter::DEPTH;

	if (channels & ImageWriter::DEPTH)
		header.channels().insert("Z", Imf::Channel(pixelType));

	// if we haven't got any normal data, don't bother
	if (!(image.components() & COMPONENT_NORMAL))
		channels = channels & ~ImageWriter::NORMALS;

	if (channels & ImageWriter::NORMALS)
	{
		header.channels().insert("normal.X", Imf::Channel(pixelType));
		header.channels().insert("normal.Y", Imf::Channel(pixelType));
		header.channels().insert("normal.Z", Imf::Channel(pixelType));
	}

	// if we haven't got any wpp data, don't bother
	if (!(image.components() & COMPONENT_WPP))
		channels = channels & ~ImageWriter::WPP;

	if (channels & ImageWriter::WPP)
	{
		header.channels().insert("wpp.X", Imf::Channel(pixelType));
		header.channels().insert("wpp.Y", Imf::Channel(pixelType));
		header.channels().insert("wpp.Z", Imf::Channel(pixelType));
	}

	// if we haven't got shadows, don't write them
	if (!(image.components() & COMPONENT_SHADOWS))
		channels = channels & ~ImageWriter::SHADOWS;

	if (channels & ImageWriter::SHADOWS)
	{
		// cope with Nuke's limitations by putting ".r" on the end so it shows up in channel list...
		header.channels().insert("shadows.r", Imf::Channel(pixelType));
	}

	unsigned int rgbStride = (channels & ImageWriter::ALPHA) ? 4 : 3;

	T* rgba = NULL;
	if (channels & ImageWriter::RGB)
		rgba = new T[width * height * rgbStride];
	T* pDepth = NULL;
	if (channels & ImageWriter::DEPTH)
		pDepth = new T[width * height];
	T* pNormal = NULL;
	if (channels & ImageWriter::NORMALS)
		pNormal = new T[width * height * 3];
	T* pWPP = NULL;
	if (channels & ImageWriter::WPP)
		pWPP = new T[width * height * 3];
	T* pShadows = NULL;
	if (channels & ImageWriter::SHADOWS)
		pShadows = new T[width * height];

	const Colour4f* pRow = NULL;
	const float* pDepthRow = NULL;
	const Colour3f* pNormalRow = NULL;
	const Colour3f* pWPPRow = NULL;
	const float* pShadowsRow = NULL;

	for (unsigned int y = 0; y < height; y++)
	{
		pRow = image.colourRowPtr(y);

		if (channels & ImageWriter::DEPTH)
			pDepthRow = image.depthRowPtr(y);

		if (channels & ImageWriter::NORMALS)
			pNormalRow = image.normalRowPtr(y);

		if (channels & ImageWriter::WPP)
			pWPPRow = image.wppRowPtr(y);

		if (channels & ImageWriter::SHADOWS)
			pShadowsRow = image.shadowsRowPtr(y);

		unsigned int rgbStartPos = width * y * rgbStride;
		unsigned int normalStartPos = width * y * 3;
		unsigned int wppStartPos = width * y * 3;

		for (unsigned int x = 0; x < width; x++)
		{
			unsigned int pixelPos = rgbStartPos + (x * rgbStride);

			if (channels & ImageWriter::RGB)
			{
				float red = ColourSpace::convertSRGBToLinearAccurate(pRow->r);
				float green = ColourSpace::convertSRGBToLinearAccurate(pRow->g);
				float blue = ColourSpace::convertSRGBToLinearAccurate(pRow->b);

				rgba[pixelPos++] = red;
				rgba[pixelPos++] = green;
				rgba[pixelPos++] = blue;
			}
			else
			{
				pixelPos += 3;
			}

			if (channels & ImageWriter::ALPHA)
				rgba[pixelPos++] = pRow->a;

			if (channels & ImageWriter::DEPTH)
			{
				pDepth[(width * y) + x] = *pDepthRow++;
			}

			if (channels & ImageWriter::NORMALS && pNormalRow)
			{
				const Colour3f& normal = *pNormalRow;

				unsigned int normalPixelPos = normalStartPos + (x * 3);

				pNormal[normalPixelPos++] = normal.r;
				pNormal[normalPixelPos++] = normal.g;
				pNormal[normalPixelPos++] = normal.b;

				pNormalRow++;
			}

			if (channels & ImageWriter::WPP && pWPPRow)
			{
				const Colour3f& wpp = *pWPPRow;

				unsigned int wppPixelPos = wppStartPos + (x * 3);

				pWPP[wppPixelPos++] = wpp.r;
				pWPP[wppPixelPos++] = wpp.g;
				pWPP[wppPixelPos++] = wpp.b;

				pWPPRow++;
			}

			if (channels & ImageWriter::SHADOWS)
			{
				pShadows[(width * y) + x] = *pShadowsRow++;
			}

			pRow++;
		}
	}

	Imf::FrameBuffer fb;
	if (channels & ImageWriter::RGB)
	{
		fb.insert("R", Imf::Slice(pixelType, (char *)rgba, rgbStride*sizeof(T), rgbStride*width*sizeof(T)));
		fb.insert("G", Imf::Slice(pixelType, (char *)rgba + sizeof(T), rgbStride*sizeof(T), rgbStride*width*sizeof(T)));
		fb.insert("B", Imf::Slice(pixelType, (char *)rgba + 2 * sizeof(T), rgbStride*sizeof(T), rgbStride*width*sizeof(T)));
	}

	if (channels & ImageWriter::ALPHA)
		fb.insert("A", Imf::Slice(pixelType, (char *)rgba+3*sizeof(T), rgbStride*sizeof(T), rgbStride*width*sizeof(T)));

	if (channels & ImageWriter::DEPTH)
	{
		fb.insert("Z", Imf::Slice(pixelType, (char *)&(*pDepth), sizeof(T), width * sizeof(T)));
	}

	if (channels & ImageWriter::NORMALS)
	{
		fb.insert("normal.X", Imf::Slice(pixelType, (char *)pNormal, 3 * sizeof(T), 3 * width * sizeof(T)));
		fb.insert("normal.Y", Imf::Slice(pixelType, (char *)pNormal + sizeof(T), 3*sizeof(T), 3*width*sizeof(T)));
		fb.insert("normal.Z", Imf::Slice(pixelType, (char *)pNormal + 2 * sizeof(T), 3*sizeof(T), 3*width*sizeof(T)));
	}

	if (channels & ImageWriter::WPP)
	{
		fb.insert("wpp.X", Imf::Slice(pixelType, (char *)pWPP, 3 * sizeof(T), 3 * width * sizeof(T)));
		fb.insert("wpp.Y", Imf::Slice(pixelType, (char *)pWPP + sizeof(T), 3*sizeof(T), 3*width*sizeof(T)));
		fb.insert("wpp.Z", Imf::Slice(pixelType, (char *)pWPP + 2 * sizeof(T), 3*sizeof(T), 3*width*sizeof(T)));
	}

	if (channels & ImageWriter::SHADOWS)
	{
		fb.insert("shadows.r", Imf::Slice(pixelType, (char *)&(*pShadows), sizeof(T), width * sizeof(T)));
	}

	Imf::OutputFile file(filePath.c_str(), header);
	file.setFrameBuffer(fb);
	file.writePixels(height);

	if (rgba)
		delete [] rgba;
	if (pDepth)
		delete [] pDepth;
	if (pNormal)
		delete [] pNormal;
	if (pWPP)
		delete [] pWPP;
	if (pShadows)
		delete [] pShadows;

	return true;
}
Esempio n. 2
0
/*!
    Tries to fill the image object with the data read from
    the given input stream. Returns true on success.
*/
bool EXRImageFileType::read(      Image        *image, 
                                  std::istream &is, 
                            const std::string  &mimetype)
{
#ifdef OSG_WITH_IMF
    if (!is.good())
        return false;

    const char *dummy = "";
    StdIStream file(is, dummy);

    Imf::Int64 pos = file.tellg();

    bool check = isOpenExrFile(is);

    file.seekg(pos);

    if (!check)
    {
        FFATAL(( "Wrong format, no %s image given!\n",
                  mimetype.c_str() ));
        return false;
    }

    // just read the header and get the channel count
    int channel_count = 0;
    pos = file.tellg();
    try
    {
        Imf::RgbaInputFile stream(file);
        const Imf::Header &header = stream.header();
        const Imf::ChannelList &channels = header.channels();

        for(Imf::ChannelList::ConstIterator it  = channels.begin();
                                            it != channels.end();
                                          ++it)
        {
            ++channel_count;
        }
    }
    catch(std::exception &e)
    {
        FFATAL(( "Error while trying to read OpenEXR Image from stream: %s\n",
                  e.what() ));
        return false;
    }
    file.seekg(pos);

    if(channel_count <= 4)
    {
        // TODO: check for mipmap levels,
        // look if line order is s.th. else than INCREASING_Y
        try
        {
            Int32 width, height, numImg = 1;
            Imf::RgbaInputFile stream(file);
    
            Imath::Box2i dw = stream.dataWindow();
            Imf::Array2D<Imf::Rgba> pixels;
    
            const Imf::Header &header = stream.header();
//            const Imf::LineOrder &order = header.lineOrder();
    
            const Imf::EnvmapAttribute *envmap =
                header.findTypedAttribute<Imf::EnvmapAttribute>("envmap");
    
            width  = dw.max.x - dw.min.x + 1;
            height = dw.max.y - dw.min.y + 1;
    
            pixels.resizeErase(height, width);
    
            if(envmap && envmap->value() == Imf::ENVMAP_CUBE)
            {
                numImg = 6;
                height /= numImg;
    
                if (width != height)
                {
                    FFATAL(( "Cubemaps must have squared size, "
                             "but w=%d and h=%d!\n", width, height ));
                    return false;
                }
            }
    
            stream.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y * width,
                                  1, 
                                  width);
            stream.readPixels(dw.min.y, dw.max.y);
    
            image->set( Image::OSG_RGBA_PF, width, height, 1, 1, 1, 0, 0,
                        Image::OSG_FLOAT16_IMAGEDATA, true, numImg );
            image->clearHalf();
    
            // now add custom attributes
            for(Imf::Header::ConstIterator it  = header.begin();
                                           it != header.end();
                                         ++it)
            {
                Imf::Attribute *copy = it.attribute().copy();
                Imf::StringAttribute *sa = 
                    dynamic_cast<Imf::StringAttribute *>(copy);
    
                if(sa != NULL)
                    image->setAttachmentField(it.name(), sa->value());
    
                delete copy;
            }
    
            Real16 *data = reinterpret_cast<Real16*>(image->editData());
    
            for (Int32 side=numImg-1; side >=0; side--)
            {
                Int32 i, j, size = side * width * height * 4;
    
                for (Int32 y=side*height; y<(side+1)*height; y++)
                {
                    for (Int32 x=0; x<width; x++)
                    {
                        if (numImg == 1 || side == 2 || side == 3)
                        {
                            i = (2 * side + 1) * height - (y + 1); // new y
                            j = x;
                        }
                        else
                        {
                            i = y;
                            j = width - x - 1; // new x
                        }
    
                        *(data + size++) = Real16(pixels[i][j].r);
                        *(data + size++) = Real16(pixels[i][j].g);
                        *(data + size++) = Real16(pixels[i][j].b);
                        *(data + size++) = Real16(pixels[i][j].a);
                    }
                }
            }
    
            return true;
        }
        catch(std::exception &e)
        {
            FFATAL(( "Error while trying to read OpenEXR Image from stream: "
                     "%s\n", e.what() ));

            return false;
        }
    }
    else
    {
        try
        {
            if(channel_count % 4 != 0)
            {
                FFATAL(( "Error while trying to read OpenEXR Image from "
                         "stream, channel count of %d is not supported!\n", 
                         channel_count));

                return false;
            }

            int num_img = channel_count / 4;

            Imf::InputFile stream(file);
            const Imf::Header &header = stream.header();
            Imath::Box2i dw = header.dataWindow();

            int width  = dw.max.x - dw.min.x + 1;
            int height = dw.max.y - dw.min.y + 1;

            image->set(Image::OSG_RGBA_PF, width, height, 1, 1, 1, 0, 0,
                       Image::OSG_FLOAT16_IMAGEDATA, true, num_img);
            image->clearHalf();

            // now add custom attributes
            for(Imf::Header::ConstIterator it  = header.begin();
                                           it != header.end();
                                         ++it                  )
            {
                Imf::Attribute *copy = it.attribute().copy();
                Imf::StringAttribute *sa = 
                    dynamic_cast<Imf::StringAttribute *>(copy);
    
                if(sa != NULL)
                    image->setAttachmentField(it.name(), sa->value());
    
                delete copy;
            }

            const Imf::ChannelList &channels = header.channels();

            // do some channel name checks
            bool channel_error = false;
            for(Imf::ChannelList::ConstIterator it=channels.begin();it!=channels.end();++it)
            {
                for(int side=0;side>num_img;++side)
                {
                    char cn[20];
                    sprintf(cn, "%d", side);

                    char name[20];
                    sprintf(name, "R%s", side == 0 ? "" : cn);
                    if(channels.findChannel(name) == NULL)
                        channel_error = true;
                    sprintf(name, "G%s", side == 0 ? "" : cn);
                    if(channels.findChannel(name) == NULL)
                        channel_error = true;
                    sprintf(name, "B%s", side == 0 ? "" : cn);
                    if(channels.findChannel(name) == NULL)
                        channel_error = true;
                    sprintf(name, "A%s", side == 0 ? "" : cn);
                    if(channels.findChannel(name) == NULL)
                        channel_error = true;
                }
            }

            if(channel_error)
            {
                FFATAL(( "Error while trying to read OpenEXR Image from "
                         "stream, expected channel names 'R' 'G' 'B' 'A', "
                         "'R1' 'G1', 'B1', 'A1', ...\n"));

                return false;
            }

            Imf::FrameBuffer frame_buffer;

            // we need to do a vertical flip so we read single scan lines in.
            int current_scan_line = 0;
            for(int i=height-1;i>=0;--i)
            {
                for(int side=0;side<num_img;++side)
                {
                    char *data = 
                        (reinterpret_cast<char *>(image->editData(0, 0, side))) + i * (sizeof(Real16) * 4 * width);

                    data -= current_scan_line * (sizeof(Real16) * 4 * width);

                    char cn[20];
                    sprintf(cn, "%d", side);

                    char name[20];
                    sprintf(name, "R%s", side == 0 ? "" : cn);
                    frame_buffer.insert(name, Imf::Slice(Imf::HALF, data, sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
                    sprintf(name, "G%s", side == 0 ? "" : cn);
                    frame_buffer.insert(name, Imf::Slice(Imf::HALF, data + 1 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
                    sprintf(name, "B%s", side == 0 ? "" : cn);
                    frame_buffer.insert(name, Imf::Slice(Imf::HALF, data + 2 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
                    sprintf(name, "A%s", side == 0 ? "" : cn);
                    frame_buffer.insert(name, Imf::Slice(Imf::HALF, data + 3 * sizeof(Real16), sizeof(Real16) * 4, sizeof(Real16) * 4 * width));
                }
                stream.setFrameBuffer(frame_buffer);
                stream.readPixels(current_scan_line, current_scan_line);
                ++current_scan_line;
            }

            return true;
        }
        catch(std::exception &e)
        {
            FFATAL(( "Error while trying to read OpenEXR Image from stream: %s\n",
                      e.what() ));
            return false;
        }
    }

#else
    SWARNING << getMimeType()
             << " read is not compiled into the current binary "
             << std::endl;
    return false;
#endif
}
bool ImageWriterEXR::writeDeepImage(const std::string& filePath, const OutputImage& image, unsigned int channels)
{
#if USE_OPENEXR2

	unsigned int width = image.getWidth();
	unsigned int height = image.getHeight();

	Imf::Header header(width, height);
	header.setName("Main");

	header.channels().insert("A", Imf::Channel(Imf::HALF));
	header.channels().insert("R", Imf::Channel(Imf::HALF));
	header.channels().insert("G", Imf::Channel(Imf::HALF));
	header.channels().insert("B", Imf::Channel(Imf::HALF));

	header.channels().insert("Z", Imf::Channel(Imf::HALF));

	Imf::StringAttribute sourceAttribute;
	sourceAttribute.value() = "Created with Imagine 0.98";
	header.insert("comments", sourceAttribute);

#if USE_DEEPTILE
	header.setType(Imf::DEEPTILE);
	header.setTileDescription(Imf::TileDescription());
#else
	header.setType(Imf::DEEPSCANLINE);
#endif

	// we need this...
	header.compression() = Imf::ZIPS_COMPRESSION;
//	header.compression() = Imf::NO_COMPRESSION;

	unsigned int* pSampleCount = new unsigned int[width * height];

	// work out total number of samples for all pixels
	unsigned int totalSamples = 0;
	unsigned int pixelIndex = 0;
	for (unsigned int y = 0; y < height; y++)
	{
		DeepValues* pDeepValues = image.getDeepImage()->colourRowPtr(y);

		for (unsigned int x = 0; x < width; x++)
		{
			totalSamples += pDeepValues->numSamples;

			pSampleCount[pixelIndex++] = pDeepValues->numSamples;

			pDeepValues++;
		}
	}

	half* pAlphaFullSamples = new half[totalSamples];
	half* pRedFullSamples = new half[totalSamples];
	half* pGreenFullSamples = new half[totalSamples];
	half* pBlueFullSamples = new half[totalSamples];
	half* pZFullSamples = new half[totalSamples];

	const DeepValues* pDeepValues = NULL;

	// accumulate the data for writing...
	std::vector<half*> aAlphaPointers(width * height);
	std::vector<half*> aRedPointers(width * height);
	std::vector<half*> aGreenPointers(width * height);
	std::vector<half*> aBluePointers(width * height);
	std::vector<half*> aZPointers(width * height);

	pixelIndex = 0;
	unsigned int fullSampleIndex = 0;
	for (unsigned int y = 0; y < height; y++)
	{
		pDeepValues = image.getDeepImage()->colourRowPtr(y);

		const DeepValues* pDeepPixelValues = pDeepValues;

		for (unsigned int x = 0; x < width; x++)
		{
			unsigned int numSamples = pDeepPixelValues->numSamples;

			half* pAlphaSampleStart = pAlphaFullSamples + fullSampleIndex;
			aAlphaPointers[pixelIndex] = pAlphaSampleStart;
			half* pRedSampleStart = pRedFullSamples + fullSampleIndex;
			aRedPointers[pixelIndex] = pRedSampleStart;
			half* pGreenSampleStart = pGreenFullSamples + fullSampleIndex;
			aGreenPointers[pixelIndex] = pGreenSampleStart;
			half* pBlueSampleStart = pBlueFullSamples + fullSampleIndex;
			aBluePointers[pixelIndex] = pBlueSampleStart;

			half* pZSampleStart = pZFullSamples + fullSampleIndex;
			aZPointers[pixelIndex] = pZSampleStart;

			DeepSample* pDeepSample = pDeepPixelValues->samples;

			// now blit the data over
			for (unsigned int s = 0; s < numSamples; s++)
			{
				*pRedSampleStart++ = pDeepSample->colour.r;
				*pGreenSampleStart++ = pDeepSample->colour.g;
				*pBlueSampleStart++ = pDeepSample->colour.b;

				*pAlphaSampleStart++ = pDeepSample->colour.a;

				*pZSampleStart++ = pDeepSample->depth;

				pDeepSample++;
			}

			pDeepPixelValues++;

			fullSampleIndex += numSamples;
			pixelIndex ++;
		}
	}

	Imf::DeepFrameBuffer frameBuffer;

	// write the sample count
	frameBuffer.insertSampleCountSlice(Imf::Slice(Imf::UINT, (char*)pSampleCount, sizeof(unsigned int), width * sizeof(unsigned int)));

	frameBuffer.insert("A", Imf::DeepSlice(Imf::HALF, (char*)(&aAlphaPointers[0]), sizeof(half*), sizeof(half*) * width, sizeof(half)));
	frameBuffer.insert("R", Imf::DeepSlice(Imf::HALF, (char*)(&aRedPointers[0]), sizeof(half*), sizeof(half*) * width, sizeof(half)));
	frameBuffer.insert("G", Imf::DeepSlice(Imf::HALF, (char*)(&aGreenPointers[0]), sizeof(half*), sizeof(half*) * width, sizeof(half)));
	frameBuffer.insert("B", Imf::DeepSlice(Imf::HALF, (char*)(&aBluePointers[0]), sizeof(half*), sizeof(half*) * width, sizeof(half)));

	frameBuffer.insert("Z", Imf::DeepSlice(Imf::HALF, (char*)(&aZPointers[0]), sizeof(half*), sizeof(half*) * width, sizeof(half)));

	const bool useMultipart = true;

	if (useMultipart)
	{
#if !USE_DEEPTILE
		Imf::MultiPartOutputFile file(filePath.c_str(), &header, 1);
		Imf::DeepScanLineOutputPart part(file, 0);

		try
		{
			part.setFrameBuffer(frameBuffer);
			part.writePixels(height);
		}
		catch (...)
		{
			int one = 5;
		}
#else
		Imf::MultiPartOutputFile file(filePath.c_str(), &header, 1);
		Imf::DeepTiledOutputPart part(file, 0);

		try
		{
			part.setFrameBuffer(frameBuffer);

			for (int y = 0; y < part.numYTiles(); y++)
			{
				for (int x = 0; x < part.numXTiles(); x++)
				{
					part.writeTile(x, y, 0);
				}
			}
		}
		catch (...)
		{
			int one = 5;
		}
#endif
	}
	else
	{
#if !USE_DEEPTILE
		Imf::DeepScanLineOutputFile file(filePath.c_str(), header);

		try
		{
			file.setFrameBuffer(frameBuffer);
			file.writePixels(height);
		}
		catch (...)
		{
			int one = 5;
		}
#else
		Imf::DeepTiledOutputFile file(filePath.c_str(), header);

		try
		{
			file.setFrameBuffer(frameBuffer);

			file.writeTiles(0, file.numXTiles() - 1, 0, file.numYTiles() - 1);
/*
			for (int y = 0; y < file.numYTiles(); y++)
			{
				for (int x = 0; x < file.numXTiles(); x++)
				{
					file.writeTile(x, y, 0);
				}
			}
*/
		}
		catch (...)
		{
			int one = 5;
		}
#endif
	}

	delete [] pSampleCount;

	delete [] pRedFullSamples;
	delete [] pGreenFullSamples;
	delete [] pBlueFullSamples;
	delete [] pAlphaFullSamples;

	delete [] pZFullSamples;
#endif
	return true;
}